diff --git a/syft/pkg/cataloger/binary/cataloger_test.go b/syft/pkg/cataloger/binary/cataloger_test.go index 84771a16f..51b0751ef 100644 --- a/syft/pkg/cataloger/binary/cataloger_test.go +++ b/syft/pkg/cataloger/binary/cataloger_test.go @@ -239,30 +239,6 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Metadata: metadata("redis-binary"), }, }, - { - name: "positive-argocd-2.5.11", - fixtureDir: "test-fixtures/classifiers/dynamic/argocd-2.5.11", - expected: pkg.Package{ - Name: "argocd", - Version: "2.5.11", - Type: "binary", - PURL: "pkg:golang/github.com/argoproj/argo-cd@2.5.11", - Locations: locations("argocd"), - Metadata: metadata("argocd"), - }, - }, - { - name: "positive-argocd-2.6.4", - fixtureDir: "test-fixtures/classifiers/dynamic/argocd-2.6.4", - expected: pkg.Package{ - Name: "argocd", - Version: "2.6.4", - Type: "binary", - PURL: "pkg:golang/github.com/argoproj/argo-cd@2.6.4", - Locations: locations("argocd"), - Metadata: metadata("argocd"), - }, - }, { name: "positive-helm-3.11.1", fixtureDir: "test-fixtures/classifiers/dynamic/helm-3.11.1", @@ -287,66 +263,6 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Metadata: metadata("helm"), }, }, - { - name: "positive-kubectl-1.24.11", - fixtureDir: "test-fixtures/classifiers/dynamic/kubectl-1.24.11", - expected: pkg.Package{ - Name: "kubectl", - Version: "1.24.11", - Type: "binary", - PURL: "pkg:golang/k8s.io/kubectl@1.24.11", - Locations: locations("kubectl"), - Metadata: metadata("kubectl"), - }, - }, - { - name: "positive-kubectl-1.25.7", - fixtureDir: "test-fixtures/classifiers/dynamic/kubectl-1.25.7", - expected: pkg.Package{ - Name: "kubectl", - Version: "1.25.7", - Type: "binary", - PURL: "pkg:golang/k8s.io/kubectl@1.25.7", - Locations: locations("kubectl"), - Metadata: metadata("kubectl"), - }, - }, - { - name: "positive-kubectl-1.26.2", - fixtureDir: "test-fixtures/classifiers/dynamic/kubectl-1.26.2", - expected: pkg.Package{ - Name: "kubectl", - Version: "1.26.2", - Type: "binary", - PURL: "pkg:golang/k8s.io/kubectl@1.26.2", - Locations: locations("kubectl"), - Metadata: metadata("kubectl"), - }, - }, - { - name: "positive-kustomize-4.5.7", - fixtureDir: "test-fixtures/classifiers/dynamic/kustomize-4.5.7", - expected: pkg.Package{ - Name: "kustomize", - Version: "4.5.7", - Type: "binary", - PURL: "pkg:golang/sigs.k8s.io/kustomize@4.5.7", - Locations: locations("kustomize"), - Metadata: metadata("kustomize"), - }, - }, - { - name: "positive-kustomize-5.0.0", - fixtureDir: "test-fixtures/classifiers/dynamic/kustomize-5.0.0", - expected: pkg.Package{ - Name: "kustomize", - Version: "5.0.0", - Type: "binary", - PURL: "pkg:golang/sigs.k8s.io/kustomize@5.0.0", - Locations: locations("kustomize"), - Metadata: metadata("kustomize"), - }, - }, { name: "positive-redis-4.0.11", fixtureDir: "test-fixtures/classifiers/positive/redis-server-4.0.11", diff --git a/syft/pkg/cataloger/binary/default_classifiers.go b/syft/pkg/cataloger/binary/default_classifiers.go index 8aa739b4b..5c2f2e17f 100644 --- a/syft/pkg/cataloger/binary/default_classifiers.go +++ b/syft/pkg/cataloger/binary/default_classifiers.go @@ -46,15 +46,6 @@ var defaultClassifiers = []classifier{ PURL: mustPURL("pkg:generic/go@version"), CPEs: singleCPE("cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*"), }, - { - Class: "argocd", - FileGlob: "**/argocd", - EvidenceMatcher: fileContentsVersionMatcher( - `(?m)common\.version=(?P[0-9]+\.[0-9]+\.[0-9]+)`), - Package: "argocd", - PURL: mustPURL("pkg:golang/github.com/argoproj/argo-cd@version"), - CPEs: singleCPE("cpe:2.3:a:argoproj:argocd:*:*:*:*:*:*:*"), - }, { Class: "helm", FileGlob: "**/helm", @@ -64,24 +55,6 @@ var defaultClassifiers = []classifier{ PURL: mustPURL("pkg:golang/helm.sh/helm@version"), CPEs: singleCPE("cpe:2.3:a:helm:helm:*:*:*:*:*:*:*"), }, - { - Class: "kustomize", - FileGlob: "**/kustomize", - EvidenceMatcher: fileContentsVersionMatcher( - `(?m)version=kustomize/v(?P[0-9]+\.[0-9]+\.[0-9]+)`), - Package: "kustomize", - PURL: mustPURL("pkg:golang/sigs.k8s.io/kustomize@version"), - CPEs: singleCPE("cpe:2.3:a:kustomize:kustomize:*:*:*:*:*:*:*"), - }, - { - Class: "kubectl", - FileGlob: "**/kubectl", - EvidenceMatcher: fileContentsVersionMatcher( - `(?m)\x00v(?P[0-9]+\.[0-9]+\.[0-9]+)\x00`), - Package: "kubectl", - PURL: mustPURL("pkg:golang/k8s.io/kubectl@version"), - CPEs: singleCPE("cpe:2.3:a:kubectl:kubectl:*:*:*:*:*:*:*"), - }, { Class: "redis-binary", FileGlob: "**/redis-server", diff --git a/syft/pkg/cataloger/binary/test-fixtures/Makefile b/syft/pkg/cataloger/binary/test-fixtures/Makefile index afd160d8f..4ed523068 100644 --- a/syft/pkg/cataloger/binary/test-fixtures/Makefile +++ b/syft/pkg/cataloger/binary/test-fixtures/Makefile @@ -7,15 +7,8 @@ all: \ classifiers/dynamic/ruby-library-3.2.1 \ classifiers/dynamic/ruby-library-2.7.7 \ classifiers/dynamic/ruby-library-2.6.10 \ - classifiers/dynamic/argocd-2.5.11 \ - classifiers/dynamic/argocd-2.6.4 \ classifiers/dynamic/helm-3.11.1 \ classifiers/dynamic/helm-3.10.3 \ - classifiers/dynamic/kubectl-1.24.11 \ - classifiers/dynamic/kubectl-1.25.7 \ - classifiers/dynamic/kubectl-1.26.2 \ - classifiers/dynamic/kustomize-4.5.7 \ - classifiers/dynamic/kustomize-5.0.0 \ classifiers/dynamic/consul-1.15.2 @@ -89,18 +82,6 @@ classifiers/dynamic/ruby-library-2.6.10: /usr/local/lib/libruby.so.2.6 \ $@/libruby.so.2.6 -classifiers/dynamic/argocd-2.5.11: - $(eval $@_image := "argoproj/argocd:v2.5.11@sha256:d1062935b3256ec69422843ebcb50debb54fd389436961586000c8ce6ee7f249") - ./get-image-file.sh $($@_image) \ - /usr/local/bin/argocd \ - $@/argocd - -classifiers/dynamic/argocd-2.6.4: - $(eval $@_image := "argoproj/argocd:v2.6.4@sha256:61fcbba187ff53c00696cb580edf70cada59c45cf399d8477631acf43cf522ee") - ./get-image-file.sh $($@_image) \ - /usr/local/bin/argocd \ - $@/argocd - classifiers/dynamic/helm-3.11.1: $(eval $@_image := "alpine/helm:3.11.1@sha256:8628e3695fb743a8b9de89626f1b7a221280c2152c0e288c2504e59b68233e8b") ./get-image-file.sh $($@_image) \ @@ -113,36 +94,6 @@ classifiers/dynamic/helm-3.10.3: /usr/local/bin/helm \ $@/helm -classifiers/dynamic/kubectl-1.24.11: - $(eval $@_image := "bitnami/kubectl:1.24.11@sha256:79d60c5ac8a1dc84e2c39f56d8e8cc0053159b5ed88f283bdf8fbda1ee86c8bc") - ./get-image-file.sh $($@_image) \ - /opt/bitnami/kubectl/bin/kubectl \ - $@/kubectl - -classifiers/dynamic/kubectl-1.25.7: - $(eval $@_image := "bitnami/kubectl:1.25.7@sha256:d7b00dbfdc6d8890aefe40edfb6c1d4c90cbb6c978794bb51a21744edc34ba7a") - ./get-image-file.sh $($@_image) \ - /opt/bitnami/kubectl/bin/kubectl \ - $@/kubectl - -classifiers/dynamic/kubectl-1.26.2: - $(eval $@_image := "line/kubectl-kustomize:1.26.2-5.0.0@sha256:9ee3b4a9a21f0777fc1d8c64208290f818a2e68c5e9e892e931621bda089bf06") - ./get-image-file.sh $($@_image) \ - /usr/local/bin/kubectl \ - $@/kubectl - -classifiers/dynamic/kustomize-4.5.7: - $(eval $@_image := "argoproj/argocd:v2.6.4@sha256:61fcbba187ff53c00696cb580edf70cada59c45cf399d8477631acf43cf522ee") - ./get-image-file.sh $($@_image) \ - /usr/local/bin/kustomize \ - $@/kustomize - -classifiers/dynamic/kustomize-5.0.0: - $(eval $@_image := "line/kubectl-kustomize:1.26.2-5.0.0@sha256:9ee3b4a9a21f0777fc1d8c64208290f818a2e68c5e9e892e931621bda089bf06") - ./get-image-file.sh $($@_image) \ - /usr/local/bin/kustomize \ - $@/kustomize - classifiers/dynamic/consul-1.15.2: $(eval $@_image := "hashicorp/consul:1.15.2@sha256:c2169f3bb18dd947ae8eb5f6766896695c71fb439f050a3343e0007d895615b8") ./get-image-file.sh $($@_image) \ diff --git a/syft/pkg/cataloger/golang/package.go b/syft/pkg/cataloger/golang/package.go index 2e4c28089..a7b1ee44e 100644 --- a/syft/pkg/cataloger/golang/package.go +++ b/syft/pkg/cataloger/golang/package.go @@ -18,7 +18,7 @@ func (c *goBinaryCataloger) newGoBinaryPackage(resolver source.FileResolver, dep licenses, err := c.licenses.getLicenses(resolver, dep.Path, dep.Version) if err != nil { - log.Tracef("error getting licenses for package: %s %v", dep.Path, err) + log.Tracef("error getting licenses for golang package: %s %v", dep.Path, err) } p := pkg.Package{ diff --git a/syft/pkg/cataloger/golang/parse_go_binary.go b/syft/pkg/cataloger/golang/parse_go_binary.go index c7b99fd25..542fcbe02 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary.go +++ b/syft/pkg/cataloger/golang/parse_go_binary.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "regexp" "runtime/debug" "strings" "time" @@ -34,6 +35,11 @@ var ( // devel is used to recognize the current default version when a golang main distribution is built // https://github.com/golang/go/issues/29228 this issue has more details on the progress of being able to // inject the correct version into the main module of the build process + + knownBuildFlagPatterns = []*regexp.Regexp{ + regexp.MustCompile(`(?m)\.([gG]it)?([bB]uild)?[vV]ersion=(\S+/)*(?Pv?\d+.\d+.\d+[-\w]*)`), + regexp.MustCompile(`(?m)\.([tT]ag)=(\S+/)*(?Pv?\d+.\d+.\d+[-\w]*)`), + } ) const devel = "(devel)" @@ -71,27 +77,82 @@ func (c *goBinaryCataloger) makeGoMainPackage(resolver source.FileResolver, mod gbs, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ) - if main.Version == devel { - if version, ok := gbs["vcs.revision"]; ok { - if timestamp, ok := gbs["vcs.time"]; ok { - //NOTE: err is ignored, because if parsing fails - // we still use the empty Time{} struct to generate an empty date, like 00010101000000 - // for consistency with the pseudo-version format: https://go.dev/ref/mod#pseudo-versions - ts, _ := time.Parse(time.RFC3339, timestamp) - if len(version) >= 12 { - version = version[:12] - } - version = module.PseudoVersion("", "", ts, version) + + if main.Version != devel { + return main + } + + version, hasVersion := gbs["vcs.revision"] + timestamp, hasTimestamp := gbs["vcs.time"] + + if hasVersion { + if hasTimestamp { + //NOTE: err is ignored, because if parsing fails + // we still use the empty Time{} struct to generate an empty date, like 00010101000000 + // for consistency with the pseudo-version format: https://go.dev/ref/mod#pseudo-versions + ts, _ := time.Parse(time.RFC3339, timestamp) + if len(version) >= 12 { + version = version[:12] + } + + var ldflags string + if metadata, ok := main.Metadata.(pkg.GolangBinMetadata); ok { + ldflags = metadata.BuildSettings["-ldflags"] + } + + majorVersion, fullVersion := extractVersionFromLDFlags(ldflags) + if fullVersion != "" { + // we've found a specific version from the ldflags! use it as the version. + // why not combine that with the pseudo version (e.g. v1.2.3-0.20210101000000-abcdef123456)? + // short answer: we're assuming that if a specific semver was provided in the ldflags that + // there is a matching vcs tag to match that could be referenced. This assumption could + // be incorrect in terms of the go.mod contents, but is not incorrect in terms of the logical + // version of the package. + version = fullVersion + } else { + version = module.PseudoVersion(majorVersion, fullVersion, ts, version) } - main.Version = version - main.PURL = packageURL(main.Name, main.Version) - main.SetID() } + + main.Version = version + main.PURL = packageURL(main.Name, main.Version) + + main.SetID() } return main } +func extractVersionFromLDFlags(ldflags string) (majorVersion string, fullVersion string) { + if ldflags == "" { + return "", "" + } + + for _, pattern := range knownBuildFlagPatterns { + groups := internal.MatchNamedCaptureGroups(pattern, ldflags) + v, ok := groups["version"] + + if !ok { + continue + } + + fullVersion = v + if !strings.HasPrefix(v, "v") { + fullVersion = fmt.Sprintf("v%s", v) + } + components := strings.Split(v, ".") + + if len(components) == 0 { + continue + } + + majorVersion = strings.TrimPrefix(components[0], "v") + return majorVersion, fullVersion + } + + return "", "" +} + // getArchs finds a binary architecture by two ways: // 1) reading build info from binaries compiled by go1.18+ // 2) reading file headers from binaries compiled by < go1.18 diff --git a/syft/pkg/cataloger/golang/parse_go_binary_test.go b/syft/pkg/cataloger/golang/parse_go_binary_test.go index d180d158f..cb0d9e3eb 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary_test.go +++ b/syft/pkg/cataloger/golang/parse_go_binary_test.go @@ -299,7 +299,101 @@ func TestBuildGoPkgInfo(t *testing.T) { expected: []pkg.Package{unmodifiedMain}, }, { - name: "parse main mod and replace devel version", + name: "parse main mod and replace devel pseudo version and ldflags exists (but contains no version)", + arch: archDetails, + mod: &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, + {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`}, + }, + }, + expected: []pkg.Package{ + { + Name: "github.com/anchore/syft", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Version: "v0.0.0-20221014195457-41bc6bb41035", + PURL: "pkg:golang/github.com/anchore/syft@v0.0.0-20221014195457-41bc6bb41035", + Locations: source.NewLocationSet( + source.NewLocationFromCoordinates( + source.Coordinates{ + RealPath: "/a-path", + FileSystemID: "layer-id", + }, + ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ), + MetadataType: pkg.GolangBinMetadataType, + Metadata: pkg.GolangBinMetadata{ + GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, + BuildSettings: map[string]string{ + "GOARCH": archDetails, + "GOOS": "darwin", + "GOAMD64": "v1", + "vcs.revision": "41bc6bb410352845f22766e27dd48ba93aa825a4", + "vcs.time": "2022-10-14T19:54:57Z", + "-ldflags": `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`, + }, + MainModule: "github.com/anchore/syft", + }, + }, + }, + }, + { + name: "parse main mod and replace devel version with one from ldflags", + arch: archDetails, + mod: &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, + {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, + }, + }, + expected: []pkg.Package{ + { + Name: "github.com/anchore/syft", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Version: "v0.79.0", + PURL: "pkg:golang/github.com/anchore/syft@v0.79.0", + Locations: source.NewLocationSet( + source.NewLocationFromCoordinates( + source.Coordinates{ + RealPath: "/a-path", + FileSystemID: "layer-id", + }, + ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ), + MetadataType: pkg.GolangBinMetadataType, + Metadata: pkg.GolangBinMetadata{ + GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, + BuildSettings: map[string]string{ + "GOARCH": archDetails, + "GOOS": "darwin", + "GOAMD64": "v1", + "vcs.revision": "41bc6bb410352845f22766e27dd48ba93aa825a4", + "vcs.time": "2022-10-14T19:54:57Z", + "-ldflags": `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`, + }, + MainModule: "github.com/anchore/syft", + }, + }, + }, + }, + { + name: "parse main mod and replace devel version with a pseudo version", arch: archDetails, mod: &debug.BuildInfo{ GoVersion: goCompiledVersion, @@ -512,3 +606,161 @@ func TestBuildGoPkgInfo(t *testing.T) { }) } } + +func Test_extractVersionFromLDFlags(t *testing.T) { + tests := []struct { + name string + ldflags string + wantMajorVersion string + wantFullVersion string + }{ + { + name: "empty ldflags", + ldflags: "", + }, + { + name: "syft ldflags", + ldflags: ` build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0 -X github.com/anchore/syft/internal/version.gitCommit=b2b332e8b2b66af0905e98b54ebd713a922be1a8 -X github.com/anchore/syft/internal/version.buildDate=2023-04-21T16:20:25Z -X github.com/anchore/syft/internal/version.gitDescription=v0.79.0 "`, + wantMajorVersion: "0", + wantFullVersion: "v0.79.0", + }, + { + name: "kubectl ldflags", + ldflags: ` build -asmflags=all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes + build -compiler=gc + build -gcflags="all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes " + build -ldflags="all=-X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.buildDate=2023-04-12T12:16:51Z' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.buildDate=2023-04-12T12:16:51Z' -X 'k8s.io/client-go/pkg/version.buildDate=2023-04-12T12:16:51Z' -X 'k8s.io/component-base/version.buildDate=2023-04-12T12:16:51Z' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitCommit=a1a87a0a2bcd605820920c6b0e618a8ab7d117d4' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitCommit=a1a87a0a2bcd605820920c6b0e618a8ab7d117d4' -X 'k8s.io/client-go/pkg/version.gitCommit=a1a87a0a2bcd605820920c6b0e618a8ab7d117d4' -X 'k8s.io/component-base/version.gitCommit=a1a87a0a2bcd605820920c6b0e618a8ab7d117d4' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitTreeState=clean' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitTreeState=clean' -X 'k8s.io/client-go/pkg/version.gitTreeState=clean' -X 'k8s.io/component-base/version.gitTreeState=clean' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitVersion=v1.25.9' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitVersion=v1.25.9' -X 'k8s.io/client-go/pkg/version.gitVersion=v1.25.9' -X 'k8s.io/component-base/version.gitVersion=v1.25.9' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitMajor=1' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitMajor=1' -X 'k8s.io/client-go/pkg/version.gitMajor=1' -X 'k8s.io/component-base/version.gitMajor=1' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitMinor=25' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitMinor=25' -X 'k8s.io/client-go/pkg/version.gitMinor=25' -X 'k8s.io/component-base/version.gitMinor=25' -s -w"`, + wantMajorVersion: "1", + wantFullVersion: "v1.25.9", + }, + { + name: "nerdctl ldflags", + ldflags: ` build -ldflags="-s -w -X github.com/containerd/nerdctl/pkg/version.Version=v1.3.1 -X github.com/containerd/nerdctl/pkg/version.Revision=b224b280ff3086516763c7335fc0e0997aca617a"`, + wantMajorVersion: "1", + wantFullVersion: "v1.3.1", + }, + { + name: "limactl ldflags", + ldflags: ` build -ldflags="-s -w -X github.com/lima-vm/lima/pkg/version.Version=v0.15.1"`, + wantMajorVersion: "0", + wantFullVersion: "v0.15.1", + }, + { + name: "terraform ldflags", + ldflags: ` build -ldflags="-w -s -X 'github.com/hashicorp/terraform/version.Version=1.4.6' -X 'github.com/hashicorp/terraform/version.Prerelease='"`, + wantMajorVersion: "1", + wantFullVersion: "v1.4.6", + }, + { + name: "kube-apiserver ldflags", + ldflags: ` build -asmflags=all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes + build -buildmode=exe + build -compiler=gc + build -gcflags="all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes " + build -ldflags="all=-X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.buildDate=2023-04-14T13:14:42Z' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.buildDate=2023-04-14T13:14:42Z' -X 'k8s.io/client-go/pkg/version.buildDate=2023-04-14T13:14:42Z' -X 'k8s.io/component-base/version.buildDate=2023-04-14T13:14:42Z' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitCommit=4c9411232e10168d7b050c49a1b59f6df9d7ea4b' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitCommit=4c9411232e10168d7b050c49a1b59f6df9d7ea4b' -X 'k8s.io/client-go/pkg/version.gitCommit=4c9411232e10168d7b050c49a1b59f6df9d7ea4b' -X 'k8s.io/component-base/version.gitCommit=4c9411232e10168d7b050c49a1b59f6df9d7ea4b' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitTreeState=clean' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitTreeState=clean' -X 'k8s.io/client-go/pkg/version.gitTreeState=clean' -X 'k8s.io/component-base/version.gitTreeState=clean' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitVersion=v1.27.1' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitVersion=v1.27.1' -X 'k8s.io/client-go/pkg/version.gitVersion=v1.27.1' -X 'k8s.io/component-base/version.gitVersion=v1.27.1' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitMajor=1' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitMajor=1' -X 'k8s.io/client-go/pkg/version.gitMajor=1' -X 'k8s.io/component-base/version.gitMajor=1' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitMinor=27' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitMinor=27' -X 'k8s.io/client-go/pkg/version.gitMinor=27' -X 'k8s.io/component-base/version.gitMinor=27' -s -w"`, + wantMajorVersion: "1", + wantFullVersion: "v1.27.1", + }, + { + name: "prometheus ldflags", + ldflags: ` build -ldflags="-X github.com/prometheus/common/version.Version=2.44.0 -X github.com/prometheus/common/version.Revision=1ac5131f698ebc60f13fe2727f89b115a41f6558 -X github.com/prometheus/common/version.Branch=HEAD -X github.com/prometheus/common/version.BuildUser=root@739e8181c5db -X github.com/prometheus/common/version.BuildDate=20230514-06:18:11 -extldflags '-static'" + build -tags=netgo,builtinassets,stringlabels`, + wantMajorVersion: "2", + wantFullVersion: "v2.44.0", + }, + { + name: "influxdb ldflags", + ldflags: ` build -ldflags="-s -w -X main.version=v2.7.1 -X main.commit=407fa622e9 -X main.date=2023-04-28T13:24:27Z -linkmode=external -extld=/musl/x86_64/bin/musl-gcc -extldflags '-fno-PIC -static-pie -Wl,-z,stack-size=8388608'" + build -tags=assets,sqlite_foreign_keys,sqlite_json,static_build,noasm`, + wantMajorVersion: "2", + wantFullVersion: "v2.7.1", + }, + { + name: "gitea ldflags", + ldflags: ` build -ldflags=" -X \"main.MakeVersion=GNU Make 4.1\" -X \"main.Version=1.19.3\" -X \"main.Tags=bindata sqlite sqlite_unlock_notify\" "`, + wantMajorVersion: "1", + wantFullVersion: "v1.19.3", + }, + { + name: "docker sbom cli ldflags", + ldflags: ` build -ldflags="-w -s -extldflags '-static' -X github.com/docker/sbom-cli-plugin/internal/version.version=0.6.1-SNAPSHOT-02cf1c8 -X github.com/docker/sbom-cli-plugin/internal/version.gitCommit=02cf1c888ad6662109ac6e3be618392514a56316 -X github.com/docker/sbom-cli-plugin/internal/version.gitDescription=v0.6.1-dirty "`, + wantMajorVersion: "0", + wantFullVersion: "v0.6.1-SNAPSHOT-02cf1c8", + }, + { + name: "docker scout ldflags", + ldflags: ` build -ldflags="-w -s -extldflags '-static' -X github.com/docker/scout-cli-plugin/internal.version=0.10.0 "`, + wantMajorVersion: "0", + wantFullVersion: "v0.10.0", + }, + { + name: "influx telegraf ldflags", + ldflags: ` build -ldflags="-w -s -X github.com/influxdata/telegraf/internal.Commit=a3a884a1 -X github.com/influxdata/telegraf/internal.Branch=HEAD -X github.com/influxdata/telegraf/internal.Version=1.26.2"`, + wantMajorVersion: "1", + wantFullVersion: "v1.26.2", + }, + { + name: "argocd ldflags", + ldflags: ` build -ldflags="-X github.com/argoproj/argo-cd/v2/common.version=2.7.2 -X github.com/argoproj/argo-cd/v2/common.buildDate=2023-05-12T14:06:49Z -X github.com/argoproj/argo-cd/v2/common.gitCommit=cbee7e6011407ed2d1066c482db74e97e0cc6bdb -X github.com/argoproj/argo-cd/v2/common.gitTreeState=clean -X github.com/argoproj/argo-cd/v2/common.kubectlVersion=v0.24.2 -extldflags=\"-static\""`, + wantMajorVersion: "2", + wantFullVersion: "v2.7.2", + }, + { + name: "kustomize ldflags", + ldflags: ` build -ldflags="-s -X sigs.k8s.io/kustomize/api/provenance.version=kustomize/v4.5.7 -X sigs.k8s.io/kustomize/api/provenance.gitCommit=56d82a8378dfc8dc3b3b1085e5a6e67b82966bd7 -X sigs.k8s.io/kustomize/api/provenance.buildDate=2022-08-02T16:35:54Z "`, + wantMajorVersion: "4", + wantFullVersion: "v4.5.7", + }, + ////////////////////////////////////////////////////////////////// + // negative cases + { + name: "hugo ldflags", + ldflags: ` build -ldflags="-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio"`, + }, + { + name: "ghostunnel ldflags", + ldflags: ` build -ldflags="-X main.version=77d9aaa"`, + }, + { + name: "opa ldflags", + ldflags: `build -ldflags=" -X github.com/open-policy-agent/opa/version.Hostname=9549178459bc"`, + }, + /////////////////////////////////////////////////////////////////// + // trickier cases + { + name: "macvlan plugin for cri-o ldflags", + ldflags: ` build -ldflags="-extldflags -static -X github.com/containernetworking/plugins/pkg/utils/buildversion.BuildVersion=v1.2.0"`, + wantMajorVersion: "1", + wantFullVersion: "v1.2.0", + }, + { + name: "coder ldflags", + ldflags: ` build -ldflags="-s -w -X 'github.com/coder/coder/buildinfo.tag=0.23.4'"`, + wantMajorVersion: "0", + wantFullVersion: "v0.23.4", + }, + /////////////////////////////////////////////////////////////////// + // don't know how to handle these... yet + //{ + // // package name: pkgName: "github.com/krakendio/krakend-ce/v2", + // name: "krakenD ldflags", + // ldflags: ` build -ldflags="-X github.com/luraproject/lura/v2/core.KrakendVersion=2.3.2 -X github.com/luraproject/lura/v2/core.GoVersion=1.20.4 -X github.com/luraproject/lura/v2/core.GlibcVersion=GLIBC-2.31_(debian-11) "`, + // wantMajorVersion: "2.3.2", + // wantFullVersion: "v2.3.2", + //}, + //{ + // // package name: pkgName: "github.com/krakendio/krakend-ce/v2", + // name: "krakenD ldflags -- answer embedded in the middle", + // ldflags: ` build -ldflags=" -X github.com/luraproject/lura/v2/core.GoVersion=1.20.4 -X github.com/luraproject/lura/v2/core.KrakendVersion=2.3.2 -X github.com/luraproject/lura/v2/core.GlibcVersion=GLIBC-2.31_(debian-11) "`, + // wantMajorVersion: "2.3.2", + // wantFullVersion: "v2.3.2", + //}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotMajorVersion, gotFullVersion := extractVersionFromLDFlags(tt.ldflags) + assert.Equal(t, tt.wantMajorVersion, gotMajorVersion, "unexpected major version") + assert.Equal(t, tt.wantFullVersion, gotFullVersion, "unexpected full version") + }) + } +}