From 36212cea3e259c2871e2624cfec1a4438e340ce5 Mon Sep 17 00:00:00 2001 From: Peter Bourgon Date: Mon, 29 Jun 2015 11:41:51 +0200 Subject: [PATCH] Fix probe/host for Darwin Also, add explicit GOOS=darwin target for circle. --- circle.yml | 3 +- probe/host/reporter.go | 31 ++------------- probe/host/reporter_test.go | 67 +++++++++++---------------------- probe/host/system_darwin.go | 49 ++++++++++++++++++++++-- probe/host/system_linux.go | 34 ++++++++++++++--- probe/host/system_linux_test.go | 40 ++++++++++++++++++++ probe/host/system_test.go | 38 +++++++++++++++++++ 7 files changed, 180 insertions(+), 82 deletions(-) create mode 100644 probe/host/system_linux_test.go create mode 100644 probe/host/system_test.go diff --git a/circle.yml b/circle.yml index c53c034b3..68c96fecb 100644 --- a/circle.yml +++ b/circle.yml @@ -37,7 +37,8 @@ test: - cd $SRCDIR; ./bin/lint . - cd $SRCDIR; make client-test - cd $SRCDIR; make static - - cd $SRCDIR; make + - cd $SRCDIR; rm -f app/app probe/probe; GOOS=darwin make + - cd $SRCDIR; rm -f app/app probe/probe; GOOS=linux make - cd $SRCDIR; ./bin/test -slow - cd $SRCDIR/experimental; make post: diff --git a/probe/host/reporter.go b/probe/host/reporter.go index e071dbfd7..ddb5a7788 100644 --- a/probe/host/reporter.go +++ b/probe/host/reporter.go @@ -1,11 +1,8 @@ package host import ( - "fmt" - "io/ioutil" "net" "runtime" - "strconv" "strings" "time" @@ -34,7 +31,6 @@ const ( var ( InterfaceAddrs = net.InterfaceAddrs Now = func() string { return time.Now().UTC().Format(time.RFC3339Nano) } - ReadFile = ioutil.ReadFile ) type reporter struct { @@ -51,27 +47,6 @@ func NewReporter(hostID, hostName string) tag.Reporter { } } -func getUptime() (time.Duration, error) { - var result time.Duration - - buf, err := ReadFile(ProcUptime) - if err != nil { - return result, err - } - - fields := strings.Fields(string(buf)) - if len(fields) != 2 { - return result, fmt.Errorf("invalid format: %s", string(buf)) - } - - uptime, err := strconv.ParseFloat(fields[0], 64) - if err != nil { - return result, err - } - - return time.Duration(uptime) * time.Second, nil -} - func (r *reporter) Report() (report.Report, error) { var ( rep = report.MakeReport() @@ -89,12 +64,12 @@ func (r *reporter) Report() (report.Report, error) { } } - uptime, err := getUptime() + uptime, err := GetUptime() if err != nil { return rep, err } - kernel, err := getKernelVersion() + kernel, err := GetKernelVersion() if err != nil { return rep, err } @@ -104,7 +79,7 @@ func (r *reporter) Report() (report.Report, error) { HostName: r.hostName, LocalNetworks: strings.Join(localCIDRs, " "), OS: runtime.GOOS, - Load: getLoad(), + Load: GetLoad(), KernelVersion: kernel, Uptime: uptime.String(), } diff --git a/probe/host/reporter_test.go b/probe/host/reporter_test.go index c8a6c03ba..51faaaac5 100644 --- a/probe/host/reporter_test.go +++ b/probe/host/reporter_test.go @@ -4,8 +4,8 @@ import ( "net" "reflect" "runtime" - "syscall" "testing" + "time" "github.com/weaveworks/scope/probe/host" "github.com/weaveworks/scope/report" @@ -13,13 +13,10 @@ import ( ) const ( - procLoad = "0.59 0.36 0.29 1/200 12187" - procUptime = "1004143.23 1263220.30" - release = "release" - version = "version" - + release = "release" + version = "version" network = "192.168.0.0/16" - hostid = "hostid" + hostID = "hostid" now = "now" hostname = "hostname" load = "0.59 0.36 0.29" @@ -27,48 +24,29 @@ const ( kernel = "release version" ) -func string2c(s string) [65]int8 { - var result [65]int8 - for i, c := range s { - result[i] = int8(c) - } - return result -} - func TestReporter(t *testing.T) { - oldInterfaceAddrs, oldNow, oldReadFile, oldUname := host.InterfaceAddrs, host.Now, host.ReadFile, host.Uname + var ( + oldGetKernelVersion = host.GetKernelVersion + oldGetLoad = host.GetLoad + oldGetUptime = host.GetUptime + oldInterfaceAddrs = host.InterfaceAddrs + oldNow = host.Now + ) defer func() { - host.InterfaceAddrs, host.Now, host.ReadFile, host.Uname = oldInterfaceAddrs, oldNow, oldReadFile, oldUname + host.GetKernelVersion = oldGetKernelVersion + host.GetLoad = oldGetLoad + host.GetUptime = oldGetUptime + host.InterfaceAddrs = oldInterfaceAddrs + host.Now = oldNow }() - - host.InterfaceAddrs = func() ([]net.Addr, error) { - _, ipnet, _ := net.ParseCIDR(network) - return []net.Addr{ipnet}, nil - } - + host.GetKernelVersion = func() (string, error) { return release + " " + version, nil } + host.GetLoad = func() string { return load } + host.GetUptime = func() (time.Duration, error) { return time.ParseDuration(uptime) } host.Now = func() string { return now } + host.InterfaceAddrs = func() ([]net.Addr, error) { _, ipnet, _ := net.ParseCIDR(network); return []net.Addr{ipnet}, nil } - host.ReadFile = func(filename string) ([]byte, error) { - switch filename { - case host.ProcUptime: - return []byte(procUptime), nil - case host.ProcLoad: - return []byte(procLoad), nil - default: - panic(filename) - } - } - - host.Uname = func(uts *syscall.Utsname) error { - uts.Release = string2c(release) - uts.Version = string2c(version) - return nil - } - - r := host.NewReporter(hostid, hostname) - have, _ := r.Report() want := report.MakeReport() - want.Host.NodeMetadatas[report.MakeHostNodeID(hostid)] = report.NodeMetadata{ + want.Host.NodeMetadatas[report.MakeHostNodeID(hostID)] = report.NodeMetadata{ host.Timestamp: now, host.HostName: hostname, host.LocalNetworks: network, @@ -77,7 +55,8 @@ func TestReporter(t *testing.T) { host.Uptime: uptime, host.KernelVersion: kernel, } - + r := host.NewReporter(hostID, hostname) + have, _ := r.Report() if !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) } diff --git a/probe/host/system_darwin.go b/probe/host/system_darwin.go index bf8c90fe1..f985a59ff 100644 --- a/probe/host/system_darwin.go +++ b/probe/host/system_darwin.go @@ -4,15 +4,31 @@ import ( "fmt" "os/exec" "regexp" + "strconv" + "time" ) -var loadRe = regexp.MustCompile(`load average\: ([0-9\.]+), ([0-9\.]+), ([0-9\.]+)`) +var ( + unameRe = regexp.MustCompile(`Darwin Kernel Version ([0-9\.]+)\:`) + loadRe = regexp.MustCompile(`load averages: ([0-9\.]+) ([0-9\.]+) ([0-9\.]+)`) + uptimeRe = regexp.MustCompile(`up ([0-9]+) day[s]*,[ ]+([0-9]+)\:([0-9][0-9])`) +) -func getKernelVersion() (string, error) { - return "", nil +// GetKernelVersion returns the kernel version as reported by uname. +var GetKernelVersion = func() (string, error) { + out, err := exec.Command("uname", "-v").CombinedOutput() + if err != nil { + return "Darwin unknown", err + } + matches := unameRe.FindAllStringSubmatch(string(out), -1) + if matches == nil || len(matches) < 1 || len(matches[0]) < 1 { + return "Darwin unknown", nil + } + return fmt.Sprintf("Darwin %s", matches[0][1]), nil } -func getLoad() string { +// GetLoad returns the current load averages in standard form. +var GetLoad = func() string { out, err := exec.Command("w").CombinedOutput() if err != nil { return "unknown" @@ -23,3 +39,28 @@ func getLoad() string { } return fmt.Sprintf("%s %s %s", matches[0][1], matches[0][2], matches[0][3]) } + +// GetUptime returns the uptime of the host. +var GetUptime = func() (time.Duration, error) { + out, err := exec.Command("w").CombinedOutput() + if err != nil { + return 0, err + } + matches := uptimeRe.FindAllStringSubmatch(string(out), -1) + if matches == nil || len(matches) < 1 || len(matches[0]) < 4 { + return 0, err + } + d, err := strconv.Atoi(matches[0][1]) + if err != nil { + return 0, err + } + h, err := strconv.Atoi(matches[0][2]) + if err != nil { + return 0, err + } + m, err := strconv.Atoi(matches[0][3]) + if err != nil { + return 0, err + } + return (time.Duration(d) * 24 * time.Hour) + (time.Duration(h) * time.Hour) + (time.Duration(m) * time.Minute), nil +} diff --git a/probe/host/system_linux.go b/probe/host/system_linux.go index 1d403d674..4f7155aac 100644 --- a/probe/host/system_linux.go +++ b/probe/host/system_linux.go @@ -2,12 +2,14 @@ package host import ( "fmt" + "io/ioutil" "strconv" "strings" "syscall" + "time" ) -// Uname exported for testing +// Uname is swappable for mocking in tests. var Uname = syscall.Uname func charsToString(ca [65]int8) string { @@ -22,16 +24,18 @@ func charsToString(ca [65]int8) string { return string(s[0:lens]) } -func getKernelVersion() (string, error) { +// GetKernelVersion returns the kernel version as reported by uname. +var GetKernelVersion = func() (string, error) { var utsname syscall.Utsname if err := Uname(&utsname); err != nil { - return "", err + return "unknown", err } return fmt.Sprintf("%s %s", charsToString(utsname.Release), charsToString(utsname.Version)), nil } -func getLoad() string { - buf, err := ReadFile(ProcLoad) +// GetLoad returns the current load averages in standard form. +var GetLoad = func() string { + buf, err := ioutil.ReadFile("/proc/loadavg") if err != nil { return "unknown" } @@ -53,3 +57,23 @@ func getLoad() string { } return fmt.Sprintf("%.2f %.2f %.2f", one, five, fifteen) } + +// GetUptime returns the uptime of the host. +var GetUptime = func() (time.Duration, error) { + buf, err := ioutil.ReadFile("/proc/uptime") + if err != nil { + return 0, err + } + + fields := strings.Fields(string(buf)) + if len(fields) != 2 { + return 0, fmt.Errorf("invalid format: %s", string(buf)) + } + + uptime, err := strconv.ParseFloat(fields[0], 64) + if err != nil { + return 0, err + } + + return time.Duration(uptime) * time.Second, nil +} diff --git a/probe/host/system_linux_test.go b/probe/host/system_linux_test.go new file mode 100644 index 000000000..91f529b00 --- /dev/null +++ b/probe/host/system_linux_test.go @@ -0,0 +1,40 @@ +package host_test + +import ( + "fmt" + "syscall" + "testing" + + "github.com/weaveworks/scope/probe/host" +) + +func TestUname(t *testing.T) { + oldUname := host.Uname + defer func() { host.Uname = oldUname }() + + const ( + release = "rls" + version = "ver" + ) + host.Uname = func(uts *syscall.Utsname) error { + uts.Release = string2c(release) + uts.Version = string2c(version) + return nil + } + + have, err := host.GetKernelVersion() + if err != nil { + t.Fatal(err) + } + if want := fmt.Sprintf("%s %s", release, version); want != have { + t.Errorf("want %q, have %q", want, have) + } +} + +func string2c(s string) [65]int8 { + var result [65]int8 + for i, c := range s { + result[i] = int8(c) + } + return result +} diff --git a/probe/host/system_test.go b/probe/host/system_test.go new file mode 100644 index 000000000..361490b88 --- /dev/null +++ b/probe/host/system_test.go @@ -0,0 +1,38 @@ +package host_test + +import ( + "strings" + "testing" + + "github.com/weaveworks/scope/probe/host" +) + +func TestGetKernelVersion(t *testing.T) { + have, err := host.GetKernelVersion() + if err != nil { + t.Fatal(err) + } + if strings.Contains(have, "unknown") { + t.Fatal(have) + } + t.Log(have) +} + +func TestGetLoad(t *testing.T) { + have := host.GetLoad() + if strings.Contains(have, "unknown") { + t.Fatal(have) + } + t.Log(have) +} + +func TestGetUptime(t *testing.T) { + have, err := host.GetUptime() + if err != nil { + t.Fatal(err) + } + if have == 0 { + t.Fatal(have) + } + t.Log(have.String()) +}