[lxc-devel] [lxd/master] Add oeprating system, architecture and possibly distribution name and version to the User-Agent
albertodonato on Github
lxc-bot at linuxcontainers.org
Thu Sep 21 09:11:17 UTC 2017
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 314 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170921/4511c1c3/attachment.bin>
-------------- next part --------------
From fc6528884afba76fb96043f376d58f4f0271e991 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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, "; "))
+}
More information about the lxc-devel
mailing list