The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3831
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Closes #3819
From fc6528884afba76fb96043f376d58f4f0271e991 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Wed, 20 Sep 2017 15:18:06 +0200 Subject: [PATCH 1/6] shared/util: extract helper to get uname Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- lxd/api_1.0.go | 42 ++++--------------------------- shared/osarch/architectures_linux.go | 17 ++++--------- shared/util_linux.go | 48 ++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 49 deletions(-) diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go index 7b68acdb3..0dd41bfaa 100644 --- a/lxd/api_1.0.go +++ b/lxd/api_1.0.go @@ -6,7 +6,6 @@ import ( "net/http" "os" "reflect" - "syscall" "gopkg.in/lxc/go-lxc.v2" @@ -140,42 +139,11 @@ func api10Get(d *Daemon, r *http.Request) Response { srv.Auth = "trusted" - /* - * Based on: https://groups.google.com/forum/#!topic/golang-nuts/Jel8Bb-YwX8 - * there is really no better way to do this, which is - * unfortunate. Also, we ditch the more accepted CharsToString - * version in that thread, since it doesn't seem as portable, - * viz. github issue #206. - */ - uname := syscall.Utsname{} - if err := syscall.Uname(&uname); err != nil { + uname, err := shared.Uname() + if err != nil { return InternalError(err) } - kernel := "" - for _, c := range uname.Sysname { - if c == 0 { - break - } - kernel += string(byte(c)) - } - - kernelVersion := "" - for _, c := range uname.Release { - if c == 0 { - break - } - kernelVersion += string(byte(c)) - } - - kernelArchitecture := "" - for _, c := range uname.Machine { - if c == 0 { - break - } - kernelArchitecture += string(byte(c)) - } - addresses, err := util.ListenAddresses(daemonConfig["core.https_address"].Get()) if err != nil { return InternalError(err) @@ -208,9 +176,9 @@ func api10Get(d *Daemon, r *http.Request) Response { CertificateFingerprint: certificateFingerprint, Driver: "lxc", DriverVersion: lxc.Version(), - Kernel: kernel, - KernelArchitecture: kernelArchitecture, - KernelVersion: kernelVersion, + Kernel: uname.Sysname, + KernelArchitecture: uname.Machine, + KernelVersion: uname.Release, Server: "lxd", ServerPid: os.Getpid(), ServerVersion: version.Version} diff --git a/shared/osarch/architectures_linux.go b/shared/osarch/architectures_linux.go index c95b58a73..a87e795a8 100644 --- a/shared/osarch/architectures_linux.go +++ b/shared/osarch/architectures_linux.go @@ -3,22 +3,15 @@ package osarch import ( - "syscall" + "github.com/lxc/lxd/shared" ) +// ArchitectureGetLocal returns the local hardware architecture func ArchitectureGetLocal() (string, error) { - uname := syscall.Utsname{} - if err := syscall.Uname(&uname); err != nil { + uname, err := shared.Uname() + if err != nil { return ArchitectureDefault, err } - architectureName := "" - for _, c := range uname.Machine { - if c == 0 { - break - } - architectureName += string(byte(c)) - } - - return architectureName, nil + return uname.Machine, nil } diff --git a/shared/util_linux.go b/shared/util_linux.go index 77daf1051..369898eb1 100644 --- a/shared/util_linux.go +++ b/shared/util_linux.go @@ -769,3 +769,51 @@ func GetErrno(err error) (errno error, iserrno bool) { return nil, false } + +// Utsname returns the same info as syscall.Utsname, as strings +type Utsname struct { + Sysname string + Nodename string + Release string + Version string + Machine string + Domainname string +} + +// Uname returns Utsname as strings +func Uname() (*Utsname, error) { + /* + * Based on: https://groups.google.com/forum/#!topic/golang-nuts/Jel8Bb-YwX8 + * there is really no better way to do this, which is + * unfortunate. Also, we ditch the more accepted CharsToString + * version in that thread, since it doesn't seem as portable, + * viz. github issue #206. + */ + + uname := syscall.Utsname{} + err := syscall.Uname(&uname) + if err != nil { + return nil, err + } + + return &Utsname{ + Sysname: intArrayToString(uname.Sysname), + Nodename: intArrayToString(uname.Nodename), + Release: intArrayToString(uname.Release), + Version: intArrayToString(uname.Version), + Machine: intArrayToString(uname.Machine), + Domainname: intArrayToString(uname.Domainname), + }, nil +} + +func intArrayToString(arr [65]int8) string { + s := "" + for _, c := range arr { + if c == 0 { + break + } + s += string(byte(c)) + } + + return s +} From 0002355d93d88880ad708cb7f0cc84a82f573fe8 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Thu, 21 Sep 2017 10:48:22 +0200 Subject: [PATCH 2/6] shared/util: add helper to create tempfiles Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- shared/testhelpers/tempfile.go | 19 +++++++++++++++++++ shared/util.go | 12 ++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 shared/testhelpers/tempfile.go diff --git a/shared/testhelpers/tempfile.go b/shared/testhelpers/tempfile.go new file mode 100644 index 000000000..b1abebe7e --- /dev/null +++ b/shared/testhelpers/tempfile.go @@ -0,0 +1,19 @@ +package testhelpers + +import ( + "os" + + "github.com/stretchr/testify/suite" + + "github.com/lxc/lxd/shared" +) + +// WriteTempFile writes content to a temporary file. +func WriteTempFile(s suite.Suite, dir string, prefix string, content string) (string, func()) { + filename, err := shared.WriteTempFile(dir, prefix, content) + if err != nil { + s.T().Fatalf("failed to create temporary file: %v", err) + } + + return filename, func() { os.Remove(filename) } +} diff --git a/shared/util.go b/shared/util.go index e49e8d058..260b32c8a 100644 --- a/shared/util.go +++ b/shared/util.go @@ -857,3 +857,15 @@ func Round(x float64) int64 { return int64(math.Floor(x + 0.5)) } + +// WriteTempFile creates a temp file with the specified content +func WriteTempFile(dir string, prefix string, content string) (string, error) { + f, err := ioutil.TempFile(dir, prefix) + if err != nil { + return "", err + } + defer f.Close() + + _, err = f.WriteString(content) + return f.Name(), err +} From bb5ecf8fb53228ad93878e3e44e89aa95a2159f1 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Wed, 20 Sep 2017 20:35:12 +0200 Subject: [PATCH 3/6] shared/osarch: add missing architecture aliases Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- shared/osarch/architectures.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/osarch/architectures.go b/shared/osarch/architectures.go index 265d7cb02..e35b12772 100644 --- a/shared/osarch/architectures.go +++ b/shared/osarch/architectures.go @@ -28,12 +28,12 @@ var architectureNames = map[int]string{ } var architectureAliases = map[int][]string{ - ARCH_32BIT_INTEL_X86: {"i386"}, + ARCH_32BIT_INTEL_X86: {"i386", "386"}, ARCH_64BIT_INTEL_X86: {"amd64"}, - ARCH_32BIT_ARMV7_LITTLE_ENDIAN: {"armel", "armhf"}, + ARCH_32BIT_ARMV7_LITTLE_ENDIAN: {"armel", "armhf", "arm"}, ARCH_64BIT_ARMV8_LITTLE_ENDIAN: {"arm64"}, ARCH_32BIT_POWERPC_BIG_ENDIAN: {"powerpc"}, - ARCH_64BIT_POWERPC_BIG_ENDIAN: {"powerpc64"}, + ARCH_64BIT_POWERPC_BIG_ENDIAN: {"powerpc64", "ppc64"}, ARCH_64BIT_POWERPC_LITTLE_ENDIAN: {"ppc64el"}, } From dbdb5db247f112aa2863c7314d82d4b4f4b01bc6 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Thu, 21 Sep 2017 09:45:25 +0200 Subject: [PATCH 4/6] shared/osarch: add function for parsing /etc/os-release Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- shared/osarch/release.go | 42 ++++++++++++++++++++ shared/osarch/release_test.go | 90 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 shared/osarch/release.go create mode 100644 shared/osarch/release_test.go diff --git a/shared/osarch/release.go b/shared/osarch/release.go new file mode 100644 index 000000000..22ba8d09b --- /dev/null +++ b/shared/osarch/release.go @@ -0,0 +1,42 @@ +package osarch + +import ( + "fmt" + "io/ioutil" + "os" + "strings" +) + +// GetLSBRelease returns a map with Linux distribution information +func GetLSBRelease() (map[string]string, error) { + osRelease, err := getLSBRelease("/etc/os-release") + if os.IsNotExist(err) { + return getLSBRelease("/usr/lib/os-release") + } + return osRelease, err +} + +func getLSBRelease(filename string) (map[string]string, error) { + osRelease := make(map[string]string) + + data, err := ioutil.ReadFile(filename) + if err != nil { + return osRelease, err + } + for i, line := range strings.Split(string(data), "\n") { + if len(line) == 0 { + continue + } + if strings.HasPrefix(line, "#") { + continue + } + + tokens := strings.SplitN(line, "=", 2) + if len(tokens) != 2 { + return osRelease, fmt.Errorf("%s: invalid format on line %d", filename, i+1) + } + osRelease[tokens[0]] = strings.Trim(tokens[1], `'"`) + } + + return osRelease, nil +} diff --git a/shared/osarch/release_test.go b/shared/osarch/release_test.go new file mode 100644 index 000000000..55b830849 --- /dev/null +++ b/shared/osarch/release_test.go @@ -0,0 +1,90 @@ +package osarch + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/lxc/lxd/shared/testhelpers" +) + +type releaseTestSuite struct { + suite.Suite +} + +func TestReleaseTestSuite(t *testing.T) { + suite.Run(t, new(releaseTestSuite)) +} + +func (s *releaseTestSuite) TestGetLSBRelease() { + content := `NAME="Ubuntu" +ID="ubuntu" +VERSION_ID="16.04" +` + filename, cleanup := testhelpers.WriteTempFile(s.Suite, "", "os-release", content) + defer cleanup() + + lsbRelease, err := getLSBRelease(filename) + s.Nil(err) + s.Equal( + map[string]string{ + "NAME": "Ubuntu", + "ID": "ubuntu", + "VERSION_ID": "16.04", + }, lsbRelease) +} + +func (s *releaseTestSuite) TestGetLSBReleaseSingleQuotes() { + content := `NAME='Ubuntu'` + filename, cleanup := testhelpers.WriteTempFile(s.Suite, "", "os-release", content) + defer cleanup() + + lsbRelease, err := getLSBRelease(filename) + s.Nil(err) + s.Equal(map[string]string{"NAME": "Ubuntu"}, lsbRelease) +} + +func (s *releaseTestSuite) TestGetLSBReleaseNoQuotes() { + content := `NAME=Ubuntu` + filename, cleanup := testhelpers.WriteTempFile(s.Suite, "", "os-release", content) + defer cleanup() + + lsbRelease, err := getLSBRelease(filename) + s.Nil(err) + s.Equal(map[string]string{"NAME": "Ubuntu"}, lsbRelease) +} + +func (s *releaseTestSuite) TestGetLSBReleaseSkipCommentsEmpty() { + content := ` +NAME="Ubuntu" + +ID="ubuntu" +# skip this line +VERSION_ID="16.04" +` + filename, cleanup := testhelpers.WriteTempFile(s.Suite, "", "os-release", content) + defer cleanup() + + lsbRelease, err := getLSBRelease(filename) + s.Nil(err) + s.Equal( + map[string]string{ + "NAME": "Ubuntu", + "ID": "ubuntu", + "VERSION_ID": "16.04", + }, lsbRelease) +} + +func (s *releaseTestSuite) TestGetLSBReleaseInvalidLine() { + content := ` +NAME="Ubuntu" +this is invalid +ID="ubuntu" +` + filename, cleanup := testhelpers.WriteTempFile(s.Suite, "", "os-release", content) + defer cleanup() + + _, err := getLSBRelease(filename) + s.EqualError(err, fmt.Sprintf("%s: invalid format on line 3", filename)) +} From 6f23fc799bef0e468a00b8d993adf51b80c83ddd Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Thu, 21 Sep 2017 09:50:40 +0200 Subject: [PATCH 5/6] shared/version: add helper to get platform-specific versions Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- shared/version/platform_linux.go | 32 ++++++++++++++++++++++++++++++++ shared/version/platform_others.go | 7 +++++++ 2 files changed, 39 insertions(+) create mode 100644 shared/version/platform_linux.go create mode 100644 shared/version/platform_others.go diff --git a/shared/version/platform_linux.go b/shared/version/platform_linux.go new file mode 100644 index 000000000..252a80bc3 --- /dev/null +++ b/shared/version/platform_linux.go @@ -0,0 +1,32 @@ +// +build linux + +package version + +import ( + "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/osarch" +) + +func getPlatformVersionStrings() []string { + versions := []string{} + + // add kernel version + uname, err := shared.Uname() + if err == nil { + versions = append(versions, uname.Release) + } + + // add distribution info + lsbRelease, err := osarch.GetLSBRelease() + if err == nil { + distroName, ok := lsbRelease["NAME"] + if ok { + versions = append(versions, distroName) + } + distroRelease, ok := lsbRelease["VERSION_ID"] + if ok { + versions = append(versions, distroRelease) + } + } + return versions +} diff --git a/shared/version/platform_others.go b/shared/version/platform_others.go new file mode 100644 index 000000000..d29148682 --- /dev/null +++ b/shared/version/platform_others.go @@ -0,0 +1,7 @@ +// +build !linux + +package version + +func getPlatformVersionStrings() []string { + return []string{} +} From da95c8ced76dab98f08c69d15a9cb04f17674ee6 Mon Sep 17 00:00:00 2001 From: Alberto Donato <alberto.don...@canonical.com> Date: Thu, 21 Sep 2017 09:56:12 +0200 Subject: [PATCH 6/6] shared/version: include OS, architecture and possibly kernel and distro info in User-Agent Signed-off-by: Alberto Donato <alberto.don...@canonical.com> --- shared/version/flex.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/shared/version/flex.go b/shared/version/flex.go index 5feeedbba..48a3537a4 100644 --- a/shared/version/flex.go +++ b/shared/version/flex.go @@ -1,10 +1,33 @@ package version +import ( + "fmt" + "runtime" + "strings" + + "github.com/lxc/lxd/shared/osarch" +) + // Version contains the LXD version number var Version = "2.18" // UserAgent contains a string suitable as a user-agent -var UserAgent = "LXD " + Version +var UserAgent = getUserAgent() // APIVersion contains the API base version. Only bumped for backward incompatible changes. var APIVersion = "1.0" + +func getUserAgent() string { + archID, err := osarch.ArchitectureId(runtime.GOARCH) + if err != nil { + panic(err) + } + arch, err := osarch.ArchitectureName(archID) + if err != nil { + panic(err) + } + + tokens := []string{strings.Title(runtime.GOOS), arch} + tokens = append(tokens, getPlatformVersionStrings()...) + return fmt.Sprintf("LXD %s (%s)", Version, strings.Join(tokens, "; ")) +}
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel