From 1d519a69d6873b45f79b89fb5312bf4b093173b5 Mon Sep 17 00:00:00 2001 From: Robin Elfrink Date: Wed, 31 Jul 2024 11:13:38 +0200 Subject: [PATCH] fix: report guest uptime to the host It appears that the host needs to know the guest's uptime information to determine if a soft reboot was successful. Additionally, the uptime needs to be reported in response to the capabilities request. This commit includes the results of `make rekres`. Signed-off-by: Robin Elfrink --- .github/workflows/ci.yaml | 48 +++++++++++++++++++++++----------- .golangci.yml | 44 ++++++++++++------------------- Dockerfile | 34 +++++++++--------------- Makefile | 34 ++++++++++++------------ cmd/talos-vmtoolsd/main.go | 8 +++--- internal/tboxcmds/guestinfo.go | 38 ++++++++++++++++++++++++++- internal/tboxcmds/power.go | 1 + 7 files changed, 123 insertions(+), 84 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4b2dcef..a19c4c0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,6 +1,6 @@ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # -# Generated on 2024-03-28T13:52:01Z by kres latest. +# Generated on 2024-07-30T11:32:20Z by kres faf91e3. name: default concurrency: @@ -29,26 +29,43 @@ jobs: - self-hosted - generic if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) - services: - buildkitd: - image: moby/buildkit:v0.13.1 - options: --privileged - ports: - - 1234:1234 - volumes: - - /var/lib/buildkit/${{ github.repository }}:/var/lib/buildkit - - /usr/etc/buildkit/buildkitd.toml:/etc/buildkit/buildkitd.toml steps: + - name: gather-system-info + id: system-info + uses: kenchan0130/actions-system-info@v1.3.0 + continue-on-error: true + - name: print-system-info + run: | + MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) + + OUTPUTS=( + "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" + "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" + "Hostname: ${{ steps.system-info.outputs.hostname }}" + "NodeName: ${NODE_NAME}" + "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" + "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" + "Name: ${{ steps.system-info.outputs.name }}" + "Platform: ${{ steps.system-info.outputs.platform }}" + "Release: ${{ steps.system-info.outputs.release }}" + "Total memory: ${MEMORY_GB} GB" + ) + + for OUTPUT in "${OUTPUTS[@]}";do + echo "${OUTPUT}" + done + continue-on-error: true - name: checkout uses: actions/checkout@v4 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx + id: setup-buildx uses: docker/setup-buildx-action@v3 with: driver: remote - endpoint: tcp://127.0.0.1:1234 + endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: base run: | @@ -82,16 +99,17 @@ jobs: run: | make image-talos-vmtoolsd - name: push-talos-vmtoolsd-latest - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' env: PUSH: "true" run: | - make image-talos-vmtoolsd TAG=latest + make image-talos-vmtoolsd IMAGE_TAG=latest - name: Generate Checksums if: startsWith(github.ref, 'refs/tags/') run: | - sha256sum _out/talos-vmtoolsd-* > _out/sha256sum.txt - sha512sum _out/talos-vmtoolsd-* > _out/sha512sum.txt + cd _out + sha256sum talos-vmtoolsd-* > sha256sum.txt + sha512sum talos-vmtoolsd-* > sha512sum.txt - name: release-notes if: startsWith(github.ref, 'refs/tags/') run: | diff --git a/.golangci.yml b/.golangci.yml index 6b27041..23ea937 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,6 @@ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # -# Generated on 2024-03-28T09:36:34Z by kres 88d1199. +# Generated on 2024-07-30T11:32:20Z by kres faf91e3. # options for analysis running run: @@ -13,8 +13,8 @@ run: # output configuration options output: formats: - - format: colored-line-number - path: stdout + - format: colored-line-number + path: stdout print-issued-lines: true print-linter-name: true uniq-by-line: true @@ -35,7 +35,7 @@ linters-settings: sections: - standard # Standard section: captures all standard packages. - default # Default section: contains all imports that could not be matched to another section type. - - prefix(github.com/siderolabs/talos-vmtoolsd/) # Custom section: groups all imports with the specified Prefix. + - localmodule # Imports from the same module. gocognit: min-complexity: 30 nestif: @@ -51,10 +51,7 @@ linters-settings: scope: declarations gofmt: simplify: true - goimports: - local-prefixes: github.com/siderolabs/talos-vmtoolsd/ gomodguard: { } - gomnd: { } govet: enable-all: true lll: @@ -97,54 +94,47 @@ linters-settings: cyclop: # the maximal code complexity to report max-complexity: 20 - # depguard: - # Main: - # deny: - # - github.com/OpenPeeDeeP/depguard # this is just an example + depguard: + rules: + prevent_unmaintained_packages: + list-mode: lax # allow unless explicitly denied + files: + - $all + deny: + - pkg: io/ioutil + desc: "replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil" linters: enable-all: true disable-all: false fast: false disable: - - exhaustivestruct - exhaustruct + - err113 - forbidigo - funlen - gochecknoglobals - gochecknoinits - godox - - goerr113 - gomnd - gomoddirectives - gosec - inamedparam - ireturn + - mnd - nestif - nonamedreturns - - nosnakecase - paralleltest - tagalign - tagliatelle - thelper - - typecheck - varnamelen - wrapcheck - - depguard # Disabled because starting with golangci-lint 1.53.0 it doesn't allow denylist alone anymore - testifylint # complains about our assert recorder and has a number of false positives for assert.Greater(t, thing, 1) - protogetter # complains about us using Value field on typed spec, instead of GetValue which has a different signature - perfsprint # complains about us using fmt.Sprintf in non-performance critical code, updating just kres took too long - # abandoned linters for which golangci shows the warning that the repo is archived by the owner - - deadcode - - golint - - ifshort - - interfacer - - maligned - - scopelint - - structcheck - - varcheck - # disabled as it seems to be broken - goes into imported libraries and reports issues there - - musttag + - goimports # same as gci + - musttag # seems to be broken - goes into imported libraries and reports issues there issues: exclude: [ ] diff --git a/Dockerfile b/Dockerfile index e4481c2..24ff63e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -# syntax = docker/dockerfile-upstream:1.7.0-labs +# syntax = docker/dockerfile-upstream:1.9.0-labs # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # -# Generated on 2024-04-01T09:12:09Z by kres latest. +# Generated on 2024-07-30T11:32:20Z by kres faf91e3. ARG TOOLCHAIN @@ -12,28 +12,27 @@ COPY manifest.yaml / COPY talos-vmtoolsd.yaml /rootfs/usr/local/etc/containers/talos-vmtoolsd.yaml # runs markdownlint -FROM docker.io/node:21.7.1-alpine3.19 AS lint-markdown +FROM docker.io/oven/bun:1.1.20-alpine AS lint-markdown WORKDIR /src -RUN npm i -g markdownlint-cli@0.39.0 -RUN npm i sentences-per-line@0.2.1 +RUN bun i markdownlint-cli@0.41.0 sentences-per-line@0.2.1 COPY .markdownlint.json . COPY ./README.md ./README.md -RUN markdownlint --ignore "CHANGELOG.md" --ignore "**/node_modules/**" --ignore '**/hack/chglog/**' --rules node_modules/sentences-per-line/index.js . +RUN bunx markdownlint --ignore "CHANGELOG.md" --ignore "**/node_modules/**" --ignore '**/hack/chglog/**' --rules node_modules/sentences-per-line/index.js . # base toolchain image -FROM ${TOOLCHAIN} AS toolchain +FROM --platform=${BUILDPLATFORM} ${TOOLCHAIN} AS toolchain RUN apk --update --no-cache add bash curl build-base protoc protobuf-dev # build tools FROM --platform=${BUILDPLATFORM} toolchain AS tools -ENV GO111MODULE on +ENV GO111MODULE=on ARG CGO_ENABLED -ENV CGO_ENABLED ${CGO_ENABLED} +ENV CGO_ENABLED=${CGO_ENABLED} ARG GOTOOLCHAIN -ENV GOTOOLCHAIN ${GOTOOLCHAIN} +ENV GOTOOLCHAIN=${GOTOOLCHAIN} ARG GOEXPERIMENT -ENV GOEXPERIMENT ${GOEXPERIMENT} -ENV GOPATH /go +ENV GOEXPERIMENT=${GOEXPERIMENT} +ENV GOPATH=/go ARG DEEPCOPY_VERSION RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install github.com/siderolabs/deep-copy@${DEEPCOPY_VERSION} \ && mv /go/bin/deep-copy /bin/deep-copy @@ -42,9 +41,6 @@ RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/g && mv /go/bin/golangci-lint /bin/golangci-lint RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install golang.org/x/vuln/cmd/govulncheck@latest \ && mv /go/bin/govulncheck /bin/govulncheck -ARG GOIMPORTS_VERSION -RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install golang.org/x/tools/cmd/goimports@${GOIMPORTS_VERSION} \ - && mv /go/bin/goimports /bin/goimports ARG GOFUMPT_VERSION RUN go install mvdan.cc/gofumpt@${GOFUMPT_VERSION} \ && mv /go/bin/gofumpt /bin/gofumpt @@ -73,15 +69,11 @@ RUN mkdir -p internal/version/data && \ FROM base AS lint-gofumpt RUN FILES="$(gofumpt -l .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'gofumpt -w .':\n${FILES}"; exit 1) -# runs goimports -FROM base AS lint-goimports -RUN FILES="$(goimports -l -local github.com/siderolabs/talos-vmtoolsd/ .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'goimports -w -local github.com/siderolabs/talos-vmtoolsd/ .':\n${FILES}"; exit 1) - # runs golangci-lint FROM base AS lint-golangci-lint WORKDIR /src COPY .golangci.yml . -ENV GOGC 50 +ENV GOGC=50 RUN golangci-lint config verify --config .golangci.yml RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/root/.cache/golangci-lint --mount=type=cache,target=/go/pkg golangci-lint run --config .golangci.yml @@ -139,6 +131,6 @@ FROM scratch AS image-talos-vmtoolsd ARG TARGETARCH COPY --from=talos-vmtoolsd talos-vmtoolsd-linux-${TARGETARCH} /rootfs/usr/local/lib/containers/talos-vmtoolsd/talos-vmtoolsd COPY --from=extension / / -LABEL org.opencontainers.image.source https://github.com/siderolabs/talos-vmtoolsd +LABEL org.opencontainers.image.source=https://github.com/siderolabs/talos-vmtoolsd ENTRYPOINT ["/rootfs/usr/local/lib/containers/talos-vmtoolsd/talos-vmtoolsd"] diff --git a/Makefile b/Makefile index 5841d1d..d377460 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # -# Generated on 2024-04-01T09:10:24Z by kres latest. +# Generated on 2024-07-30T11:32:20Z by kres faf91e3. # common variables @@ -9,20 +9,23 @@ TAG := $(shell git describe --tag --always --dirty --match v[0-9]\*) ABBREV_TAG := $(shell git describe --tags >/dev/null 2>/dev/null && git describe --tag --always --match v[0-9]\* --abbrev=0 || echo 'undefined') BRANCH := $(shell git rev-parse --abbrev-ref HEAD) ARTIFACTS := _out +IMAGE_TAG ?= $(TAG) +OPERATING_SYSTEM := $(shell uname -s | tr '[:upper:]' '[:lower:]') +GOARCH := $(shell uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/') WITH_DEBUG ?= false WITH_RACE ?= false REGISTRY ?= ghcr.io USERNAME ?= siderolabs REGISTRY_AND_USERNAME ?= $(REGISTRY)/$(USERNAME) -PROTOBUF_GO_VERSION ?= 1.33.0 -GRPC_GO_VERSION ?= 1.3.0 -GRPC_GATEWAY_VERSION ?= 2.19.1 +PROTOBUF_GO_VERSION ?= 1.34.2 +GRPC_GO_VERSION ?= 1.4.0 +GRPC_GATEWAY_VERSION ?= 2.20.0 VTPROTOBUF_VERSION ?= 0.6.0 +GOIMPORTS_VERSION ?= 0.23.0 DEEPCOPY_VERSION ?= v0.5.6 -GOLANGCILINT_VERSION ?= v1.57.0 +GOLANGCILINT_VERSION ?= v1.59.1 GOFUMPT_VERSION ?= v0.6.0 -GO_VERSION ?= 1.22.1 -GOIMPORTS_VERSION ?= v0.19.0 +GO_VERSION ?= 1.22.5 GO_BUILDFLAGS ?= GO_LDFLAGS ?= CGO_ENABLED ?= 0 @@ -59,9 +62,9 @@ COMMON_ARGS += --build-arg=PROTOBUF_GO_VERSION="$(PROTOBUF_GO_VERSION)" COMMON_ARGS += --build-arg=GRPC_GO_VERSION="$(GRPC_GO_VERSION)" COMMON_ARGS += --build-arg=GRPC_GATEWAY_VERSION="$(GRPC_GATEWAY_VERSION)" COMMON_ARGS += --build-arg=VTPROTOBUF_VERSION="$(VTPROTOBUF_VERSION)" +COMMON_ARGS += --build-arg=GOIMPORTS_VERSION="$(GOIMPORTS_VERSION)" COMMON_ARGS += --build-arg=DEEPCOPY_VERSION="$(DEEPCOPY_VERSION)" COMMON_ARGS += --build-arg=GOLANGCILINT_VERSION="$(GOLANGCILINT_VERSION)" -COMMON_ARGS += --build-arg=GOIMPORTS_VERSION="$(GOIMPORTS_VERSION)" COMMON_ARGS += --build-arg=GOFUMPT_VERSION="$(GOFUMPT_VERSION)" COMMON_ARGS += --build-arg=TESTPKGS="$(TESTPKGS)" TOOLCHAIN ?= docker.io/golang:1.22-alpine @@ -110,7 +113,7 @@ If you already have a compatible builder instance, you may use that instead. ## Artifacts All artifacts will be output to ./$(ARTIFACTS). Images will be tagged with the -registry "$(REGISTRY)", username "$(USERNAME)", and a dynamic tag (e.g. $(IMAGE):$(TAG)). +registry "$(REGISTRY)", username "$(USERNAME)", and a dynamic tag (e.g. $(IMAGE):$(IMAGE_TAG)). The registry and username can be overridden by exporting REGISTRY, and USERNAME respectively. @@ -130,6 +133,9 @@ endif all: unit-tests talos-vmtoolsd image-talos-vmtoolsd extension lint +$(ARTIFACTS): ## Creates artifacts directory. + @mkdir -p $(ARTIFACTS) + .PHONY: clean clean: ## Cleans up all artifacts. @rm -rf $(ARTIFACTS) @@ -160,9 +166,6 @@ fmt: ## Formats the source code lint-govulncheck: ## Runs govulncheck linter. @$(MAKE) target-$@ -lint-goimports: ## Runs goimports linter. - @$(MAKE) target-$@ - .PHONY: base base: ## Prepare base toolchain @$(MAKE) target-$@ @@ -190,11 +193,11 @@ lint-markdown: ## Runs markdownlint. @$(MAKE) target-$@ .PHONY: lint -lint: lint-golangci-lint lint-gofumpt lint-govulncheck lint-goimports lint-markdown ## Run all linters for the project. +lint: lint-golangci-lint lint-gofumpt lint-govulncheck lint-markdown ## Run all linters for the project. .PHONY: image-talos-vmtoolsd image-talos-vmtoolsd: ## Builds image for talos-vmtoolsd. - @$(MAKE) target-$@ TARGET_ARGS="--tag=$(REGISTRY)/$(USERNAME)/talos-vmtoolsd:$(TAG)" + @$(MAKE) target-$@ TARGET_ARGS="--tag=$(REGISTRY)/$(USERNAME)/talos-vmtoolsd:$(IMAGE_TAG)" .PHONY: rekres rekres: @@ -207,8 +210,7 @@ help: ## This help menu. @grep -E '^[a-zA-Z%_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' .PHONY: release-notes -release-notes: - mkdir -p $(ARTIFACTS) +release-notes: $(ARTIFACTS) @ARTIFACTS=$(ARTIFACTS) ./hack/release.sh $@ $(ARTIFACTS)/RELEASE_NOTES.md $(TAG) .PHONY: conformance diff --git a/cmd/talos-vmtoolsd/main.go b/cmd/talos-vmtoolsd/main.go index 24394f5..c0a0021 100644 --- a/cmd/talos-vmtoolsd/main.go +++ b/cmd/talos-vmtoolsd/main.go @@ -143,21 +143,21 @@ func testQuery(api *talosapi.LocalClient, query string) error { case "net-interfaces": for idx, intf := range api.NetInterfaces() { for _, addr := range intf.Addrs { - _, _ = fmt.Fprintf(w, "%d: name=%s mac=%s addr=%s\n", idx, intf.Name, intf.MAC, addr) + _, _ = fmt.Fprintf(w, "%d: name=%s mac=%s addr=%s\n", idx, intf.Name, intf.MAC, addr) //nolint:errcheck } } return nil case "hostname": - _, _ = fmt.Fprintln(w, api.Hostname()) + _, _ = fmt.Fprintln(w, api.Hostname()) //nolint:errcheck return nil case "os-version": - _, _ = fmt.Fprintln(w, api.OSVersion()) + _, _ = fmt.Fprintln(w, api.OSVersion()) //nolint:errcheck return nil case "os-version-short": - _, _ = fmt.Fprintln(w, api.OSVersionShort()) + _, _ = fmt.Fprintln(w, api.OSVersionShort()) //nolint:errcheck return nil default: diff --git a/internal/tboxcmds/guestinfo.go b/internal/tboxcmds/guestinfo.go index 025a7ef..b1ec9b2 100644 --- a/internal/tboxcmds/guestinfo.go +++ b/internal/tboxcmds/guestinfo.go @@ -4,6 +4,8 @@ import ( "bytes" "fmt" "net/netip" + "os" + "strconv" "github.com/sirupsen/logrus" xdr "github.com/stellar/go-xdr/xdr3" @@ -22,7 +24,8 @@ const ( GuestInfoOSNameFull // GuestInfoOSName is the guest info kind for the OS name. GuestInfoOSName - _ // uptime + // GuestInfoUptime is the guest uptime in 100s of seconds. + GuestInfoUptime _ // memory _ // IP v2 // GuestInfoIPAddressV3 is the guest info kind for the IP address. @@ -156,6 +159,34 @@ func (cmd *GuestInfoCommands) SendGuestInfoOSName() { } } +// GuestUptime represents the system uptime. +func (cmd *GuestInfoCommands) GuestUptime() int64 { + u, err := os.ReadFile("/proc/uptime") + if err != nil { + cmd.log.WithError(err).Error("error getting uptime") + + return -1 + } + + field := bytes.Fields(u)[0] + + uptime, err := strconv.ParseFloat(string(field), 64) + if err != nil { + cmd.log.WithError(err).Error("error getting uptime") + + return -1 + } + + return int64(uptime * 100) +} + +// SendGuestInfoUptime sends the guest uptime. +func (cmd *GuestInfoCommands) SendGuestInfoUptime() { + uptime := cmd.GuestUptime() + cmd.log.Debugf("sending uptime: %v", uptime) + cmd.SendGuestInfoString(GuestInfoUptime, fmt.Sprintf("%d", uptime)) +} + // SendGuestInfoNIC sends the guest info NIC. func (cmd *GuestInfoCommands) SendGuestInfoNIC() { cmd.SendGuestInfoXDR(GuestInfoIPAddressV3, cmd.GuestNicInfo()) @@ -176,6 +207,7 @@ func (cmd *GuestInfoCommands) PushGuestInfo() { cmd.SendGuestInfoDNSName() cmd.SendGuestInfoOSNameFull() cmd.SendGuestInfoOSName() + cmd.SendGuestInfoUptime() cmd.SendGuestInfoNIC() } @@ -188,4 +220,8 @@ func RegisterGuestInfoCommands(svc *nanotoolbox.Service, delegate NicDelegate) { } svc.RegisterResetHandler(cmd.PushGuestInfo) svc.RegisterOptionHandler("broadcastIP", cmd.BroadcastIPOptionHandler) + + // As stated in guestInfoServer.c, VMX expects uptime information in response + // to the capabilities request. + svc.AddCapability(fmt.Sprintf("SetGuestInfo %d %d", GuestInfoUptime, cmd.GuestUptime())) } diff --git a/internal/tboxcmds/power.go b/internal/tboxcmds/power.go index a2f3dcf..d36f768 100644 --- a/internal/tboxcmds/power.go +++ b/internal/tboxcmds/power.go @@ -82,6 +82,7 @@ func powerOpHandler(svc *nanotoolbox.Service, state int, handler PowerHandler) ( // RegisterPowerDelegate registers the power operations with the service. func RegisterPowerDelegate(svc *nanotoolbox.Service, delegate PowerDelegate) { svc.AddCapability("tools.capability.statechange") + svc.AddCapability("tools.capability.softpowerop_retry") svc.RegisterCommandHandler(powerOpHandler(svc, PowerStateHalt, delegate.Shutdown)) svc.RegisterCommandHandler(powerOpHandler(svc, PowerStateReboot, delegate.Reboot)) svc.RegisterCommandHandler(powerOpHandler(svc, PowerStatePowerOn, nil))