Repository: gardener/machine-controller-manager
Pull request #985: Fix version logging at binary startup.
Author: login: renormalize
Labels: name: reviewed/ok-to-test
description: Has approval for testing (check PR in detail before setting this label because PR is run on CI/CD)
name: needs/ok-to-test
description: Needs approval for testing (check PR in detail before setting this label because PR is run on CI/CD)
name: status/closed
description: Issue is closed (either delivered or triaged)
name: reviewed/lgtm
description: Has approval for merging
name: size/S
description: Denotes a PR that changes 10-29 lines, ignoring generated files.
Created: 2025-04-16T07:21:54Z | Merged: 2025-04-29T05:48:11Z

**What this PR does / why we need it**:

Version information is not being logged at startup currently.

https://github.com/gardener/machine-controller-manager/blob/57dad3e97b4b0d18e576c8f3ec5ae3d17a062782/cmd/machine-controller-manager/app/controllermanager.go#L78

does not really print anything meaningful. Currently it prints the following:

```
I0416 03:47:56.984925       1 controllermanager.go:78] Version: v0.0.0-master+$Format:%H$
```

which is the template string present in [client-go@v0.31.0/pkg/version/base.go](https://github.com/kubernetes/client-go/blob/02a19c375c491042890be396d94a26a69da89563/pkg/version/base.go#L59). As described in the linked file a few lines above:

```
// If you are looking at these fields in the git tree, they look
// strange. They are modified on the fly by the build process. The
// in-tree values are dummy values used for "git archive", which also
// works for GitHub tar downloads.
//
// When releasing a new Kubernetes version, this file is updated by
// build/mark_new_version.sh to reflect the new version, and then a
// git annotated tag (using format vX.Y where X == Major version and Y
// == Minor version) is created to point to the commit that updates
// pkg/version/base.go
```

where the modification is not being done by the build process in this repository, and is unnecessarily complicated for what it does.

I've introduced a simple `pkg/version/version.go` where exported variables are written during linking through `ldflags` like other projects in @gardener.

**Release note**:
<!--  Write your release note:
1. Enter your release note in the below block.
2. If no release note is required, just write "NONE" within the block.

Format of block header: <category> <target_group>
Possible values:
- category:       breaking|feature|bugfix|doc|other
- target_group:   user|operator|developer|dependency
-->
```bugfix operator
machine-controller-manager version, and build information are printed at startup.
```



Diff:
diff --git a/.ci/build b/.ci/build
index 7736220b0c..9ad10154d7 100755
--- a/.ci/build
+++ b/.ci/build
@@ -28,11 +28,22 @@ else
   export BINARY_PATH="$(${READLINK_BIN} -f "${BINARY_PATH}")/bin"
 fi
 
-cd "${SOURCE_PATH}"
+VCS="github.com"
+ORGANIZATION="gardener"
+PROJECT="machine-controller-manager"
+REPOSITORY=${VCS}/${ORGANIZATION}/${PROJECT}
+GIT_SHA="${GIT_SHA:-$(git rev-parse --short HEAD || echo "GitNotFound")}"
+
+cd "${SOURCE_PATH}" 
 
 ###############################################################################
 
+VERSION_FILE="$(${READLINK_BIN}  -f "${SOURCE_PATH}/VERSION")"
+VERSION="${VERSION:-"$(cat "${VERSION_FILE}")"}"
+GIT_SHA="${GIT_SHA:-$(git rev-parse --short HEAD || echo "GitNotFound")}"
+
 CGO_ENABLED=0 GO111MODULE=on go build \
   -v \
   -o "${BINARY_PATH}/machine-controller-manager" \
+	-ldflags "-w -X ${REPOSITORY}/pkg/version.Version=${VERSION} -X ${REPOSITORY}/pkg/version.GitSHA=${GIT_SHA}" \
   cmd/machine-controller-manager/controller_manager.go
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 174364d72e..89b18a03f6 100644
--- a/Makefile
+++ b/Makefile
@@ -6,8 +6,11 @@
 TOOLS_DIR := hack/tools
 include hack/tools.mk
 
+REPOSITORY         := $(shell go list -m)
 IMAGE_REPOSITORY   := europe-docker.pkg.dev/gardener-project/public/gardener/machine-controller-manager
-IMAGE_TAG          := $(shell cat VERSION)
+VERSION            := $(shell cat VERSION)
+IMAGE_TAG          := $(VERSION)
+GIT_SHA            := $(shell git rev-parse --short HEAD || echo "GitNotFound")
 COVERPROFILE       := test/output/coverprofile.out
 
 LEADER_ELECT 	   ?= "true" # If LEADER_ELECT is not set in the environment, use the default value "true"
@@ -89,6 +92,7 @@ non-gardener-restore:
 .PHONY: start
 start:
 	@GO111MODULE=on go run \
+			-ldflags "-w -X ${REPOSITORY}/pkg/version.Version=${VERSION} -X ${REPOSITORY}/pkg/version.GitSHA=${GIT_SHA}" \
 			cmd/machine-controller-manager/controller_manager.go \
 			--control-kubeconfig=${CONTROL_KUBECONFIG} \
 			--target-kubeconfig=${TARGET_KUBECONFIG} \
diff --git a/cmd/machine-controller-manager/app/controllermanager.go b/cmd/machine-controller-manager/app/controllermanager.go
index 86ed5701b0..8bbb462169 100644
--- a/cmd/machine-controller-manager/app/controllermanager.go
+++ b/cmd/machine-controller-manager/app/controllermanager.go
@@ -43,6 +43,7 @@ import (
 	"github.com/gardener/machine-controller-manager/cmd/machine-controller-manager/app/options"
 	"github.com/gardener/machine-controller-manager/pkg/handlers"
 	"github.com/gardener/machine-controller-manager/pkg/util/configz"
+	"github.com/gardener/machine-controller-manager/pkg/version"
 	prometheus "github.com/prometheus/client_golang/prometheus/promhttp"
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
@@ -51,7 +52,6 @@ import (
 	"k8s.io/client-go/discovery"
 	"k8s.io/client-go/kubernetes"
 	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
-	"k8s.io/client-go/pkg/version"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/clientcmd"
 	"k8s.io/client-go/tools/leaderelection"
@@ -75,7 +75,7 @@ var (
 // Run runs the MCMServer.  This should never exit.
 func Run(s *options.MCMServer) error {
 	// To help debugging, immediately log version
-	klog.V(3).Infof("Version: %+v", version.Get())
+	version.LogVersionInfoWithLevel(3)
 	if err := s.Validate(); err != nil {
 		return err
 	}
diff --git a/pkg/util/provider/app/app.go b/pkg/util/provider/app/app.go
index 2d73559eef..9b87f9a87f 100644
--- a/pkg/util/provider/app/app.go
+++ b/pkg/util/provider/app/app.go
@@ -45,6 +45,7 @@ import (
 	"github.com/gardener/machine-controller-manager/pkg/util/configz"
 	"github.com/gardener/machine-controller-manager/pkg/util/provider/app/options"
 	"github.com/gardener/machine-controller-manager/pkg/util/provider/driver"
+	"github.com/gardener/machine-controller-manager/pkg/version"
 	prometheus "github.com/prometheus/client_golang/prometheus/promhttp"
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
@@ -53,7 +54,6 @@ import (
 	"k8s.io/client-go/discovery"
 	"k8s.io/client-go/kubernetes"
 	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
-	"k8s.io/client-go/pkg/version"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/clientcmd"
 	"k8s.io/client-go/tools/leaderelection"
@@ -73,7 +73,7 @@ var (
 // Run runs the MCServer. This should never exit.
 func Run(s *options.MCServer, driver driver.Driver) error {
 	// To help debugging, immediately log version
-	klog.V(4).Infof("Version: %+v", version.Get())
+	version.LogVersionInfoWithLevel(4)
 	if err := s.Validate(); err != nil {
 		return err
 	}
diff --git a/pkg/version/version.go b/pkg/version/version.go
new file mode 100644
index 0000000000..d35fab67b8
--- /dev/null
+++ b/pkg/version/version.go
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package version
+
+import (
+	"k8s.io/klog/v2"
+	"runtime"
+)
+
+var (
+	// These variables typically come from -ldflags settings in build
+
+	// Version shows the machine-controller-manager binary version.
+	Version string
+	// GitSHA shows the machine-controller-manager binary code commit SHA on git.
+	GitSHA string
+)
+
+// LogVersionInfoWithLevel logs machine-controller-manager version and build information.
+func LogVersionInfoWithLevel(debugLevel int32) {
+	level := klog.Level(debugLevel)
+	klog.V(level).Infof("machine-controller-manager Version: %s\n", Version)
+	klog.V(level).Infof("Git SHA: %s\n", GitSHA)
+	klog.V(level).Infof("Go Version: %s\n", runtime.Version())
+	klog.V(level).Infof("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
+}


Reviews:
user: login: aaronfern
body: Thanks for the PR!

What is the impact when MCM runs locally as a process? I assume that `Version` and `GitSha` will not be printed. Is that correct?
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-16T07:40:56Z
user: login: renormalize
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-16T07:41:54Z
user: login: renormalize
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-16T07:48:45Z
user: login: renormalize
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-16T07:48:50Z
user: login: aaronfern
body: Thanks for the changes!
/lgtm
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-16T08:40:28Z

Review comments:
diff_hunk: @@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package version
+
+import (
+	"k8s.io/klog/v2"
+	"runtime"
+)
+
+var (
+	// These variables typically come from -ldflags settings in build
+
+	// Version shows the etcd-backup-restore binary version.
path: pkg/version/version.go
user: login: aaronfern
body: ```suggestion
	// Version shows the machine-controller-manager binary version.
```
created_at: 2025-04-16T07:39:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package version
+
+import (
+	"k8s.io/klog/v2"
+	"runtime"
+)
+
+var (
+	// These variables typically come from -ldflags settings in build
+
+	// Version shows the etcd-backup-restore binary version.
+	Version string
+	// GitSHA shows the etcd-backup-restore binary code commit SHA on git.
path: pkg/version/version.go
user: login: aaronfern
body: ```suggestion
	// GitSHA shows the machine-controller-manager binary code commit SHA on git.
```
created_at: 2025-04-16T07:39:25Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package version
+
+import (
+	"k8s.io/klog/v2"
+	"runtime"
+)
+
+var (
+	// These variables typically come from -ldflags settings in build
+
+	// Version shows the etcd-backup-restore binary version.
path: pkg/version/version.go
user: login: renormalize
body: Sorry for missing this! will fix.
created_at: 2025-04-16T07:41:53Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package version
+
+import (
+	"k8s.io/klog/v2"
+	"runtime"
+)
+
+var (
+	// These variables typically come from -ldflags settings in build
+
+	// Version shows the etcd-backup-restore binary version.
path: pkg/version/version.go
user: login: renormalize
body: Addressed in [8301ef3](https://github.com/gardener/machine-controller-manager/pull/985/commits/8301ef39bcade8e531c46c998abeedc4f8d13606).
created_at: 2025-04-16T07:48:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package version
+
+import (
+	"k8s.io/klog/v2"
+	"runtime"
+)
+
+var (
+	// These variables typically come from -ldflags settings in build
+
+	// Version shows the etcd-backup-restore binary version.
+	Version string
+	// GitSHA shows the etcd-backup-restore binary code commit SHA on git.
path: pkg/version/version.go
user: login: renormalize
body: Addressed in [8301ef3](https://github.com/gardener/machine-controller-manager/pull/985/commits/8301ef39bcade8e531c46c998abeedc4f8d13606).
created_at: 2025-04-16T07:48:49Z
author_association: MEMBER

PR comments:
user: login: renormalize
created_at: 2025-04-16T07:47:25Z
body: Thanks for the review @aaronfern!

When MCM is run locally as a process, if the verbosity level is sufficient, then the Version and Git SHA are both printed.

```fish
❯ ./bin/machine-controller-manager --v=3
I0416 13:14:12.194054   91628 flags.go:57] FLAG: --address="0.0.0.0"
I0416 13:14:12.194453   91628 flags.go:57] FLAG: --autoscaler-scaldown-annotation-during-rollout="true"
...a bunch of flags...
I0416 13:14:12.194513   91628 flags.go:57] FLAG: --v="3"
I0416 13:14:12.194515   91628 flags.go:57] FLAG: --vmodule=""
I0416 13:14:12.194538   91628 version.go:24] machine-controller-manager Version: v0.58.0-dev
I0416 13:14:12.194543   91628 version.go:25] Git SHA: 48cab6d7e
I0416 13:14:12.194546   91628 version.go:26] Go Version: go1.24.2
I0416 13:14:12.194549   91628 version.go:27] Go OS/Arch: darwin/arm64
W0416 13:14:12.194557   91628 client_config.go:659] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
W0416 13:14:12.194562   91628 client_config.go:664] error creating inClusterConfig, falling back to default config: unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined
invalid configuration: no configuration has been provided, try setting KUBERNETES_MASTER environment variable
```

The logs print Git SHA `48cab6d`, which is the commit on this PR branch [48cab6d](https://github.com/gardener/machine-controller-manager/pull/985/commits/48cab6d7e2937377b81bf8d64b7897a919db63d5).


When verbosity is not sufficient:

```fish
❯ ./bin/machine-controller-manager
W0416 13:17:02.278256   92616 client_config.go:659] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
W0416 13:17:02.278834   92616 client_config.go:664] error creating inClusterConfig, falling back to default config: unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined
invalid configuration: no configuration has been provided, try setting KUBERNETES_MASTER environment variable
```
author_association: MEMBER
user: login: aaronfern
created_at: 2025-04-16T07:51:11Z
body: > When MCM is run locally as a process, if the verbosity level is sufficient, then the Version and Git SHA are both printed.

I meant more from the pov of someone running `make start` or `go run cmd/machine-controller-manager/controller_manager.go <flags>`
author_association: MEMBER
user: login: renormalize
created_at: 2025-04-16T07:56:51Z
body: The Git SHA word is left empty, and the binary proceeds running as is, no regression is observed.
```
❯ make start
I0416 13:23:27.775232   95046 flags.go:57] FLAG: --address="0.0.0.0"
I0416 13:23:27.775294   95046 flags.go:57] FLAG: --autoscaler-scaldown-annotation-during-rollout="true"
...a bunch of flags...
I0416 13:23:27.775334   95046 flags.go:57] FLAG: --v="3"
I0416 13:23:27.775335   95046 flags.go:57] FLAG: --vmodule=""
I0416 13:23:27.775347   95046 version.go:24] machine-controller-manager Version:
I0416 13:23:27.775349   95046 version.go:25] Git SHA:
I0416 13:23:27.775351   95046 version.go:26] Go Version: go1.24.2
I0416 13:23:27.775353   95046 version.go:27] Go OS/Arch: darwin/arm64
W0416 13:23:27.775357   95046 client_config.go:659] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
W0416 13:23:27.775362   95046 client_config.go:664] error creating inClusterConfig, falling back to default config: unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined
invalid configuration: no configuration has been provided, try setting KUBERNETES_MASTER environment variable
exit status 1
```

Perhaps I could enhance the `start` make target to include the Git SHA and Version as well. However, the user running `go run cmd/machine-controller-manager/controller_manager.go <flags>` will have to pass the necessary linking flags themselves to see the Version and Git SHA.

author_association: MEMBER
user: login: renormalize
created_at: 2025-04-16T08:18:56Z
body: The `start` target has been enhanced as well, in [2c39f8a](https://github.com/gardener/machine-controller-manager/pull/985/commits/2c39f8abfe5a72f08f7fee428b64f01676b4966e).

```
❯ git log -1
commit 2c39f8abfe5a72f08f7fee428b64f01676b4966e (HEAD -> version, origin/version)
Author: Saketh Kalaga <51327242+renormalize@users.noreply.github.com>
Date:   Wed Apr 16 13:44:19 2025 +0530

    Add `ldflags` to the `start` make target.
[I] ~/go/src/github.com/gardener/machine-controller-manager (version)
❯ make start
I0416 13:47:47.820233    8115 flags.go:57] FLAG: --address="0.0.0.0"
I0416 13:47:47.820521    8115 flags.go:57] FLAG: --autoscaler-scaldown-annotation-during-rollout="true"
...a bunch of flags...
I0416 13:47:47.820562    8115 flags.go:57] FLAG: --v="3"
I0416 13:47:47.820564    8115 flags.go:57] FLAG: --vmodule=""
I0416 13:47:47.820608    8115 version.go:24] machine-controller-manager Version: v0.58.0-dev
I0416 13:47:47.820611    8115 version.go:25] Git SHA: 2c39f8abf
I0416 13:47:47.820613    8115 version.go:26] Go Version: go1.24.2
I0416 13:47:47.820615    8115 version.go:27] Go OS/Arch: darwin/arm64
W0416 13:47:47.820638    8115 client_config.go:659] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
W0416 13:47:47.820641    8115 client_config.go:664] error creating inClusterConfig, falling back to default config: unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined
invalid configuration: no configuration has been provided, try setting KUBERNETES_MASTER environment variable
exit status 1
make: *** [start] Error 1
```
author_association: MEMBER