Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sys: add host-info endpoint #7330

Merged
merged 20 commits into from
Oct 3, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/hashicorp/hcl v1.0.0
github.com/hashicorp/vault/sdk v0.1.14-0.20190805214312-16112a336457
github.com/mitchellh/mapstructure v1.1.2
github.com/shirou/gopsutil v0.0.0-20190731134726-d80c43f9c984 // indirect
calvn marked this conversation as resolved.
Show resolved Hide resolved
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
gopkg.in/square/go-jose.v2 v2.3.1
Expand Down
7 changes: 7 additions & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
Expand All @@ -10,6 +11,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
Expand Down Expand Up @@ -66,6 +68,9 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/shirou/gopsutil v0.0.0-20190731134726-d80c43f9c984 h1:wsZAb4P8F7uQSwsnxE1gk9AHCcc5U0wvyDzcLwFY0Eo=
github.com/shirou/gopsutil v0.0.0-20190731134726-d80c43f9c984/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
Expand All @@ -91,8 +96,10 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU=
Expand Down
58 changes: 58 additions & 0 deletions api/sys_hostinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package api

import (
"context"
"errors"
"time"

"github.com/mitchellh/mapstructure"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/host"
"github.com/shirou/gopsutil/mem"
)

func (c *Sys) HostInfo() (*HostInfoResponse, error) {
r := c.c.NewRequest("GET", "/v1/sys/host-info")

ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
resp, err := c.c.RawRequestWithContext(ctx, r)
if err != nil {
return nil, err
}
defer resp.Body.Close()

secret, err := ParseSecret(resp.Body)
if err != nil {
return nil, err
}
if secret == nil || secret.Data == nil {
return nil, errors.New("data from server response is empty")
}

// Parse timestamp separately since WeakDecode can't handle this field.
timestampRaw := secret.Data["timestamp"].(string)
timestamp, err := time.Parse(time.RFC3339, timestampRaw)
if err != nil {
return nil, err
}
delete(secret.Data, "timestamp")

var result HostInfoResponse
err = mapstructure.WeakDecode(secret.Data, &result)
if err != nil {
return nil, err
}
result.Timestamp = timestamp

return &result, err
}

type HostInfoResponse struct {
calvn marked this conversation as resolved.
Show resolved Hide resolved
Timestamp time.Time `json:"timestamp"`
CPU []cpu.InfoStat `json:"cpu"`
Disk []*disk.UsageStat `json:"disk"`
Host *host.InfoStat `json:"host"`
Memory *mem.VirtualMemoryStat `json:"memory"`
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ require (
github.com/ryanuber/columnize v2.1.0+incompatible
github.com/ryanuber/go-glob v1.0.0
github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec
github.com/shirou/gopsutil v0.0.0-20190731134726-d80c43f9c984
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94 // indirect
github.com/stretchr/testify v1.3.0
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ github.com/SAP/go-hdb v0.14.1 h1:hkw4ozGZ/i4eak7ZuGkY5e0hxiXFdNUBNhr4AvZVNFE=
github.com/SAP/go-hdb v0.14.1/go.mod h1:7fdQLVC2lER3urZLjZCm0AuMQfApof92n3aylBPEkMo=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
Expand Down Expand Up @@ -157,6 +158,7 @@ github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
Expand Down Expand Up @@ -508,6 +510,11 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shirou/gopsutil v0.0.0-20190731134726-d80c43f9c984 h1:wsZAb4P8F7uQSwsnxE1gk9AHCcc5U0wvyDzcLwFY0Eo=
github.com/shirou/gopsutil v0.0.0-20190731134726-d80c43f9c984/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM=
github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/sirupsen/logrus v1.0.5 h1:8c8b5uO0zS4X6RPl/sd1ENwSkIc0/H2PaHxE3udaE8I=
Expand Down
1 change: 1 addition & 0 deletions http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func Handler(props *vault.HandlerProperties) http.Handler {

// Create the muxer to handle the actual endpoints
mux := http.NewServeMux()
mux.Handle("/v1/sys/host-info", handleLogical(core))
mux.Handle("/v1/sys/init", handleSysInit(core))
mux.Handle("/v1/sys/seal-status", handleSysSealStatus(core))
mux.Handle("/v1/sys/seal", handleSysSeal(core))
Expand Down
7 changes: 7 additions & 0 deletions http/logical.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,13 @@ func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool) http.H
// success.
resp, ok, needsForward := request(core, w, r, req)
if needsForward {
// Do not forward these specific requests since they are only
// applicable to the local node.
switch req.Path {
case "sys/host-info":
return
}

if origBody != nil {
r.Body = origBody
}
Expand Down
49 changes: 49 additions & 0 deletions http/sys_hostinfo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package http

import (
"testing"

"github.com/hashicorp/vault/vault"
)

func TestSysHostInfo(t *testing.T) {
cluster := vault.NewTestCluster(t, &vault.CoreConfig{}, &vault.TestClusterOptions{
HandlerFunc: Handler,
})
cluster.Start()
defer cluster.Cleanup()
cores := cluster.Cores

vault.TestWaitActive(t, cores[0].Core)

// Query against the active node, should get host information back
info, err := cores[0].Client.Sys().HostInfo()
if err != nil {
t.Fatal(err)
}
if info == nil {
t.Fatal("expected non-nil HostInfo")
}

if info.Timestamp.IsZero() {
t.Fatalf("expected a valid timestamp")
}
if info.CPU == nil {
t.Fatal("expected CPU info")
}
if info.Disk == nil {
t.Fatal("expected disk info")
}
if info.Host == nil {
t.Fatal("expected host info")
}
if info.Memory == nil {
t.Fatal("expected memory info")
}

// Query against the standby, should error
info, err = cores[1].Client.Sys().HostInfo()
if err == nil || info != nil {
t.Fatalf("expected error on standby node, HostInfo: %v", info)
}
}
80 changes: 80 additions & 0 deletions vault/hostinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package vault

import (
"time"

"github.com/hashicorp/go-multierror"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/host"
"github.com/shirou/gopsutil/mem"
)

// HostInfo holds all the information that gets captured on the host.
type HostInfo struct {
Timestamp time.Time `json:"timestamp"`
CPU []cpu.InfoStat `json:"cpu"`
Disk []*disk.UsageStat `json:"disk"`
Host *host.InfoStat `json:"host"`
Memory *mem.VirtualMemoryStat `json:"memory"`
}

// HostInfoError is a typed error for more convenient error checking.
type HostInfoError struct {
Err error
}

func (e *HostInfoError) WrappedErrors() []error {
return []error{e.Err}
}

func (e *HostInfoError) Error() string {
return e.Err.Error()
}

// CollectHostInfo returns information on the host, which includes general
// host status, CPU, memory, and disk utilization.
//
// The function does a best-effort capture on the most information possible,
// continuing on capture errors encountered and appending them to a resulting
// multierror.Error that gets returned at the end.
func (c *Core) CollectHostInfo() (*HostInfo, error) {
var retErr *multierror.Error
info := &HostInfo{Timestamp: time.Now().UTC()}

if h, err := host.Info(); err != nil {
retErr = multierror.Append(retErr, &HostInfoError{err})
} else {
info.Host = h
}

if v, err := mem.VirtualMemory(); err != nil {
retErr = multierror.Append(retErr, &HostInfoError{err})
} else {
info.Memory = v
}

parts, err := disk.Partitions(false)
if err != nil {
retErr = multierror.Append(retErr, &HostInfoError{err})
} else {
var usage []*disk.UsageStat
for _, part := range parts {
u, err := disk.Usage(part.Mountpoint)
if err != nil {
retErr = multierror.Append(retErr, &HostInfoError{err})
}
usage = append(usage, u)
calvn marked this conversation as resolved.
Show resolved Hide resolved

}
info.Disk = usage
}

if c, err := cpu.Info(); err != nil {
calvn marked this conversation as resolved.
Show resolved Hide resolved
retErr = multierror.Append(retErr, &HostInfoError{err})
} else {
info.CPU = c
}

return info, retErr.ErrorOrNil()
calvn marked this conversation as resolved.
Show resolved Hide resolved
}
29 changes: 29 additions & 0 deletions vault/hostinfo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package vault

import (
"testing"
)

func TestCollectHostInfo(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)

info, err := c.CollectHostInfo()
if err != nil {
t.Fatal(err)
}
if info.Timestamp.IsZero() {
t.Fatal("expected non-zero Timestamp")
}
if info.CPU == nil {
t.Fatal("expected non-nil CPU value")
}
if info.Disk == nil {
t.Fatal("expected non-nil Disk value")
}
if info.Host == nil {
t.Fatal("expected non-nil Host value")
}
if info.Memory == nil {
t.Fatal("expected non-nil Memory value")
}
}
Loading