Extract go module versions from ldflags for binaries built by go (#1832)

* wip

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* with golang bin ldflags refactor

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add test for golang binary cataloger for ldflag extraction

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* remove binary classfiers that overlap with new go ldflags detection

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

---------

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
Co-authored-by: Weston Steimel <weston.steimel@anchore.com>
This commit is contained in:
Alex Goodman 2023-05-23 10:27:48 -04:00 committed by GitHub
parent a3c5550217
commit 26c201f7f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 329 additions and 176 deletions

View File

@ -239,30 +239,6 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
Metadata: metadata("redis-binary"), 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", name: "positive-helm-3.11.1",
fixtureDir: "test-fixtures/classifiers/dynamic/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"), 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", name: "positive-redis-4.0.11",
fixtureDir: "test-fixtures/classifiers/positive/redis-server-4.0.11", fixtureDir: "test-fixtures/classifiers/positive/redis-server-4.0.11",

View File

@ -46,15 +46,6 @@ var defaultClassifiers = []classifier{
PURL: mustPURL("pkg:generic/go@version"), PURL: mustPURL("pkg:generic/go@version"),
CPEs: singleCPE("cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*"), CPEs: singleCPE("cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*"),
}, },
{
Class: "argocd",
FileGlob: "**/argocd",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)common\.version=(?P<version>[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", Class: "helm",
FileGlob: "**/helm", FileGlob: "**/helm",
@ -64,24 +55,6 @@ var defaultClassifiers = []classifier{
PURL: mustPURL("pkg:golang/helm.sh/helm@version"), PURL: mustPURL("pkg:golang/helm.sh/helm@version"),
CPEs: singleCPE("cpe:2.3:a:helm:helm:*:*:*:*:*:*:*"), CPEs: singleCPE("cpe:2.3:a:helm:helm:*:*:*:*:*:*:*"),
}, },
{
Class: "kustomize",
FileGlob: "**/kustomize",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)version=kustomize/v(?P<version>[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<version>[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", Class: "redis-binary",
FileGlob: "**/redis-server", FileGlob: "**/redis-server",

View File

@ -7,15 +7,8 @@ all: \
classifiers/dynamic/ruby-library-3.2.1 \ classifiers/dynamic/ruby-library-3.2.1 \
classifiers/dynamic/ruby-library-2.7.7 \ classifiers/dynamic/ruby-library-2.7.7 \
classifiers/dynamic/ruby-library-2.6.10 \ 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.11.1 \
classifiers/dynamic/helm-3.10.3 \ 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 classifiers/dynamic/consul-1.15.2
@ -89,18 +82,6 @@ classifiers/dynamic/ruby-library-2.6.10:
/usr/local/lib/libruby.so.2.6 \ /usr/local/lib/libruby.so.2.6 \
$@/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: classifiers/dynamic/helm-3.11.1:
$(eval $@_image := "alpine/helm:3.11.1@sha256:8628e3695fb743a8b9de89626f1b7a221280c2152c0e288c2504e59b68233e8b") $(eval $@_image := "alpine/helm:3.11.1@sha256:8628e3695fb743a8b9de89626f1b7a221280c2152c0e288c2504e59b68233e8b")
./get-image-file.sh $($@_image) \ ./get-image-file.sh $($@_image) \
@ -113,36 +94,6 @@ classifiers/dynamic/helm-3.10.3:
/usr/local/bin/helm \ /usr/local/bin/helm \
$@/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: classifiers/dynamic/consul-1.15.2:
$(eval $@_image := "hashicorp/consul:1.15.2@sha256:c2169f3bb18dd947ae8eb5f6766896695c71fb439f050a3343e0007d895615b8") $(eval $@_image := "hashicorp/consul:1.15.2@sha256:c2169f3bb18dd947ae8eb5f6766896695c71fb439f050a3343e0007d895615b8")
./get-image-file.sh $($@_image) \ ./get-image-file.sh $($@_image) \

View File

@ -18,7 +18,7 @@ func (c *goBinaryCataloger) newGoBinaryPackage(resolver source.FileResolver, dep
licenses, err := c.licenses.getLicenses(resolver, dep.Path, dep.Version) licenses, err := c.licenses.getLicenses(resolver, dep.Path, dep.Version)
if err != nil { 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{ p := pkg.Package{

View File

@ -8,6 +8,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"regexp"
"runtime/debug" "runtime/debug"
"strings" "strings"
"time" "time"
@ -34,6 +35,11 @@ var (
// devel is used to recognize the current default version when a golang main distribution is built // 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 // 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 // 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+/)*(?P<version>v?\d+.\d+.\d+[-\w]*)`),
regexp.MustCompile(`(?m)\.([tT]ag)=(\S+/)*(?P<version>v?\d+.\d+.\d+[-\w]*)`),
}
) )
const devel = "(devel)" const devel = "(devel)"
@ -71,27 +77,82 @@ func (c *goBinaryCataloger) makeGoMainPackage(resolver source.FileResolver, mod
gbs, gbs,
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
) )
if main.Version == devel {
if version, ok := gbs["vcs.revision"]; ok { if main.Version != devel {
if timestamp, ok := gbs["vcs.time"]; ok { return main
//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 version, hasVersion := gbs["vcs.revision"]
ts, _ := time.Parse(time.RFC3339, timestamp) timestamp, hasTimestamp := gbs["vcs.time"]
if len(version) >= 12 {
version = version[:12] if hasVersion {
} if hasTimestamp {
version = module.PseudoVersion("", "", ts, version) //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 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: // getArchs finds a binary architecture by two ways:
// 1) reading build info from binaries compiled by go1.18+ // 1) reading build info from binaries compiled by go1.18+
// 2) reading file headers from binaries compiled by < go1.18 // 2) reading file headers from binaries compiled by < go1.18

View File

@ -299,7 +299,101 @@ func TestBuildGoPkgInfo(t *testing.T) {
expected: []pkg.Package{unmodifiedMain}, 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, arch: archDetails,
mod: &debug.BuildInfo{ mod: &debug.BuildInfo{
GoVersion: goCompiledVersion, 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")
})
}
}