diff --git a/src/pkg/utils/utils.go b/src/pkg/utils/utils.go index 394136cb1..e48e2b48b 100644 --- a/src/pkg/utils/utils.go +++ b/src/pkg/utils/utils.go @@ -125,6 +125,9 @@ var ( "XTERM_VERSION", } + // func var to allow mocking in testing + osreleaseRead = osrelease.Read + releaseDefault string runtimeDirectories map[string]string @@ -201,7 +204,7 @@ func init() { distroDefault = distroFallback releaseDefault = releaseFallback - hostID, err := getHostID() + hostID, err := matchHostToSupportedDistro() if err == nil { if distroObj, supportedDistro := supportedDistros[hostID]; supportedDistro { release, err := getDefaultReleaseForDistro(hostID) @@ -455,7 +458,7 @@ func GetGroupForSudo() (string, error) { // Examples: // - host is Fedora, returned string is 'fedora' func getHostID() (string, error) { - osRelease, err := osrelease.Read() + osRelease, err := osreleaseRead() if err != nil { return "", err } @@ -463,12 +466,55 @@ func getHostID() (string, error) { return osRelease["ID"], nil } +// getHostIDLike returns the ID_LIKE from the os-release files +// +// Examples: +// - host is CentOS, returned []string is ['rhel', 'fedora'] +func getHostIDLike() ([]string, error) { + osRelease, err := osreleaseRead() + if err != nil { + return nil, err + } + + return strings.Fields(osRelease["ID_LIKE"]), nil +} + +// matchHostToSupportedDistro returns the first matching distro ID of host that is supported +// if none are supported, ErrDistroUnsupported +// +// Examples: +// - host is CentOS, returned string is 'rhel' +// - host is "linux", returned error is ErrDistroUnsupported +func matchHostToSupportedDistro() (string, error) { + distroID, err := getHostID() + if err != nil { + return "", err + } + + if _, ok := supportedDistros[distroID]; ok { + return distroID, nil + } + + distroIDLikeVals, err := getHostIDLike() + if err != nil { + return "", err + } + + for _, distroIDLike := range distroIDLikeVals { + if _, ok := supportedDistros[distroIDLike]; ok { + return distroIDLike, nil + } + } + + return "", &DistroError{distroID, ErrDistroUnsupported} +} + // getHostVersionID returns the VERSION_ID from the os-release files // // Examples: // - host is Fedora 32, returned string is '32' func getHostVersionID() (string, error) { - osRelease, err := osrelease.Read() + osRelease, err := osreleaseRead() if err != nil { return "", err } @@ -644,7 +690,7 @@ func IsP11KitClientPresent() (bool, error) { var p11KitClientPaths []string var supportedDistro bool - hostID, err := getHostID() + hostID, err := matchHostToSupportedDistro() if err == nil { distroObj, ok := supportedDistros[hostID] supportedDistro = ok diff --git a/src/pkg/utils/utils_test.go b/src/pkg/utils/utils_test.go index 8b9242968..d5beb9d36 100644 --- a/src/pkg/utils/utils_test.go +++ b/src/pkg/utils/utils_test.go @@ -417,3 +417,66 @@ func TestPathExistsSymlinkTargetExists(t *testing.T) { exists := PathExists(link) assert.True(t, exists) } + +func TestMatchHostToSupportedDistro(t *testing.T) { + osreleaseReadStash := osreleaseRead + t.Cleanup(func() { osreleaseRead = osreleaseReadStash }) + + testCases := []struct { + inputOsRelease map[string]string + output string + errMsg string + }{ + { + // 1) Supported ID + inputOsRelease: map[string]string{ + "ID": "ubuntu", + "ID_LIKE": "debian", + }, + output: "ubuntu", + }, + { + // 2) Unsupported ID, first ID_LIKE is supported + inputOsRelease: map[string]string{ + "ID": "centos", + "ID_LIKE": "rhel fedora", + }, + output: "rhel", + }, + { + // 3) Unsupported ID and first ID_LIKE, subsequent ID_LIKE is supported + inputOsRelease: map[string]string{ + "ID": "rocky", + "ID_LIKE": "centos rhel fedora", + }, + output: "rhel", + }, + { + // 4) Neither ID nor ID_LIKE values are supported + inputOsRelease: map[string]string{ + "ID": "android", + "ID_LIKE": "linux", + }, + errMsg: "android: distribution is unsupported", + }, + } + + for _, tc := range testCases { + name := "ID=" + tc.inputOsRelease["ID"] + ", ID_LIKE=" + tc.inputOsRelease["ID_LIKE"] + t.Run(name, func(t *testing.T) { + osreleaseRead = func() (map[string]string, error) { return tc.inputOsRelease, nil } + distro, err := matchHostToSupportedDistro() + + if tc.errMsg == "" { + assert.NoError(t, err) + assert.Equal(t, tc.output, distro) + } else { + assert.Error(t, err) + assert.EqualError(t, err, tc.errMsg) + + var errDistroUnsupported *DistroError + assert.ErrorAs(t, err, &errDistroUnsupported) + } + }) + } +}