Repository: kubernetes-sigs/security-profiles-operator
Pull request #860 (2022-03-23 13:43:31)

Make version.Get fallible


#### What type of PR is this?


/kind cleanup


#### What this PR does / why we need it:
It may be possible that the whole version retrieval fails, which is something where we should not throw a runtime panic.

#### Which issue(s) this PR fixes:

<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.

Fixes #

or

None
-->
None
#### Does this PR have test?
None
<!--
If tests aren't applicable just write N/A.
-->

#### Special notes for your reviewer:
None
#### Does this PR introduce a user-facing change?

<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->

```release-note
None
```


k8s-ci-robot: @saschagrunert: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-security-profiles-operator-verify | c08fcfedfd0adb720232f53185b2c2691d2e0a43 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/860/pull-security-profiles-operator-verify/1506627709277573120) | true | `/test pull-security-profiles-operator-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=860). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Asaschagrunert). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/security-profiles-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
codecov-commenter: # [Codecov](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/860?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#860](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/pull/860?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (c08fcfe) into [main](https://codecov.io/gh/kubernetes-sigs/security-profiles-operator/commit/e517761bd910f6030b1f92302975b749ac7522fe?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (e517761) will **decrease** coverage by `0.04%`.
> The diff coverage is `50.00%`.

> :exclamation: Current head c08fcfe differs from pull request most recent head 0e4d908. Consider uploading reports for the commit 0e4d908 to get more accurate results


```diff
@@            Coverage Diff             @@
##             main     #860      +/-   ##
==========================================
- Coverage   52.47%   52.42%   -0.05%     
==========================================
  Files          42       42              
  Lines        4324     4326       +2     
==========================================
- Hits         2269     2268       -1     
- Misses       1981     1983       +2     
- Partials       74       75       +1     
```

k8s-ci-robot: @saschagrunert: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-security-profiles-operator-verify | 0e4d908f2d9cbe4155d2be8c69b39514643eabd5 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_security-profiles-operator/860/pull-security-profiles-operator-verify/1506628298732474368) | true | `/test pull-security-profiles-operator-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=security-profiles-operator&pr=860). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Asaschagrunert). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/security-profiles-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/cmd/security-profiles-operator/main.go b/cmd/security-profiles-operator/main.go
index 09a994336..511c97644 100644
--- a/cmd/security-profiles-operator/main.go
+++ b/cmd/security-profiles-operator/main.go
@@ -20,6 +20,7 @@ import (
 	"context"
 	"flag"
 	"fmt"
+	"log"
 	"net/http"
 	_ "net/http/pprof" // nolint:gosec // required for profiling
 	"os"
@@ -79,7 +80,13 @@ func main() {
 	app.Usage = "Kubernetes Security Profiles Operator"
 	app.Description = "The Security Profiles Operator makes it easier for cluster admins " +
 		"to manage their seccomp or AppArmor profiles and apply them to Kubernetes' workloads."
-	app.Version = version.Get().Version
+
+	info, err := version.Get()
+	if err != nil {
+		log.Fatal(err)
+	}
+	app.Version = info.Version
+
 	app.Commands = cli.Commands{
 		&cli.Command{
 			Name:    "version",
@@ -93,10 +100,9 @@ func main() {
 				},
 			},
 			Action: func(c *cli.Context) error {
-				v := version.Get()
-				res := v.String()
+				res := info.String()
 				if c.Bool(jsonFlag) {
-					j, err := v.JSONString()
+					j, err := info.JSONString()
 					if err != nil {
 						return errors.Wrap(err, "unable to generate JSON from version info")
 					}
@@ -111,14 +117,18 @@ func main() {
 			Name:    "manager",
 			Aliases: []string{"m"},
 			Usage:   "run the manager",
-			Action:  runManager,
+			Action: func(ctx *cli.Context) error {
+				return runManager(ctx, info)
+			},
 		},
 		&cli.Command{
 			Before:  initialize,
 			Name:    "daemon",
 			Aliases: []string{"d"},
 			Usage:   "run the daemon",
-			Action:  runDaemon,
+			Action: func(ctx *cli.Context) error {
+				return runDaemon(ctx, info)
+			},
 			Flags: []cli.Flag{
 				&cli.BoolFlag{
 					Name:  selinuxFlag,
@@ -137,7 +147,9 @@ func main() {
 			Name:    "webhook",
 			Aliases: []string{"w"},
 			Usage:   "run the webhook",
-			Action:  runWebhook,
+			Action: func(ctx *cli.Context) error {
+				return runWebhook(ctx, info)
+			},
 			Flags: []cli.Flag{
 				&cli.IntFlag{
 					Name:    "port",
@@ -151,21 +163,27 @@ func main() {
 			Before: initialize,
 			Name:   "non-root-enabler",
 			Usage:  "run the non root enabler",
-			Action: runNonRootEnabler,
+			Action: func(ctx *cli.Context) error {
+				return runNonRootEnabler(ctx, info)
+			},
 		},
 		&cli.Command{
 			Before:  initialize,
 			Name:    "log-enricher",
 			Aliases: []string{"l"},
 			Usage:   "run the audit's log enricher",
-			Action:  runLogEnricher,
+			Action: func(ctx *cli.Context) error {
+				return runLogEnricher(ctx, info)
+			},
 		},
 		&cli.Command{
 			Before:  initialize,
 			Name:    "bpf-recorder",
 			Aliases: []string{"b"},
 			Usage:   "run the bpf recorder",
-			Action:  runBPFRecorder,
+			Action: func(ctx *cli.Context) error {
+				return runBPFRecorder(ctx, info)
+			},
 		},
 	}
 	app.Flags = []cli.Flag{
@@ -237,15 +255,15 @@ func initProfiling(ctx *cli.Context) {
 	}
 }
 
-func printInfo(component string) {
+func printInfo(component string, info *version.Info) {
 	setupLog.Info(
 		fmt.Sprintf("starting component: %s", component),
-		version.Get().AsKeyValues()...,
+		info.AsKeyValues()...,
 	)
 }
 
-func runManager(ctx *cli.Context) error {
-	printInfo("security-profiles-operator")
+func runManager(ctx *cli.Context, info *version.Info) error {
+	printInfo("security-profiles-operator", info)
 	cfg, err := ctrl.GetConfig()
 	if err != nil {
 		return errors.Wrap(err, "get config")
@@ -349,9 +367,9 @@ func getEnabledControllers(ctx *cli.Context) []controller.Controller {
 	return controllers
 }
 
-func runDaemon(ctx *cli.Context) error {
+func runDaemon(ctx *cli.Context, info *version.Info) error {
 	// security-profiles-operator-daemon
-	printInfo("spod")
+	printInfo("spod", info)
 
 	enabledControllers := getEnabledControllers(ctx)
 	if len(enabledControllers) == 0 {
@@ -411,26 +429,26 @@ func runDaemon(ctx *cli.Context) error {
 	return nil
 }
 
-func runBPFRecorder(ctx *cli.Context) error {
+func runBPFRecorder(_ *cli.Context, info *version.Info) error {
 	const component = "bpf-recorder"
-	printInfo(component)
+	printInfo(component, info)
 	return bpfrecorder.New(ctrl.Log.WithName(component)).Run()
 }
 
-func runLogEnricher(ctx *cli.Context) error {
+func runLogEnricher(_ *cli.Context, info *version.Info) error {
 	const component = "log-enricher"
-	printInfo(component)
+	printInfo(component, info)
 	return enricher.New(ctrl.Log.WithName(component)).Run()
 }
 
-func runNonRootEnabler(ctx *cli.Context) error {
+func runNonRootEnabler(_ *cli.Context, info *version.Info) error {
 	const component = "non-root-enabler"
-	printInfo(component)
+	printInfo(component, info)
 	return nonrootenabler.New().Run(ctrl.Log.WithName(component))
 }
 
-func runWebhook(ctx *cli.Context) error {
-	printInfo("security-profiles-operator-webhook")
+func runWebhook(ctx *cli.Context, info *version.Info) error {
+	printInfo("security-profiles-operator-webhook", info)
 	cfg, err := ctrl.GetConfig()
 	if err != nil {
 		return errors.Wrap(err, "get config")
diff --git a/dependencies.yaml b/dependencies.yaml
index 32f4b4120..8fb867260 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -30,7 +30,7 @@ dependencies:
       match: KUSTOMIZE_VERSION
 
   - name: cert-manager
-    version: 1.7.1
+    version: 1.7.2
     refPaths:
     - path: test/e2e_test.go
       match: jetstack/cert-manager
diff --git a/go.mod b/go.mod
index 71e683d5f..0b9351580 100644
--- a/go.mod
+++ b/go.mod
@@ -6,10 +6,10 @@ require (
 	github.com/ReneKroon/ttlcache/v2 v2.11.0
 	github.com/acobaugh/osrelease v0.0.0-20181218015638-a93a0a55a249
 	github.com/aquasecurity/libbpfgo v0.2.5-libbpf-0.7.0
-	github.com/containers/common v0.47.4
+	github.com/containers/common v0.47.5
 	github.com/crossplane/crossplane-runtime v0.14.1-0.20210713194031-85b19c28ea88
 	github.com/go-logr/logr v1.2.3
-	github.com/jetstack/cert-manager v1.7.1
+	github.com/jetstack/cert-manager v1.7.2
 	github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0
 	github.com/nxadm/tail v1.4.8
 	github.com/openshift/api v0.0.0-20220209124712-b632c5fc10c0
diff --git a/go.sum b/go.sum
index c51dad820..42f91be38 100644
--- a/go.sum
+++ b/go.sum
@@ -308,8 +308,8 @@ github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtr
 github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
 github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
 github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE=
-github.com/containers/common v0.47.4 h1:kS202Z/bTQIM/pwyuJ+lF8143Uli6AB9Q9OVR0xa9CM=
-github.com/containers/common v0.47.4/go.mod h1:HgX0mFXyB0Tbe2REEIp9x9CxET6iSzmHfwR6S/t2LZc=
+github.com/containers/common v0.47.5 h1:Qm9o+wVPO9sbggTKubN3xYMtPRaPv7dmcrJQgongHHw=
+github.com/containers/common v0.47.5/go.mod h1:HgX0mFXyB0Tbe2REEIp9x9CxET6iSzmHfwR6S/t2LZc=
 github.com/containers/image/v5 v5.19.1/go.mod h1:ewoo3u+TpJvGmsz64XgzbyTHwHtM94q7mgK/pX+v2SE=
 github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
 github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
@@ -731,8 +731,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
-github.com/jetstack/cert-manager v1.7.1 h1:qIIP0RN5FzBChJLJ3uGCGJmdAAonwDMdcsJExATa64I=
-github.com/jetstack/cert-manager v1.7.1/go.mod h1:xj0TPp31HE0Jub5mNOnF3Fp3XvhIsiP+tsPZVOmU/Qs=
+github.com/jetstack/cert-manager v1.7.2 h1:kDme6/AtdCgLupnB1qrAOxFCoM7ahtt+Y9DNQXZXIkA=
+github.com/jetstack/cert-manager v1.7.2/go.mod h1:xj0TPp31HE0Jub5mNOnF3Fp3XvhIsiP+tsPZVOmU/Qs=
 github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
diff --git a/installation-usage.md b/installation-usage.md
index b3ba25112..ee29173f0 100644
--- a/installation-usage.md
+++ b/installation-usage.md
@@ -51,7 +51,7 @@ cert-manager via `kubectl`, if you're **not** running on
 [OpenShift](https://www.redhat.com/en/technologies/cloud-computing/openshift):
 
 ```sh
-$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.7.1/cert-manager.yaml
+$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.7.2/cert-manager.yaml
 $ kubectl --namespace cert-manager wait --for condition=ready pod -l app.kubernetes.io/instance=cert-manager
 ```
 
diff --git a/internal/pkg/version/version.go b/internal/pkg/version/version.go
index 55c95d58a..6186a9b33 100644
--- a/internal/pkg/version/version.go
+++ b/internal/pkg/version/version.go
@@ -19,6 +19,7 @@ package version
 import (
 	"encoding/json"
 	"fmt"
+	"reflect"
 	"runtime"
 	"runtime/debug"
 	"strings"
@@ -49,8 +50,11 @@ type Info struct {
 	Dependencies  []string `json:"dependencies,omitempty"`
 }
 
-func Get() *Info {
-	info, _ := debug.ReadBuildInfo()
+func Get() (*Info, error) {
+	info, ok := debug.ReadBuildInfo()
+	if !ok {
+		return nil, errors.New("unable to retrieve build info")
+	}
 
 	const unknown = "unknown"
 	gitCommit := unknown
@@ -107,7 +111,7 @@ func Get() *Info {
 		LDFlags:       ldFlags,
 		CGOLDFlags:    cgoLdflags,
 		Dependencies:  dependencies,
-	}
+	}, nil
 }
 
 // String returns the string representation of the version info.
@@ -116,20 +120,36 @@ func (i *Info) String() string {
 	const padding = 2
 	w := tabwriter.NewWriter(&b, 0, 0, padding, ' ', 0)
 
-	fmt.Fprintf(w, "Version:\t%s\n", i.Version)
-	fmt.Fprintf(w, "GitCommit:\t%s\n", i.GitCommit)
-	fmt.Fprintf(w, "GitCommitDate:\t%s\n", i.GitCommitDate)
-	fmt.Fprintf(w, "GitTreeState:\t%s\n", i.GitTreeState)
-	fmt.Fprintf(w, "BuildDate:\t%s\n", i.BuildDate)
-	fmt.Fprintf(w, "GoVersion:\t%s\n", i.GoVersion)
-	fmt.Fprintf(w, "Compiler:\t%s\n", i.Compiler)
-	fmt.Fprintf(w, "Platform:\t%s\n", i.Platform)
-	fmt.Fprintf(w, "Libseccomp:\t%s\n", i.Libseccomp)
-	fmt.Fprintf(w, "Libbpf:\t%s\n", i.Libbpf)
-	fmt.Fprintf(w, "BuildTags:\t%s\n", i.BuildTags)
-	fmt.Fprintf(w, "LDFlags:\t%s\n", i.LDFlags)
-	fmt.Fprintf(w, "CGOLDFlags:\t%s\n", i.CGOLDFlags)
-	fmt.Fprintf(w, "Dependencies:\n  %s\n", strings.Join(i.Dependencies, "\n  "))
+	v := reflect.ValueOf(*i)
+	t := v.Type()
+	for i := 0; i < t.NumField(); i++ {
+		field := t.Field(i)
+		value := v.FieldByName(field.Name)
+
+		valueString := ""
+		// nolint:exhaustive // we ignore missing switch cases on purpose
+		switch field.Type.Kind() {
+		case reflect.Bool:
+			valueString = fmt.Sprint(value.Bool())
+
+		case reflect.Slice:
+			// Only expecting []string here; ignore other slices.
+			if s, ok := value.Interface().([]string); ok {
+				const sep = "\n  "
+				valueString = sep + strings.Join(s, sep)
+			}
+
+		case reflect.String:
+			valueString = value.String()
+		}
+
+		if valueString != "" {
+			fmt.Fprintf(w, "%s:\t%s", field.Name, valueString)
+			if i+1 < t.NumField() {
+				fmt.Fprintf(w, "\n")
+			}
+		}
+	}
 
 	w.Flush()
 	return b.String()
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 9a8520e5d..889744ae1 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -91,7 +91,7 @@ github.com/blang/semver
 # github.com/cespare/xxhash/v2 v2.1.2
 ## explicit; go 1.11
 github.com/cespare/xxhash/v2
-# github.com/containers/common v0.47.4
+# github.com/containers/common v0.47.5
 ## explicit; go 1.15
 github.com/containers/common/pkg/seccomp
 # github.com/cpuguy83/go-md2man/v2 v2.0.1
@@ -249,7 +249,7 @@ github.com/inconshreveable/mousetrap
 # github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
 ## explicit
 github.com/jbenet/go-context/io
-# github.com/jetstack/cert-manager v1.7.1
+# github.com/jetstack/cert-manager v1.7.2
 ## explicit; go 1.17
 github.com/jetstack/cert-manager/pkg/apis/acme
 github.com/jetstack/cert-manager/pkg/apis/acme/v1


Test patch:
diff --git a/internal/pkg/version/version_test.go b/internal/pkg/version/version_test.go
index cfc0e9f01..eee1f95d7 100644
--- a/internal/pkg/version/version_test.go
+++ b/internal/pkg/version/version_test.go
@@ -25,7 +25,8 @@ import (
 func TestVersionText(t *testing.T) {
 	t.Parallel()
 
-	sut := Get()
+	sut, err := Get()
+	require.Nil(t, err)
 	require.NotEmpty(t, sut.BuildDate)
 	require.NotEmpty(t, sut.Compiler)
 	require.NotEmpty(t, sut.GitCommit)
@@ -39,13 +40,17 @@ func TestVersionText(t *testing.T) {
 func TestVersionJSON(t *testing.T) {
 	t.Parallel()
 
-	sut, err := Get().JSONString()
+	sut, err := Get()
+	require.Nil(t, err)
+	json, err := sut.JSONString()
 	require.Nil(t, err)
-	require.NotEmpty(t, sut)
+	require.NotEmpty(t, json)
 }
 
 func TestAsKeyValues(t *testing.T) {
 	t.Parallel()
-	sut := Get().AsKeyValues()
-	require.Len(t, sut, 2*14)
+	sut, err := Get()
+	require.Nil(t, err)
+	kv := sut.AsKeyValues()
+	require.Len(t, kv, 2*14)
 }
diff --git a/test/e2e_test.go b/test/e2e_test.go
index c88b85ffc..4fd02fe47 100644
--- a/test/e2e_test.go
+++ b/test/e2e_test.go
@@ -32,7 +32,7 @@ import (
 )
 
 const (
-	certmanager       = "https://github.com/jetstack/cert-manager/releases/download/v1.7.1/cert-manager.yaml"
+	certmanager       = "https://github.com/jetstack/cert-manager/releases/download/v1.7.2/cert-manager.yaml"
 	manifest          = "deploy/operator.yaml"
 	namespaceManifest = "deploy/namespace-operator.yaml"
 	testNamespace     = "test-ns"