Skip to content

Commit

Permalink
providers/linux/os.go - Get OS info from Manjaro Linux (#183)
Browse files Browse the repository at this point in the history
Manjaro was not working as expected because the `DISTRIB_RELEASE` value
was never used to get the OS version.

There was no OS distribution family for "arch". So this maps Majaro as
part of the "arch" family along with Arch Linux itself and Antergos.

Added an additional fallback to use the `ID_LIKE` field in `os-release`
to determine the OS distribution family.

I replaced the deprecated `io/ioutil` with the `os` package.

Fixes #168
  • Loading branch information
andrewkroh committed Jun 1, 2023
1 parent 7185883 commit 9ea2eba
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 22 deletions.
7 changes: 7 additions & 0 deletions .changelog/183.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
linux: Added an `os.Family` mapping for Arch Linux based distributions.
```

```release-note:bug
linux: Fix OS metadata collection for Manjaro Linux.
```
80 changes: 58 additions & 22 deletions providers/linux/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
Expand Down Expand Up @@ -50,12 +49,13 @@ var (

// familyMap contains a mapping of family -> []platforms.
var familyMap = map[string][]string{
"arch": {"arch", "antergos", "manjaro"},
"redhat": {
"redhat", "fedora", "centos", "scientific", "oraclelinux", "ol",
"amzn", "rhel", "almalinux", "openeuler", "rocky",
},
"debian": {"debian", "ubuntu", "raspbian", "linuxmint"},
"suse": {"suse", "sles", "opensuse", "opensuse-leap", "opensuse-tumbleweed"},
"suse": {"suse", "sles", "opensuse"},
}

var platformToFamilyMap map[string]string
Expand Down Expand Up @@ -99,9 +99,9 @@ func getOSInfo(baseDir string) (*types.OSInfo, error) {
}

func getOSRelease(baseDir string) (*types.OSInfo, error) {
lsbRel, _ := ioutil.ReadFile(filepath.Join(baseDir, lsbRelease))
lsbRel, _ := os.ReadFile(filepath.Join(baseDir, lsbRelease))

osRel, err := ioutil.ReadFile(filepath.Join(baseDir, osRelease))
osRel, err := os.ReadFile(filepath.Join(baseDir, osRelease))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -150,21 +150,15 @@ func parseOSRelease(content []byte) (*types.OSInfo, error) {
func makeOSInfo(osRelease map[string]string) (*types.OSInfo, error) {
os := &types.OSInfo{
Type: "linux",
Platform: osRelease["ID"],
Name: osRelease["NAME"],
Version: osRelease["VERSION"],
Platform: firstOf(osRelease, "ID", "DISTRIB_ID"),
Name: firstOf(osRelease, "NAME", "PRETTY_NAME"),
Version: firstOf(osRelease, "VERSION", "VERSION_ID", "DISTRIB_RELEASE"),
Build: osRelease["BUILD_ID"],
Codename: osRelease["VERSION_CODENAME"],
}

if os.Version == "" {
// Fallback to VERSION_ID if VERSION is empty.
os.Version = osRelease["VERSION_ID"]
Codename: firstOf(osRelease, "VERSION_CODENAME", "DISTRIB_CODENAME"),
}

if os.Codename == "" {
// Some OSes uses their own CODENAME keys (e.g UBUNTU_CODENAME) or we
// can get the DISTRIB_CODENAME value from the lsb-release data.
// Some OSes use their own CODENAME keys (e.g UBUNTU_CODENAME).
for k, v := range osRelease {
if strings.Contains(k, "CODENAME") {
os.Codename = v
Expand All @@ -174,10 +168,19 @@ func makeOSInfo(osRelease map[string]string) (*types.OSInfo, error) {
}

if os.Platform == "" {
// Fallback to the first word of the NAME field.
parts := strings.SplitN(os.Name, " ", 2)
if len(parts) > 0 {
os.Platform = strings.ToLower(parts[0])
// Fallback to the first word of the Name field.
os.Platform, _, _ = strings.Cut(os.Name, " ")
}

os.Family = linuxFamily(os.Platform)
if os.Family == "" {
// ID_LIKE is a space-separated list of OS identifiers that this
// OS is similar to. Use this to figure out the Linux family.
for _, id := range strings.Fields(osRelease["ID_LIKE"]) {
os.Family = linuxFamily(id)
if os.Family != "" {
break
}
}
}

Expand All @@ -200,7 +203,6 @@ func makeOSInfo(osRelease map[string]string) (*types.OSInfo, error) {
}
}

os.Family = platformToFamilyMap[strings.ToLower(os.Platform)]
return os, nil
}

Expand Down Expand Up @@ -231,7 +233,7 @@ func findDistribRelease(baseDir string) (*types.OSInfo, error) {
}

func getDistribRelease(file string) (*types.OSInfo, error) {
data, err := ioutil.ReadFile(file)
data, err := os.ReadFile(file)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -277,6 +279,40 @@ func parseDistribRelease(platform string, content []byte) (*types.OSInfo, error)
}
}

os.Family = platformToFamilyMap[strings.ToLower(os.Platform)]
os.Family = linuxFamily(os.Platform)
return os, nil
}

// firstOf returns the first non-empty value found in the map while
// iterating over keys.
func firstOf(kv map[string]string, keys ...string) string {
for _, key := range keys {
if v := kv[key]; v != "" {
return v
}
}
return ""
}

// linuxFamily returns the linux distribution family associated to the OS platform.
// If there is no family associated then it returns an empty string.
func linuxFamily(platform string) string {
if platform == "" {
return ""
}

platform = strings.ToLower(platform)

// First try a direct lookup.
if family, found := platformToFamilyMap[platform]; found {
return family
}

// Try prefix matching (e.g. opensuse matches opensuse-tumpleweed).
for platformPrefix, family := range platformToFamilyMap {
if strings.HasPrefix(platform, platformPrefix) {
return family
}
}
return ""
}
30 changes: 30 additions & 0 deletions providers/linux/os_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ func TestOperatingSystem(t *testing.T) {
}, *os)
t.Logf("%#v", os)
})
t.Run("archlinux", func(t *testing.T) {
os, err := getOSInfo("testdata/archlinux")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, types.OSInfo{
Type: "linux",
Family: "arch",
Platform: "archarm",
Name: "Arch Linux ARM",
Build: "rolling",
}, *os)
t.Logf("%#v", os)
})
t.Run("centos6", func(t *testing.T) {
os, err := getOSInfo("testdata/centos6")
if err != nil {
Expand Down Expand Up @@ -181,6 +195,22 @@ func TestOperatingSystem(t *testing.T) {
}, *os)
t.Logf("%#v", os)
})
t.Run("manjaro23", func(t *testing.T) {
os, err := getOSInfo("testdata/manjaro23")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, types.OSInfo{
Type: "linux",
Family: "arch",
Platform: "manjaro-arm",
Name: "Manjaro ARM",
Version: "23.02",
Major: 23,
Minor: 2,
}, *os)
t.Logf("%#v", os)
})
t.Run("redhat7", func(t *testing.T) {
os, err := getOSInfo("testdata/redhat7")
if err != nil {
Expand Down
Empty file.
1 change: 1 addition & 0 deletions providers/linux/testdata/archlinux/etc/os-release
11 changes: 11 additions & 0 deletions providers/linux/testdata/archlinux/usr/lib/os-release
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
NAME="Arch Linux ARM"
PRETTY_NAME="Arch Linux ARM"
ID=archarm
ID_LIKE=arch
BUILD_ID=rolling
ANSI_COLOR="38;2;23;147;209"
HOME_URL="https://archlinuxarm.org/"
DOCUMENTATION_URL="https://archlinuxarm.org/wiki"
SUPPORT_URL="https://archlinuxarm.org/forum"
BUG_REPORT_URL="https://github.com/archlinuxarm/PKGBUILDs/issues"
LOGO=archlinux-logo
Empty file.
4 changes: 4 additions & 0 deletions providers/linux/testdata/manjaro23/etc/lsb-release
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DISTRIB_ID=Manjaro-ARM
DISTRIB_RELEASE=23.02
DISTRIB_CODENAME=
DISTRIB_DESCRIPTION="Manjaro ARM Linux"
1 change: 1 addition & 0 deletions providers/linux/testdata/manjaro23/etc/os-release
8 changes: 8 additions & 0 deletions providers/linux/testdata/manjaro23/usr/lib/os-release
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
NAME="Manjaro ARM"
ID="manjaro-arm"
ID_LIKE="manjaro arch"
PRETTY_NAME="Manjaro ARM"
ANSI_COLOR="1;32"
HOME_URL="https://www.manjaro.org/"
SUPPORT_URL="https://forum.manjaro.org/c/arm/"
LOGO=manjarolinux

0 comments on commit 9ea2eba

Please sign in to comment.