diff --git a/common/pkg/version/version.go b/common/pkg/version/version.go index 80261c125e..297f6f8bfe 100644 --- a/common/pkg/version/version.go +++ b/common/pkg/version/version.go @@ -13,6 +13,21 @@ const ( UnknownPackage = "Unknown" ) +// parseDlocateList extracts "_" from the output of +// `dlocate -P -l`, which lists one package per line in dpkg's format. +// It reports false if the output does not have the expected shape. +func parseDlocateList(out []byte) (string, bool) { + lines := strings.Split(string(out), "\n") + if len(lines) < 2 { + return "", false + } + f := strings.Fields(lines[len(lines)-2]) // the last line before the trailing newline + if len(f) < 3 { + return "", false + } + return f[1] + "_" + f[2], true +} + // Note: This function is copied from containers/podman libpod/util.go // Please see https://github.com/containers/common/pull/1460 func queryPackageVersion(cmdArg ...string) string { @@ -35,13 +50,8 @@ func queryPackageVersion(cmdArg ...string) string { cmd.Env = []string{"COLUMNS=160"} // show entire value // dlocate always returns exit code 1 for list command if outp, _ = cmd.Output(); len(outp) > 0 { - lines := strings.Split(string(outp), "\n") - if len(lines) > 1 { - line := lines[len(lines)-2] // trailing newline - f := strings.Fields(line) - if len(f) >= 2 { - return f[1] + "_" + f[2] - } + if pkg, ok := parseDlocateList(outp); ok { + return pkg } } case "/usr/bin/dpkg": diff --git a/common/pkg/version/version_test.go b/common/pkg/version/version_test.go new file mode 100644 index 0000000000..d12bbbf021 --- /dev/null +++ b/common/pkg/version/version_test.go @@ -0,0 +1,68 @@ +package version + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseDlocateList(t *testing.T) { + tests := []struct { + name string + out string + want string + wantOk bool + }{ + { + name: "package line in dpkg format", + out: "ii podman 4.9.3-1 arm64 engine to run OCI containers\n", + want: "podman_4.9.3-1", + wantOk: true, + }, + { + name: "last package line is used", + out: "ii crun 1.14-1 arm64 OCI runtime\nii podman 4.9.3-1 arm64 engine\n", + want: "podman_4.9.3-1", + wantOk: true, + }, + { + name: "exactly the three fields that are read", + out: "ii podman 4.9.3-1\n", + want: "podman_4.9.3-1", + wantOk: true, + }, + { + name: "line with too few fields to name a version", + out: "ii podman\n", + wantOk: false, + }, + { + name: "no trailing newline leaves nothing to read", + out: "ii podman 4.9.3-1 arm64 engine", + wantOk: false, + }, + { + name: "empty output", + out: "", + wantOk: false, + }, + { + name: "newline only", + out: "\n", + wantOk: false, + }, + { + name: "whitespace only line", + out: " \n", + wantOk: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, ok := parseDlocateList([]byte(tt.out)) + assert.Equal(t, tt.wantOk, ok) + assert.Equal(t, tt.want, got) + }) + } +}