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 3 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
20 changes: 12 additions & 8 deletions api/sys_hostinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,28 @@ func (c *Sys) HostInfo() (*HostInfoResponse, error) {
return nil, errors.New("data from server response is empty")
}

var result HostInfoResponse
err = mapstructure.WeakDecode(secret.Data, &result)
// 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")

result.CollectionTime, err = time.Parse(time.RFC3339, secret.Data["collection_time"].(string))
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
CollectionTime time.Time `json:"collection_time"`
CPU []cpu.InfoStat `json:"cpu"`
Disk *disk.UsageStat `json:"disk"`
Host *host.InfoStat `json:"host"`
Memory *mem.VirtualMemoryStat `json:"memory"`
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"`
}
2 changes: 1 addition & 1 deletion http/sys_hostinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestSysHostInfo(t *testing.T) {
t.Fatal("expected non-nil HostInfo")
}

if info.CollectionTime.IsZero() {
if info.Timestamp.IsZero() {
t.Fatalf("expected a valid timestamp")
}
if info.CPU == nil {
Expand Down
40 changes: 26 additions & 14 deletions vault/hostinfo.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package vault

import (
"runtime"
"time"

"github.com/hashicorp/go-multierror"
Expand All @@ -11,14 +10,16 @@ import (
"github.com/shirou/gopsutil/mem"
)

// HostInfo holds all the information that gets captured on the host.
type HostInfo struct {
CollectionTime time.Time `json:"collection_time"`
CPU []cpu.InfoStat `json:"cpu"`
Disk *disk.UsageStat `json:"disk"`
Host *host.InfoStat `json:"host"`
Memory *mem.VirtualMemoryStat `json:"memory"`
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
}
Expand All @@ -31,9 +32,15 @@ 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{CollectionTime: time.Now()}
info := &HostInfo{Timestamp: time.Now().UTC()}

if h, err := host.Info(); err != nil {
retErr = multierror.Append(retErr, &HostInfoError{err})
Expand All @@ -47,15 +54,20 @@ func (c *Core) CollectHostInfo() (*HostInfo, error) {
info.Memory = v
}

diskPath := "/"
if runtime.GOOS == "windows" {
diskPath = "C:"
}

if d, err := disk.Usage(diskPath); err != nil {
parts, err := disk.Partitions(false)
if err != nil {
retErr = multierror.Append(retErr, &HostInfoError{err})
} else {
info.Disk = d
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
Expand Down
4 changes: 2 additions & 2 deletions vault/hostinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ func TestCollectHostInfo(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if info.CollectionTime.IsZero() {
t.Fatal("expected non-zero CollectionTime")
if info.Timestamp.IsZero() {
t.Fatal("expected non-zero Timestamp")
}
if info.CPU == nil {
t.Fatal("expected non-nil CPU value")
Expand Down
10 changes: 9 additions & 1 deletion vault/logical_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -2611,6 +2611,9 @@ 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 := b.Core.CollectHostInfo()
Expand All @@ -2621,6 +2624,11 @@ func (b *SystemBackend) handleHostInfo(ctx context.Context, req *logical.Request
for _, mErr := range errs.Errors {
if errwrap.ContainsType(mErr, new(HostInfoError)) {
calvn marked this conversation as resolved.
Show resolved Hide resolved
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
Expand All @@ -2634,7 +2642,7 @@ func (b *SystemBackend) handleHostInfo(ctx context.Context, req *logical.Request
}

respData := map[string]interface{}{
"collection_time": info.CollectionTime,
"timestamp": info.Timestamp,
}
if info.CPU != nil {
respData["cpu"] = info.CPU
Expand Down