From 63ba7ae47dfdf2b465bcad966bbde1abbf2f852f Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 23 Jul 2020 20:35:40 -0400 Subject: [PATCH] add remaining debian & el distro support (#99) --- imgbom/distro/identify.go | 43 ++++++--- imgbom/distro/identify_test.go | 92 +++++++++++++++---- .../test-fixtures/os/alpine/etc/os-release | 6 ++ .../test-fixtures/os/amazon/etc/os-release | 9 ++ .../test-fixtures/os/busybox/bin/busybox | 1 + .../os/centos/usr/lib/os-release | 16 ++++ .../os/debian/usr/lib/os-release | 8 ++ .../os/fedora/usr/lib/os-release | 21 +++++ .../os/redhat/usr/lib/os-release | 15 +++ .../{ubuntu-20.04 => ubuntu}/etc/os-release | 0 imgbom/distro/type.go | 57 ++++++------ imgbom/lib.go | 9 +- 12 files changed, 213 insertions(+), 64 deletions(-) create mode 100644 imgbom/distro/test-fixtures/os/alpine/etc/os-release create mode 100644 imgbom/distro/test-fixtures/os/amazon/etc/os-release create mode 100644 imgbom/distro/test-fixtures/os/busybox/bin/busybox create mode 100644 imgbom/distro/test-fixtures/os/centos/usr/lib/os-release create mode 100644 imgbom/distro/test-fixtures/os/debian/usr/lib/os-release create mode 100644 imgbom/distro/test-fixtures/os/fedora/usr/lib/os-release create mode 100644 imgbom/distro/test-fixtures/os/redhat/usr/lib/os-release rename imgbom/distro/test-fixtures/os/{ubuntu-20.04 => ubuntu}/etc/os-release (100%) diff --git a/imgbom/distro/identify.go b/imgbom/distro/identify.go index a3595582f..548e9ff49 100644 --- a/imgbom/distro/identify.go +++ b/imgbom/distro/identify.go @@ -12,21 +12,38 @@ import ( // returns a distro or nil type parseFunc func(string) *Distro +type parseEntry struct { + path file.Path + fn parseFunc +} + // Identify parses distro-specific files to determine distro metadata like version and release func Identify(s scope.Scope) Distro { distro := NewUnknownDistro() - identityFiles := map[file.Path]parseFunc{ - "/etc/os-release": parseOsRelease, - // Debian and Debian-based distros have the same contents linked from this path - "/usr/lib/os-release": parseOsRelease, - "/bin/busybox": parseBusyBox, + identityFiles := []parseEntry{ + { + // most distros provide a link at this location + path: "/etc/os-release", + fn: parseOsRelease, + }, + { + // standard location for rhel & debian distros + path: "/usr/lib/os-release", + fn: parseOsRelease, + }, + { + // check for busybox (important to check this last since other distros contain the busybox binary) + path: "/bin/busybox", + fn: parseBusyBox, + }, } - for path, fn := range identityFiles { - refs, err := s.FilesByPath(path) +identifyLoop: + for _, entry := range identityFiles { + refs, err := s.FilesByPath(entry.path) if err != nil { - log.Errorf("unable to get path refs from %s: %s", path, err) + log.Errorf("unable to get path refs from %s: %s", entry.path, err) break } @@ -44,18 +61,18 @@ func Identify(s scope.Scope) Distro { } if err != nil { - log.Debugf("unable to get contents from %s: %s", path, err) + log.Debugf("unable to get contents from %s: %s", entry.path, err) continue } if content == "" { - log.Debugf("no contents in file, skipping: %s", path) + log.Debugf("no contents in file, skipping: %s", entry.path) continue } - if candidateDistro := fn(content); candidateDistro != nil { + if candidateDistro := entry.fn(content); candidateDistro != nil { distro = *candidateDistro - break + break identifyLoop } } } @@ -64,7 +81,7 @@ func Identify(s scope.Scope) Distro { } func assemble(name, version string) *Distro { - distroType, ok := Mappings[name] + distroType, ok := IDMapping[name] // Both distro and version must be present if len(name) == 0 || len(version) == 0 { diff --git a/imgbom/distro/identify_test.go b/imgbom/distro/identify_test.go index deb556f5d..549f552b8 100644 --- a/imgbom/distro/identify_test.go +++ b/imgbom/distro/identify_test.go @@ -6,45 +6,103 @@ import ( "os" "testing" + "github.com/anchore/imgbom/internal" + "github.com/anchore/imgbom/imgbom/scope" ) func TestIdentifyDistro(t *testing.T) { tests := []struct { - fixture string - name string - RawVersion string - Type Type + fixture string + Type Type + Version string }{ { - fixture: "test-fixtures/os/ubuntu-20.04", - name: "ubuntu", + fixture: "test-fixtures/os/alpine", + Type: Alpine, + Version: "3.11.6", + }, + { + fixture: "test-fixtures/os/amazon", + Type: AmazonLinux, + Version: "2.0.0", + }, + { + fixture: "test-fixtures/os/busybox", + Type: Busybox, + Version: "1.31.1", + }, + { + fixture: "test-fixtures/os/centos", + Type: CentOS, + Version: "8.0.0", + }, + { + fixture: "test-fixtures/os/debian", + Type: Debian, + Version: "8.0.0", + }, + { + fixture: "test-fixtures/os/fedora", + Type: Fedora, + Version: "31.0.0", + }, + { + fixture: "test-fixtures/os/redhat", + Type: RedHat, + Version: "7.3.0", + }, + { + fixture: "test-fixtures/os/ubuntu", Type: Ubuntu, + Version: "20.4.0", }, { fixture: "test-fixtures/os/empty", - name: "No OS files", Type: UnknownDistroType, }, { fixture: "test-fixtures/os/unmatchable", - name: "Unmatchable distro", Type: UnknownDistroType, }, } + observedDistros := internal.NewStringSet() + definedDistros := internal.NewStringSet() + for _, d := range All { + definedDistros.Add(d.String()) + } + for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + t.Run(test.fixture, func(t *testing.T) { s, err := scope.NewScopeFromDir(test.fixture, scope.AllLayersScope) if err != nil { t.Fatalf("unable to produce a new scope for testing: %s", test.fixture) } - distro := Identify(s) - if distro.Type != test.Type { - t.Errorf("expected distro doesn't match: %v != %v", distro.Type, test.Type) + + d := Identify(s) + observedDistros.Add(d.String()) + + if d.Type != test.Type { + t.Errorf("expected distro doesn't match: %v != %v", d.Type, test.Type) + } + + if d.Type == UnknownDistroType && d.Version != nil { + t.Fatalf("version should be nil for unknown distros") + } else if d.Type == UnknownDistroType && d.Version == nil { + // don't check versions for unknown distro types + return + } + + if d.Version.String() != test.Version { + t.Errorf("expected distro version doesn't match: %v != %v", d.Version.String(), test.Version) } }) + } + // ensure that test cases stay in sync with the distros that can be identified + if len(observedDistros) < len(definedDistros) { + t.Errorf("distro coverage incomplete (distro=%d, coverage=%d)", len(definedDistros), len(observedDistros)) } } @@ -86,11 +144,10 @@ func TestParseOsRelease(t *testing.T) { for _, test := range tests { name := fmt.Sprintf("%s:%s", test.name, test.RawVersion) fixture, err := os.Open(test.fixture) - defer fixture.Close() - if err != nil { - t.Fatalf("failed to open fixture: %+v", err) + t.Fatalf("could not open test fixture=%s: %+v", test.fixture, err) } + defer fixture.Close() b, err := ioutil.ReadAll(fixture) if err != nil { @@ -131,11 +188,10 @@ func TestParseOsReleaseFailures(t *testing.T) { for _, test := range tests { name := fmt.Sprintf("%s:%s", test.name, test.fixture) fixture, err := os.Open(test.fixture) - defer fixture.Close() - if err != nil { - t.Fatalf("failed to open fixture: %+v", err) + t.Fatalf("could not open test fixture=%s: %+v", test.fixture, err) } + defer fixture.Close() b, err := ioutil.ReadAll(fixture) if err != nil { diff --git a/imgbom/distro/test-fixtures/os/alpine/etc/os-release b/imgbom/distro/test-fixtures/os/alpine/etc/os-release new file mode 100644 index 000000000..39f4591b7 --- /dev/null +++ b/imgbom/distro/test-fixtures/os/alpine/etc/os-release @@ -0,0 +1,6 @@ +NAME="Alpine Linux" +ID=alpine +VERSION_ID=3.11.6 +PRETTY_NAME="Alpine Linux v3.11" +HOME_URL="https://alpinelinux.org/" +BUG_REPORT_URL="https://bugs.alpinelinux.org/" diff --git a/imgbom/distro/test-fixtures/os/amazon/etc/os-release b/imgbom/distro/test-fixtures/os/amazon/etc/os-release new file mode 100644 index 000000000..07a45072f --- /dev/null +++ b/imgbom/distro/test-fixtures/os/amazon/etc/os-release @@ -0,0 +1,9 @@ +NAME="Amazon Linux" +VERSION="2" +ID="amzn" +ID_LIKE="centos rhel fedora" +VERSION_ID="2" +PRETTY_NAME="Amazon Linux 2" +ANSI_COLOR="0;33" +CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2" +HOME_URL="https://amazonlinux.com/" diff --git a/imgbom/distro/test-fixtures/os/busybox/bin/busybox b/imgbom/distro/test-fixtures/os/busybox/bin/busybox new file mode 100644 index 000000000..86c43ff13 --- /dev/null +++ b/imgbom/distro/test-fixtures/os/busybox/bin/busybox @@ -0,0 +1 @@ +junk...BusyBox v1.31.1more junk \ No newline at end of file diff --git a/imgbom/distro/test-fixtures/os/centos/usr/lib/os-release b/imgbom/distro/test-fixtures/os/centos/usr/lib/os-release new file mode 100644 index 000000000..399efbfae --- /dev/null +++ b/imgbom/distro/test-fixtures/os/centos/usr/lib/os-release @@ -0,0 +1,16 @@ +NAME="CentOS Linux" +VERSION="8 (Core)" +ID="centos" +ID_LIKE="rhel fedora" +VERSION_ID="8" +PLATFORM_ID="platform:el8" +PRETTY_NAME="CentOS Linux 8 (Core)" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:centos:centos:8" +HOME_URL="https://www.centos.org/" +BUG_REPORT_URL="https://bugs.centos.org/" + +CENTOS_MANTISBT_PROJECT="CentOS-8" +CENTOS_MANTISBT_PROJECT_VERSION="8" +REDHAT_SUPPORT_PRODUCT="centos" +REDHAT_SUPPORT_PRODUCT_VERSION="8" diff --git a/imgbom/distro/test-fixtures/os/debian/usr/lib/os-release b/imgbom/distro/test-fixtures/os/debian/usr/lib/os-release new file mode 100644 index 000000000..120c51b08 --- /dev/null +++ b/imgbom/distro/test-fixtures/os/debian/usr/lib/os-release @@ -0,0 +1,8 @@ +PRETTY_NAME="Debian GNU/Linux 8 (jessie)" +NAME="Debian GNU/Linux" +VERSION_ID="8" +VERSION="8 (jessie)" +ID=debian +HOME_URL="http://www.debian.org/" +SUPPORT_URL="http://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" diff --git a/imgbom/distro/test-fixtures/os/fedora/usr/lib/os-release b/imgbom/distro/test-fixtures/os/fedora/usr/lib/os-release new file mode 100644 index 000000000..8791e2694 --- /dev/null +++ b/imgbom/distro/test-fixtures/os/fedora/usr/lib/os-release @@ -0,0 +1,21 @@ +NAME=Fedora +VERSION="31 (Container Image)" +ID=fedora +VERSION_ID=31 +VERSION_CODENAME="" +PLATFORM_ID="platform:f31" +PRETTY_NAME="Fedora 31 (Container Image)" +ANSI_COLOR="0;34" +LOGO=fedora-logo-icon +CPE_NAME="cpe:/o:fedoraproject:fedora:31" +HOME_URL="https://fedoraproject.org/" +DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f31/system-administrators-guide/" +SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help" +BUG_REPORT_URL="https://bugzilla.redhat.com/" +REDHAT_BUGZILLA_PRODUCT="Fedora" +REDHAT_BUGZILLA_PRODUCT_VERSION=31 +REDHAT_SUPPORT_PRODUCT="Fedora" +REDHAT_SUPPORT_PRODUCT_VERSION=31 +PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy" +VARIANT="Container Image" +VARIANT_ID=container diff --git a/imgbom/distro/test-fixtures/os/redhat/usr/lib/os-release b/imgbom/distro/test-fixtures/os/redhat/usr/lib/os-release new file mode 100644 index 000000000..c6ad68010 --- /dev/null +++ b/imgbom/distro/test-fixtures/os/redhat/usr/lib/os-release @@ -0,0 +1,15 @@ +NAME="Red Hat Enterprise Linux Server" +VERSION="7.3 (Maipo)" +ID="rhel" +ID_LIKE="fedora" +VERSION_ID="7.3" +PRETTY_NAME="Red Hat Enterprise Linux Server 7.3 (Maipo)" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:redhat:enterprise_linux:7.3:GA:server" +HOME_URL="https://www.redhat.com/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" + +REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 7" +REDHAT_BUGZILLA_PRODUCT_VERSION=7.3 +REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="7.3" \ No newline at end of file diff --git a/imgbom/distro/test-fixtures/os/ubuntu-20.04/etc/os-release b/imgbom/distro/test-fixtures/os/ubuntu/etc/os-release similarity index 100% rename from imgbom/distro/test-fixtures/os/ubuntu-20.04/etc/os-release rename to imgbom/distro/test-fixtures/os/ubuntu/etc/os-release diff --git a/imgbom/distro/type.go b/imgbom/distro/type.go index 667ae61b7..091d2430d 100644 --- a/imgbom/distro/type.go +++ b/imgbom/distro/type.go @@ -6,17 +6,12 @@ const ( Ubuntu RedHat CentOS - // Fedora - // Alpine + Fedora + Alpine Busybox - // AmazonLinux - // OracleLinux - // ArchLinux -) - -const ( - // UnknownVersion is a default of 0.0.0 when it can't be parsed - UnknownVersion string = "0.0.0" + AmazonLinux + OracleLinux + //ArchLinux ) type Type int @@ -27,12 +22,12 @@ var distroStr = []string{ "ubuntu", "redhat", "centos", - // "fedora", - // "alpine", + "fedora", + "alpine", "busybox", - // "amazn", - // "oraclelinux", - // "archlinux", + "amazn", + "oraclelinux", + //"archlinux", } var All = []Type{ @@ -40,12 +35,12 @@ var All = []Type{ Ubuntu, RedHat, CentOS, - // Fedora, - // Alpine, + Fedora, + Alpine, Busybox, - // AmazonLinux, - // OracleLinux, - // ArchLinux, + AmazonLinux, + OracleLinux, + //ArchLinux, } func (t Type) String() string { @@ -56,16 +51,16 @@ func (t Type) String() string { return distroStr[t] } -// Mappings connects a distro ID like "ubuntu" to a Distro type -var Mappings = map[string]Type{ - "debian": Debian, - "ubuntu": Ubuntu, - "rhel": RedHat, - "centos": CentOS, - // "fedora": Fedora, - // "alpine": Alpine, +// IDMapping connects a distro ID like "ubuntu" to a Distro type +var IDMapping = map[string]Type{ + "debian": Debian, + "ubuntu": Ubuntu, + "rhel": RedHat, + "centos": CentOS, + "fedora": Fedora, + "alpine": Alpine, "busybox": Busybox, - // "amazn": AmazonLinux, - // "oraclelinux": OracleLinux, - // "archlinux": ArchLinux, + "amzn": AmazonLinux, + "ol": OracleLinux, + //"arch": ArchLinux, } diff --git a/imgbom/lib.go b/imgbom/lib.go index 6b7843ca0..e22dae76e 100644 --- a/imgbom/lib.go +++ b/imgbom/lib.go @@ -29,8 +29,13 @@ func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scop } func IdentifyDistro(s scope.Scope) distro.Distro { - log.Info("Identifying Distro") - return distro.Identify(s) + d := distro.Identify(s) + if d.Type != distro.UnknownDistroType { + log.Infof("Identified Distro: %s", d.String()) + } else { + log.Info("Could not identify distro") + } + return d } func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {