sys: add host-info endpoint (#7330)
* sys: add host-info endpoint, add client API method * remove old commented handler * add http tests, fix bugs * query all partitions for disk usage * fix Timestamp decoding * add comments for clarification * dont append a nil entry on disk usage query error * remove HostInfo from the sdk api We can use Logical().Read(...) to query this endpoint since the payload is contained with the data object. All warnings are preserved under Secret.Warnings. * ensure that we're testing failure case against a standby node * add and use TestWaitStandby to ensure core is on standby * remove TestWaitStandby * respond with local-only error * move HostInfo into its own helper package * fix imports; use new no-forward handler * add cpu times to collection * emit clearer multierrors/warnings by collection type * add comments on HostInfo fields
This commit is contained in:
parent
a726e71e20
commit
3f1c7c86a0
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20190919081434-645ac174deeb
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
github.com/shirou/gopsutil v0.0.0-20190731134726-d80c43f9c984 // indirect
|
||||
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
|
||||
|
|
|
@ -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=
|
||||
|
@ -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=
|
||||
|
@ -67,6 +69,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=
|
||||
|
@ -92,8 +97,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=
|
||||
|
|
2
go.mod
2
go.mod
|
@ -91,6 +91,7 @@ require (
|
|||
github.com/joyent/triton-go v0.0.0-20190112182421-51ffac552869
|
||||
github.com/keybase/go-crypto v0.0.0-20190403132359-d65b6b94177f
|
||||
github.com/kr/pretty v0.1.0
|
||||
github.com/kr/pty v1.1.3 // indirect
|
||||
github.com/kr/text v0.1.0
|
||||
github.com/lib/pq v1.2.0
|
||||
github.com/mattn/go-colorable v0.1.2
|
||||
|
@ -115,6 +116,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
|
||||
|
|
7
go.sum
7
go.sum
|
@ -33,6 +33,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=
|
||||
|
@ -167,6 +168,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=
|
||||
|
@ -551,6 +553,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=
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package hostutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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. The
|
||||
// set of information captured depends on the host operating system. For more
|
||||
// information, refer to: https://github.com/shirou/gopsutil#current-status
|
||||
type HostInfo struct {
|
||||
// Timestamp returns the timestamp in UTC on the collection time.
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
// CPU returns information about the CPU such as family, model, cores, etc.
|
||||
CPU []cpu.InfoStat `json:"cpu"`
|
||||
// CPUTimes returns statistics on CPU usage represented in Jiffies.
|
||||
CPUTimes []cpu.TimesStat `json:"cpu_times"`
|
||||
// Disk returns statitics on disk usage for all accessible partitions.
|
||||
Disk []*disk.UsageStat `json:"disk"`
|
||||
// Host returns general host information such as hostname, platform, uptime,
|
||||
// kernel version, etc.
|
||||
Host *host.InfoStat `json:"host"`
|
||||
// Memory contains statistics about the memory such as total, available, and
|
||||
// used memory in number of bytes.
|
||||
Memory *mem.VirtualMemoryStat `json:"memory"`
|
||||
}
|
||||
|
||||
// HostInfoError is a typed error for more convenient error checking.
|
||||
type HostInfoError struct {
|
||||
Type string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *HostInfoError) WrappedErrors() []error {
|
||||
return []error{e.Err}
|
||||
}
|
||||
|
||||
func (e *HostInfoError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.Type, 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 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{"host", err})
|
||||
} else {
|
||||
info.Host = h
|
||||
}
|
||||
|
||||
if v, err := mem.VirtualMemory(); err != nil {
|
||||
retErr = multierror.Append(retErr, &HostInfoError{"memory", err})
|
||||
} else {
|
||||
info.Memory = v
|
||||
}
|
||||
|
||||
parts, err := disk.Partitions(false)
|
||||
if err != nil {
|
||||
retErr = multierror.Append(retErr, &HostInfoError{"disk", err})
|
||||
} else {
|
||||
var usage []*disk.UsageStat
|
||||
for i, part := range parts {
|
||||
u, err := disk.Usage(part.Mountpoint)
|
||||
if err != nil {
|
||||
retErr = multierror.Append(retErr, &HostInfoError{fmt.Sprintf("disk.%d", i), err})
|
||||
continue
|
||||
}
|
||||
usage = append(usage, u)
|
||||
|
||||
}
|
||||
info.Disk = usage
|
||||
}
|
||||
|
||||
if c, err := cpu.Info(); err != nil {
|
||||
retErr = multierror.Append(retErr, &HostInfoError{"cpu", err})
|
||||
} else {
|
||||
info.CPU = c
|
||||
}
|
||||
|
||||
t, err := cpu.Times(true)
|
||||
if err != nil {
|
||||
retErr = multierror.Append(retErr, &HostInfoError{"cpu_times", err})
|
||||
} else {
|
||||
info.CPUTimes = t
|
||||
}
|
||||
|
||||
return info, retErr.ErrorOrNil()
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package hostutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCollectHostInfo(t *testing.T) {
|
||||
info, err := 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.CPUTimes == nil {
|
||||
t.Fatal("expected non-nil CPUTimes 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")
|
||||
}
|
||||
}
|
|
@ -111,8 +111,9 @@ func Handler(props *vault.HandlerProperties) http.Handler {
|
|||
// Create the muxer to handle the actual endpoints
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Handle pprof paths
|
||||
// Handle non-forwarded paths
|
||||
mux.Handle("/v1/sys/pprof/", handleLogicalNoForward(core))
|
||||
mux.Handle("/v1/sys/host-info", handleLogicalNoForward(core))
|
||||
|
||||
mux.Handle("/v1/sys/init", handleSysInit(core))
|
||||
mux.Handle("/v1/sys/seal-status", handleSysSealStatus(core))
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/helper/hostutil"
|
||||
"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
|
||||
secret, err := cores[0].Client.Logical().Read("sys/host-info")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret == nil || secret.Data == nil {
|
||||
t.Fatal("expected data in the response")
|
||||
}
|
||||
|
||||
dataBytes, err := json.Marshal(secret.Data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var info hostutil.HostInfo
|
||||
if err := json.Unmarshal(dataBytes, &info); 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 disk info")
|
||||
}
|
||||
if info.Host == nil {
|
||||
t.Fatal("expected host info")
|
||||
}
|
||||
if info.Memory == nil {
|
||||
t.Fatal("expected memory info")
|
||||
}
|
||||
|
||||
// Query against a standby, should error
|
||||
secret, err = cores[1].Client.Logical().Read("sys/host-info")
|
||||
if err == nil || secret != nil {
|
||||
t.Fatalf("expected error on standby node, HostInfo: %v", secret)
|
||||
}
|
||||
}
|
|
@ -18,12 +18,14 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/vault/physical/raft"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
memdb "github.com/hashicorp/go-memdb"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/helper/hostutil"
|
||||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
|
@ -167,6 +169,7 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend {
|
|||
b.Backend.Paths = append(b.Backend.Paths, b.pprofPaths()...)
|
||||
b.Backend.Paths = append(b.Backend.Paths, b.remountPath())
|
||||
b.Backend.Paths = append(b.Backend.Paths, b.metricsPath())
|
||||
b.Backend.Paths = append(b.Backend.Paths, b.hostInfoPath())
|
||||
|
||||
if core.rawEnabled {
|
||||
b.Backend.Paths = append(b.Backend.Paths, &framework.Path{
|
||||
|
@ -2610,6 +2613,59 @@ func (b *SystemBackend) handleMetrics(ctx context.Context, req *logical.Request,
|
|||
return b.Core.metricsHelper.ResponseForFormat(format)
|
||||
}
|
||||
|
||||
// handleHostInfo collects and returns host-related information, which includes
|
||||
// system information, cpu, disk, and memory usage. Any capture-related errors
|
||||
// returned by the collection method will be returned as response warnings.
|
||||
func (b *SystemBackend) handleHostInfo(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
resp := &logical.Response{}
|
||||
info, err := hostutil.CollectHostInfo()
|
||||
if err != nil {
|
||||
// If the error is a HostInfoError, we return them as response warnings
|
||||
if errs, ok := err.(*multierror.Error); ok {
|
||||
var warnings []string
|
||||
for _, mErr := range errs.Errors {
|
||||
if errwrap.ContainsType(mErr, new(hostutil.HostInfoError)) {
|
||||
warnings = append(warnings, mErr.Error())
|
||||
} else {
|
||||
// If the error is a multierror, it should only be for
|
||||
// HostInfoError, but if it's not for any reason, we return
|
||||
// it as an error to avoid it being swallowed.
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
resp.Warnings = warnings
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if info == nil {
|
||||
return nil, errors.New("unable to collect host information: nil HostInfo")
|
||||
}
|
||||
|
||||
respData := map[string]interface{}{
|
||||
"timestamp": info.Timestamp,
|
||||
}
|
||||
if info.CPU != nil {
|
||||
respData["cpu"] = info.CPU
|
||||
}
|
||||
if info.CPUTimes != nil {
|
||||
respData["cpu_times"] = info.CPUTimes
|
||||
}
|
||||
if info.Disk != nil {
|
||||
respData["disk"] = info.Disk
|
||||
}
|
||||
if info.Host != nil {
|
||||
respData["host"] = info.Host
|
||||
}
|
||||
if info.Memory != nil {
|
||||
respData["memory"] = info.Memory
|
||||
}
|
||||
resp.Data = respData
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *SystemBackend) handleWrappingLookup(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
// This ordering of lookups has been validated already in the wrapping
|
||||
// validation func, we're just doing this for a safety check
|
||||
|
@ -4052,4 +4108,10 @@ This path responds to the following HTTP methods.
|
|||
"Count of requests seen by this Vault cluster over time.",
|
||||
"Count of requests seen by this Vault cluster over time. Not included in count: health checks, UI asset requests, requests forwarded from another cluster.",
|
||||
},
|
||||
"host-info": {
|
||||
"Information about the the host instance that this Vault server is running on.",
|
||||
`Information about the the host instance that this Vault server is running on.
|
||||
The information that gets collected includes host hardware information, and CPU,
|
||||
disk, and memory utilization`,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1166,6 +1166,21 @@ func (b *SystemBackend) metricsPath() *framework.Path {
|
|||
|
||||
}
|
||||
|
||||
func (b *SystemBackend) hostInfoPath() *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "host-info/?",
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.handleHostInfo,
|
||||
Summary: strings.TrimSpace(sysHelp["host-info"][0]),
|
||||
Description: strings.TrimSpace(sysHelp["host-info"][1]),
|
||||
},
|
||||
},
|
||||
HelpSynopsis: strings.TrimSpace(sysHelp["host-info"][0]),
|
||||
HelpDescription: strings.TrimSpace(sysHelp["host-info"][1]),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SystemBackend) authPaths() []*framework.Path {
|
||||
return []*framework.Path{
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue