mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-04-15 07:16:34 +00:00
Get time info from timedated
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostPreflight
|
||||
metadata:
|
||||
name: ntp
|
||||
spec:
|
||||
collectors:
|
||||
- time: {}
|
||||
analyzers:
|
||||
- time:
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "ntp == unsynchronized+inactive"
|
||||
message: System clock not synchronized
|
||||
- warn:
|
||||
when: "ntp == unsynchronized+active"
|
||||
message: System clock not yet synchronized
|
||||
- warn:
|
||||
when: "ntp == synchronized+inactive"
|
||||
message: NTP not active
|
||||
- pass:
|
||||
when: "ntp == synchronized+active"
|
||||
message: System clock is synchronized
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostPreflight
|
||||
metadata:
|
||||
name: timezone
|
||||
spec:
|
||||
collectors:
|
||||
- time: {}
|
||||
analyzers:
|
||||
- time:
|
||||
outcomes:
|
||||
- pass:
|
||||
when: "timezone == UTC"
|
||||
message: Timezone is UTC
|
||||
- fail:
|
||||
message: Timezone is not UTC
|
||||
@@ -15,6 +15,7 @@ require (
|
||||
github.com/go-redis/redis/v7 v7.2.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/godbus/dbus v4.1.0+incompatible
|
||||
github.com/google/go-cmp v0.3.1 // indirect
|
||||
github.com/google/gofuzz v1.1.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
|
||||
@@ -189,6 +189,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
||||
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
|
||||
@@ -83,6 +83,13 @@ func HostAnalyze(hostAnalyzer *troubleshootv1beta2.HostAnalyze, getFile getColle
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
if hostAnalyzer.Time != nil {
|
||||
result, err := analyzeHostTime(hostAnalyzer.Time, getFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("invalid analyzer")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
)
|
||||
|
||||
const (
|
||||
SynchronizedActive = "synchronized+active"
|
||||
SynchronizedInactive = "synchronized+inactive"
|
||||
UnsynchronizedActive = "unsynchronized+active"
|
||||
UnsynchronizedInactive = "unsynchronized+inactive"
|
||||
)
|
||||
|
||||
func analyzeHostTime(hostAnalyzer *troubleshootv1beta2.TimeAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
contents, err := getCollectedFileContents("system/time.json")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get collected file")
|
||||
}
|
||||
|
||||
timeInfo := collect.TimeInfo{}
|
||||
if err := json.Unmarshal(contents, &timeInfo); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal time info")
|
||||
}
|
||||
|
||||
result := AnalyzeResult{}
|
||||
|
||||
title := hostAnalyzer.CheckName
|
||||
if title == "" {
|
||||
title = "Time"
|
||||
}
|
||||
result.Title = title
|
||||
|
||||
for _, outcome := range hostAnalyzer.Outcomes {
|
||||
if outcome.Fail != nil {
|
||||
if outcome.Fail.When == "" {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
isMatch, err := compareHostTimeStatusToActual(outcome.Fail.When, timeInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When)
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsFail = true
|
||||
result.Message = outcome.Fail.Message
|
||||
result.URI = outcome.Fail.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
} else if outcome.Warn != nil {
|
||||
if outcome.Warn.When == "" {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
isMatch, err := compareHostTimeStatusToActual(outcome.Warn.When, timeInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When)
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsWarn = true
|
||||
result.Message = outcome.Warn.Message
|
||||
result.URI = outcome.Warn.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
} else if outcome.Pass != nil {
|
||||
if outcome.Pass.When == "" {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
isMatch, err := compareHostTimeStatusToActual(outcome.Pass.When, timeInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When)
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func compareHostTimeStatusToActual(status string, timeInfo collect.TimeInfo) (res bool, err error) {
|
||||
parts := strings.Split(status, " ")
|
||||
if len(parts) != 3 {
|
||||
return false, fmt.Errorf("Expected exactly 3 parts, got %d", len(parts))
|
||||
}
|
||||
if parts[0] == "timezone" {
|
||||
if parts[1] != "=" && parts[1] != "==" && parts[1] != "===" && parts[1] != "!=" {
|
||||
return false, errors.New(`Only supported operators are "==" and "!="`)
|
||||
}
|
||||
if parts[1] == "!=" {
|
||||
return parts[2] != timeInfo.Timezone, nil
|
||||
}
|
||||
return parts[2] == timeInfo.Timezone, nil
|
||||
}
|
||||
|
||||
if parts[0] == "ntp" {
|
||||
if parts[1] != "=" && parts[1] != "==" && parts[1] != "===" {
|
||||
return false, errors.New(`Only supported operator is "=="`)
|
||||
}
|
||||
|
||||
switch parts[2] {
|
||||
case SynchronizedActive:
|
||||
return timeInfo.NTPSynchronized && timeInfo.NTPActive, nil
|
||||
case SynchronizedInactive:
|
||||
return timeInfo.NTPSynchronized && !timeInfo.NTPActive, nil
|
||||
case UnsynchronizedActive:
|
||||
return !timeInfo.NTPSynchronized && timeInfo.NTPActive, nil
|
||||
case UnsynchronizedInactive:
|
||||
return !timeInfo.NTPSynchronized && !timeInfo.NTPActive, nil
|
||||
default:
|
||||
return false, fmt.Errorf("Unknown status %q. Allowed values are %q, %q, %q, or %q", parts[2], SynchronizedActive, SynchronizedInactive, UnsynchronizedActive, UnsynchronizedInactive)
|
||||
}
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("Unknown keyword: %s", parts[0])
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAnalyzeHostTime(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
timeInfo *collect.TimeInfo
|
||||
hostAnalyzer *troubleshootv1beta2.TimeAnalyze
|
||||
result *AnalyzeResult
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "ntp == synchronized+active",
|
||||
timeInfo: &collect.TimeInfo{
|
||||
Timezone: "UTC",
|
||||
NTPSynchronized: true,
|
||||
NTPActive: true,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.TimeAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == unsynchronized+inactive",
|
||||
Message: "System clock not synchronized",
|
||||
},
|
||||
},
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == synchronized+active",
|
||||
Message: "System clock synchronized and NTP is active",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Time",
|
||||
IsPass: true,
|
||||
Message: "System clock synchronized and NTP is active",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ntp == unsynchronized+inactive",
|
||||
timeInfo: &collect.TimeInfo{
|
||||
Timezone: "UTC",
|
||||
NTPSynchronized: false,
|
||||
NTPActive: false,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.TimeAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == unsynchronized+inactive",
|
||||
Message: "System clock not synchronized",
|
||||
},
|
||||
},
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == synchronized+active",
|
||||
Message: "System clock synchronized and NTP is active",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Time",
|
||||
IsFail: true,
|
||||
Message: "System clock not synchronized",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ntp == unsynchronized+active",
|
||||
timeInfo: &collect.TimeInfo{
|
||||
Timezone: "UTC",
|
||||
NTPSynchronized: false,
|
||||
NTPActive: true,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.TimeAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == unsynchronized+inactive",
|
||||
Message: "System clock not synchronized",
|
||||
},
|
||||
},
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == unsynchronized+active",
|
||||
Message: "System clock not yet synchronized",
|
||||
},
|
||||
},
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == synchronized+active",
|
||||
Message: "System clock synchronized and NTP is active",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Time",
|
||||
IsWarn: true,
|
||||
Message: "System clock not yet synchronized",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ntp == synchronized+inactive",
|
||||
timeInfo: &collect.TimeInfo{
|
||||
Timezone: "UTC",
|
||||
NTPSynchronized: true,
|
||||
NTPActive: false,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.TimeAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == unsynchronized+inactive",
|
||||
Message: "System clock not synchronized",
|
||||
},
|
||||
},
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == unsynchronized+active",
|
||||
Message: "System clock not yet synchronized",
|
||||
},
|
||||
},
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == synchronized+inactive",
|
||||
Message: "System clock synchronized for now",
|
||||
},
|
||||
},
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "ntp == synchronized+active",
|
||||
Message: "System clock synchronized and NTP is active",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Time",
|
||||
IsWarn: true,
|
||||
Message: "System clock synchronized for now",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "timezone",
|
||||
timeInfo: &collect.TimeInfo{
|
||||
Timezone: "UTC",
|
||||
NTPSynchronized: true,
|
||||
NTPActive: true,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.TimeAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "timezone == UTC",
|
||||
Message: "Timezone is set to UTC",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
Message: "timezone not set to UTC",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Time",
|
||||
IsPass: true,
|
||||
Message: "Timezone is set to UTC",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "timezone is not UTC",
|
||||
timeInfo: &collect.TimeInfo{
|
||||
Timezone: "PST",
|
||||
NTPSynchronized: true,
|
||||
NTPActive: true,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.TimeAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "timezone != UTC",
|
||||
Message: "Timezone is not set to UTC",
|
||||
},
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
Message: "Timezone is set to UTC",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Time",
|
||||
IsFail: true,
|
||||
Message: "Timezone is not set to UTC",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
b, err := json.Marshal(test.timeInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
getCollectedFileContents := func(filename string) ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
result, err := analyzeHostTime(test.hostAnalyzer, getCollectedFileContents)
|
||||
if test.expectErr {
|
||||
req.Error(err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.result, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,11 @@ type HTTPAnalyze struct {
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type TimeAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type HostAnalyze struct {
|
||||
CPU *CPUAnalyze `json:"cpu,omitempty" yaml:"cpu,omitempty"`
|
||||
//
|
||||
@@ -46,4 +51,6 @@ type HostAnalyze struct {
|
||||
TCPPortStatus *TCPPortStatusAnalyze `json:"tcpPortStatus,omitempty" yaml:"tcpPortStatus,omitempty"`
|
||||
|
||||
HTTP *HTTPAnalyze `json:"http" yaml:"http"`
|
||||
|
||||
Time *TimeAnalyze `json:"time" yaml:"time"`
|
||||
}
|
||||
|
||||
@@ -59,6 +59,10 @@ type HostHTTP struct {
|
||||
Put *Put `json:"put,omitempty" yaml:"put,omitempty"`
|
||||
}
|
||||
|
||||
type HostTime struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
type HostCollect struct {
|
||||
CPU *CPU `json:"cpu,omitempty" yaml:"cpu,omitempty"`
|
||||
Memory *Memory `json:"memory,omitempty" yaml:"memory,omitempty"`
|
||||
@@ -69,6 +73,7 @@ type HostCollect struct {
|
||||
IPV4Interfaces *IPV4Interfaces `json:"ipv4Interfaces,omitempty" yaml:"ipv4Interfaces,omitempty"`
|
||||
DiskUsage *DiskUsage `json:"diskUsage,omitempty" yaml:"diskUsage,omitempty"`
|
||||
HTTP *HostHTTP `json:"http,omitempty" yaml:"http,omitempty"`
|
||||
Time *HostTime `json:"time,omitempty" yaml:"time,omitempty"`
|
||||
}
|
||||
|
||||
func (c *HostCollect) GetName() string {
|
||||
|
||||
@@ -1103,6 +1103,11 @@ func (in *HostAnalyze) DeepCopyInto(out *HostAnalyze) {
|
||||
*out = new(HTTPAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Time != nil {
|
||||
in, out := &in.Time, &out.Time
|
||||
*out = new(TimeAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostAnalyze.
|
||||
@@ -1163,6 +1168,11 @@ func (in *HostCollect) DeepCopyInto(out *HostCollect) {
|
||||
*out = new(HostHTTP)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Time != nil {
|
||||
in, out := &in.Time, &out.Time
|
||||
*out = new(HostTime)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostCollect.
|
||||
@@ -1333,6 +1343,22 @@ func (in *HostPreflightStatus) DeepCopy() *HostPreflightStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HostTime) DeepCopyInto(out *HostTime) {
|
||||
*out = *in
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostTime.
|
||||
func (in *HostTime) DeepCopy() *HostTime {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HostTime)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *IPV4Interfaces) DeepCopyInto(out *IPV4Interfaces) {
|
||||
*out = *in
|
||||
@@ -2348,3 +2374,30 @@ func (in *TextAnalyze) DeepCopy() *TextAnalyze {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TimeAnalyze) DeepCopyInto(out *TimeAnalyze) {
|
||||
*out = *in
|
||||
out.AnalyzeMeta = in.AnalyzeMeta
|
||||
if in.Outcomes != nil {
|
||||
in, out := &in.Outcomes, &out.Outcomes
|
||||
*out = make([]*Outcome, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(Outcome)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeAnalyze.
|
||||
func (in *TimeAnalyze) DeepCopy() *TimeAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TimeAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ func (c *HostCollector) RunCollectorSync() (result map[string][]byte, err error)
|
||||
result, err = HostTCPPortStatus(c)
|
||||
} else if c.Collect.HTTP != nil {
|
||||
result, err = HostHTTP(c)
|
||||
} else if c.Collect.Time != nil {
|
||||
result, err = HostTime(c)
|
||||
} else {
|
||||
err = errors.New("no spec found to run")
|
||||
return
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type NTPStatus string
|
||||
|
||||
type TimeInfo struct {
|
||||
Timezone string `json:"timezone"`
|
||||
NTPSynchronized bool `json:"ntp_synchronized"`
|
||||
NTPActive bool `json:"ntp_active"`
|
||||
}
|
||||
|
||||
func HostTime(c *HostCollector) (map[string][]byte, error) {
|
||||
timeInfo := TimeInfo{}
|
||||
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to connect to dbus")
|
||||
}
|
||||
defer func() {
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Printf("Failed to close dbus connection: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
prop := "org.freedesktop.timedate1.Timezone"
|
||||
variant, err := conn.Object("org.freedesktop.timedate1", "/org/freedesktop/timedate1").GetProperty(prop)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read property %s", prop)
|
||||
}
|
||||
timeInfo.Timezone = strings.Trim(variant.String(), `"`)
|
||||
// UTC is reported as Etc/UTC on Ubuntu
|
||||
if strings.ToLower(timeInfo.Timezone) == "etc/utc" {
|
||||
timeInfo.Timezone = "UTC"
|
||||
}
|
||||
|
||||
prop = "org.freedesktop.timedate1.NTPSynchronized"
|
||||
variant, err = conn.Object("org.freedesktop.timedate1", "/org/freedesktop/timedate1").GetProperty(prop)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read property %s", prop)
|
||||
}
|
||||
switch variant.String() {
|
||||
case "true":
|
||||
timeInfo.NTPSynchronized = true
|
||||
case "false":
|
||||
timeInfo.NTPSynchronized = false
|
||||
default:
|
||||
return nil, fmt.Errorf("Unexpected value for property %s: %s", prop, variant.String())
|
||||
}
|
||||
|
||||
prop = "org.freedesktop.timedate1.NTP"
|
||||
variant, err = conn.Object("org.freedesktop.timedate1", "/org/freedesktop/timedate1").GetProperty(prop)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read property %s", prop)
|
||||
}
|
||||
switch variant.String() {
|
||||
case "true":
|
||||
timeInfo.NTPActive = true
|
||||
case "false":
|
||||
timeInfo.NTPActive = false
|
||||
default:
|
||||
return nil, fmt.Errorf("Unexpected value for property %s: %s", prop, variant.String())
|
||||
}
|
||||
|
||||
b, err := json.Marshal(timeInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal time info")
|
||||
}
|
||||
|
||||
return map[string][]byte{
|
||||
"system/time.json": b,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user