mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-04-15 07:16:34 +00:00
Host preflight (#311)
* Add HostPreflight v1beta2 * Work on TCP Load Balancer * Host disk usage collector and analyzer * Host memory analyzer * TCP port status * TCP load balancer * Review changes Co-authored-by: Marc Campbell <marc.e.campbell@gmail.com>
This commit is contained in:
co-authored by
Marc Campbell
parent
df68f7445f
commit
10a34c2e58
+82
-36
@@ -86,15 +86,16 @@ func runPreflights(v *viper.Viper, arg string) error {
|
||||
return errors.Wrapf(err, "failed to parse %s", arg)
|
||||
}
|
||||
|
||||
preflightSpec := obj.(*troubleshootv1beta2.Preflight)
|
||||
var collectResults preflight.CollectResult
|
||||
preflightSpecName := ""
|
||||
finishedCh := make(chan bool, 1)
|
||||
progressCh := make(chan interface{}, 0) // non-zero buffer will result in missed messages
|
||||
|
||||
s := spin.New()
|
||||
finishedCh := make(chan bool, 1)
|
||||
progressChan := make(chan interface{}, 0) // non-zero buffer will result in missed messages
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case msg, ok := <-progressChan:
|
||||
case msg, ok := <-progressCh:
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -114,47 +115,40 @@ func runPreflights(v *viper.Viper, arg string) error {
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
close(finishedCh)
|
||||
close(progressCh)
|
||||
}()
|
||||
|
||||
restConfig, err := k8sutil.GetRESTConfig()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
collectOpts := preflight.CollectOpts{
|
||||
Namespace: v.GetString("namespace"),
|
||||
IgnorePermissionErrors: v.GetBool("collect-without-permissions"),
|
||||
ProgressChan: progressChan,
|
||||
KubernetesRestConfig: restConfig,
|
||||
}
|
||||
|
||||
if v.GetString("since") != "" || v.GetString("since-time") != "" {
|
||||
err := parseTimeFlags(v, progressChan, preflightSpec.Spec.Collectors)
|
||||
if preflightSpec, ok := obj.(*troubleshootv1beta2.Preflight); ok {
|
||||
r, err := collectInCluster(preflightSpec, finishedCh, progressCh)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to collect in cluster")
|
||||
}
|
||||
collectResults = *r
|
||||
preflightSpecName = preflightSpec.Name
|
||||
} else if hostPreflightSpec, ok := obj.(*troubleshootv1beta2.HostPreflight); ok {
|
||||
r, err := collectHost(hostPreflightSpec, finishedCh, progressCh)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to collect from host")
|
||||
}
|
||||
collectResults = *r
|
||||
preflightSpecName = hostPreflightSpec.Name
|
||||
}
|
||||
|
||||
collectResults, err := preflight.Collect(collectOpts, preflightSpec)
|
||||
if err != nil {
|
||||
if !collectResults.IsRBACAllowed {
|
||||
if preflightSpec.Spec.UploadResultsTo != "" {
|
||||
err := uploadErrors(preflightSpec.Spec.UploadResultsTo, collectResults.Collectors)
|
||||
if err != nil {
|
||||
progressChan <- err
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
if collectResults == nil {
|
||||
return errors.New("no results")
|
||||
}
|
||||
|
||||
analyzeResults := collectResults.Analyze()
|
||||
if preflightSpec.Spec.UploadResultsTo != "" {
|
||||
err := uploadResults(preflightSpec.Spec.UploadResultsTo, analyzeResults)
|
||||
if err != nil {
|
||||
progressChan <- err
|
||||
|
||||
if preflightSpec, ok := obj.(*troubleshootv1beta2.Preflight); ok {
|
||||
if preflightSpec.Spec.UploadResultsTo != "" {
|
||||
err := uploadResults(preflightSpec.Spec.UploadResultsTo, analyzeResults)
|
||||
if err != nil {
|
||||
progressCh <- err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,10 +158,62 @@ func runPreflights(v *viper.Viper, arg string) error {
|
||||
if len(analyzeResults) == 0 {
|
||||
return errors.New("no data has been collected")
|
||||
}
|
||||
return showInteractiveResults(preflightSpec.Name, analyzeResults)
|
||||
return showInteractiveResults(preflightSpecName, analyzeResults)
|
||||
}
|
||||
|
||||
return showStdoutResults(v.GetString("format"), preflightSpec.Name, analyzeResults)
|
||||
return showStdoutResults(v.GetString("format"), preflightSpecName, analyzeResults)
|
||||
}
|
||||
|
||||
func collectInCluster(preflightSpec *troubleshootv1beta2.Preflight, finishedCh chan bool, progressCh chan interface{}) (*preflight.CollectResult, error) {
|
||||
v := viper.GetViper()
|
||||
|
||||
restConfig, err := k8sutil.GetRESTConfig()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert kube flags to rest config")
|
||||
}
|
||||
|
||||
collectOpts := preflight.CollectOpts{
|
||||
Namespace: v.GetString("namespace"),
|
||||
IgnorePermissionErrors: v.GetBool("collect-without-permissions"),
|
||||
ProgressChan: progressCh,
|
||||
KubernetesRestConfig: restConfig,
|
||||
}
|
||||
|
||||
if v.GetString("since") != "" || v.GetString("since-time") != "" {
|
||||
err := parseTimeFlags(v, progressCh, preflightSpec.Spec.Collectors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
collectResults, err := preflight.Collect(collectOpts, preflightSpec)
|
||||
if err != nil {
|
||||
if !collectResults.IsRBACAllowed() {
|
||||
if preflightSpec.Spec.UploadResultsTo != "" {
|
||||
clusterCollectResults := collectResults.(preflight.ClusterCollectResult)
|
||||
err := uploadErrors(preflightSpec.Spec.UploadResultsTo, clusterCollectResults.Collectors)
|
||||
if err != nil {
|
||||
progressCh <- err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &collectResults, nil
|
||||
}
|
||||
|
||||
func collectHost(hostPreflightSpec *troubleshootv1beta2.HostPreflight, finishedCh chan bool, progressCh chan interface{}) (*preflight.CollectResult, error) {
|
||||
collectOpts := preflight.CollectOpts{
|
||||
ProgressChan: progressCh,
|
||||
}
|
||||
|
||||
collectResults, err := preflight.CollectHost(collectOpts, hostPreflightSpec)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to collect from host")
|
||||
}
|
||||
|
||||
return &collectResults, nil
|
||||
}
|
||||
|
||||
func parseTimeFlags(v *viper.Viper, progressChan chan interface{}, collectors []*troubleshootv1beta2.Collect) error {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostPreflight
|
||||
metadata:
|
||||
name: cpu
|
||||
spec:
|
||||
collectors:
|
||||
- cpu: {}
|
||||
analyzers:
|
||||
- cpu:
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "physical < 4"
|
||||
message: At least 4 physical CPU cores are required
|
||||
- fail:
|
||||
when: "logical < 8"
|
||||
message: At least 8 CPU cores are required
|
||||
- warn:
|
||||
when: "count < 16"
|
||||
message: At least 16 CPU cores preferred
|
||||
- pass:
|
||||
message: This server has sufficient CPU cores
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostPreflight
|
||||
metadata:
|
||||
name: diskUsage
|
||||
spec:
|
||||
collectors:
|
||||
- diskUsage:
|
||||
collectorName: ephemeral
|
||||
path: /var/lib/kubelet
|
||||
analyzers:
|
||||
- diskUsage:
|
||||
collectorName: ephemeral
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "total < 20Gi"
|
||||
message: /var/lib/kubelet has less than 20Gi of total space
|
||||
- fail:
|
||||
when: "available < 10Gi"
|
||||
message: /var/lib/kubelet has less than 10Gi of disk space available
|
||||
- fail:
|
||||
when: "used/total > 70%"
|
||||
message: /var/lib/kubelet is more than 70% full
|
||||
- warn:
|
||||
when: "total < 40Gi"
|
||||
message: /var/lib/kubelet has less than 40Gi of total space
|
||||
- warn:
|
||||
when: "used/total > 60%"
|
||||
message: /var/lib/kubelet is more than 60% full
|
||||
- pass:
|
||||
when: "available/total >= 90%"
|
||||
message: /var/lib/kubelet has more than 90% available
|
||||
- pass:
|
||||
message: /var/lib/kubelet has sufficient disk space available
|
||||
@@ -0,0 +1,19 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostPreflight
|
||||
metadata:
|
||||
name: memory
|
||||
spec:
|
||||
collectors:
|
||||
- memory:
|
||||
collectorName: memory
|
||||
analyzers:
|
||||
- memory:
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "< 8Gi"
|
||||
message: At least 8Gi of memory is required
|
||||
- warn:
|
||||
when: "< 32Gi"
|
||||
message: At least 32Gi of memory is recommended
|
||||
- pass:
|
||||
message: The system has as sufficient memory
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostPreflight
|
||||
metadata:
|
||||
name: port
|
||||
spec:
|
||||
collectors:
|
||||
- tcpPortStatus:
|
||||
collectorName: k8s
|
||||
port: 7443
|
||||
analyzers:
|
||||
- tcpPortStatus:
|
||||
collectorName: k8s
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "connection-refused"
|
||||
message: Connection to port 7443 was refused.
|
||||
- fail:
|
||||
when: "address-in-use"
|
||||
message: Another process was already listening on port 7443.
|
||||
- fail:
|
||||
when: "connection-timeout"
|
||||
message: Timed out connecting to port 7443. Check your firewall.
|
||||
- fail:
|
||||
when: "error"
|
||||
message: Unexpected port status
|
||||
- pass:
|
||||
when: "connected"
|
||||
message: Port 7443 is open
|
||||
- warn:
|
||||
message: Unexpected port status
|
||||
@@ -0,0 +1,31 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostPreflight
|
||||
metadata:
|
||||
name: loadbalancer
|
||||
spec:
|
||||
collectors:
|
||||
- tcpLoadBalancer:
|
||||
collectorName: loadbalancer
|
||||
port: 7443
|
||||
address: 10.128.0.29:7444
|
||||
analyzers:
|
||||
- tcpLoadBalancer:
|
||||
collectorName: loadbalancer
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "connection-refused"
|
||||
message: Connection to port 7443 via load balancer was refused.
|
||||
- fail:
|
||||
when: "address-in-use"
|
||||
message: Another process was already listening on port 7443.
|
||||
- fail:
|
||||
when: "connection-timeout"
|
||||
message: Timed out connecting to port 7443 via load balancer. Check your firewall.
|
||||
- fail:
|
||||
when: "error"
|
||||
message: Unexpected port status
|
||||
- pass:
|
||||
when: "connected"
|
||||
message: Successfully connected to port 7443 via load balancer
|
||||
- warn:
|
||||
message: Unexpected port status
|
||||
@@ -0,0 +1,51 @@
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: HostPreflight
|
||||
metadata:
|
||||
name: example
|
||||
spec:
|
||||
collectors:
|
||||
- tcpLoadBalancer:
|
||||
collectorName: LB1
|
||||
address: 10.1.1.1
|
||||
port: 6443
|
||||
timeout: 5000ms
|
||||
- diskUsage:
|
||||
collectorName: ephemeral
|
||||
path: /var/lib/kubelet
|
||||
analyzers:
|
||||
- cpu:
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "< 4"
|
||||
message: This server has less than 4 CPU cores, and we require 8, but recommend 16
|
||||
- warn:
|
||||
when: "< 16"
|
||||
message: This server has at least 4 CPU cores, but we recommend 16 or more
|
||||
- pass:
|
||||
message: This server has sufficient CPU cores
|
||||
- tcpLoadBalancer:
|
||||
collectorName: LB1
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "connection-timeout"
|
||||
message: The TCP Load Balancer is not forwarding traffic to this server.
|
||||
- fail:
|
||||
when: "address-in-use"
|
||||
message: The local port is not available to validate the Load Balancer configuration.
|
||||
- pass:
|
||||
when: "connected"
|
||||
message: The specified TCP Load Balancer appears to be properly forwarding traffic to this server.
|
||||
- diskUsage:
|
||||
collectorName: ephemeral
|
||||
outcomes:
|
||||
- fail:
|
||||
when: "total < 20Gi"
|
||||
message: /var/lib/kubelet has less than 20Gi of total space
|
||||
- fail:
|
||||
when: "available < 10Gi"
|
||||
message: /var/lib/kubelet has less than 10Gi of disk space available
|
||||
- fail:
|
||||
when: "used/total > 70%"
|
||||
message: /var/lib/kubelet is more than 70% full
|
||||
- pass:
|
||||
message: /var/lib/kubelet has sufficient disk space available
|
||||
@@ -32,6 +32,7 @@ require (
|
||||
github.com/prometheus/procfs v0.0.5 // indirect
|
||||
github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851
|
||||
github.com/segmentio/ksuid v1.0.3
|
||||
github.com/shirou/gopsutil v3.20.12+incompatible
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.4.0
|
||||
|
||||
@@ -414,6 +414,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
|
||||
github.com/segmentio/ksuid v1.0.3 h1:FoResxvleQwYiPAVKe1tMUlEirodZqlqglIuFsdDntY=
|
||||
github.com/segmentio/ksuid v1.0.3/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil v3.20.12+incompatible h1:6VEGkOXP/eP4o2Ilk8cSsX0PhOEfX6leqAnD+urrp9M=
|
||||
github.com/shirou/gopsutil v3.20.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
|
||||
+41
-1
@@ -40,6 +40,46 @@ func isExcluded(excludeVal multitype.BoolOrString) (bool, error) {
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func HostAnalyze(hostAnalyzer *troubleshootv1beta2.HostAnalyze, getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
||||
if hostAnalyzer.CPU != nil {
|
||||
result, err := analyzeHostCPU(hostAnalyzer.CPU, getFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
if hostAnalyzer.TCPLoadBalancer != nil {
|
||||
result, err := analyzeHostTCPLoadBalancer(hostAnalyzer.TCPLoadBalancer, getFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
if hostAnalyzer.DiskUsage != nil {
|
||||
result, err := analyzeHostDiskUsage(hostAnalyzer.DiskUsage, getFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
if hostAnalyzer.Memory != nil {
|
||||
result, err := analyzeHostMemory(hostAnalyzer.Memory, getFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
if hostAnalyzer.TCPPortStatus != nil {
|
||||
result, err := analyzeHostTCPPortStatus(hostAnalyzer.TCPPortStatus, getFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("invalid analyzer")
|
||||
}
|
||||
|
||||
func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
||||
if analyzer.ClusterVersion != nil {
|
||||
isExcluded, err := isExcluded(analyzer.ClusterVersion.Exclude)
|
||||
@@ -265,6 +305,6 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont
|
||||
}
|
||||
return []*AnalyzeResult{result}, nil
|
||||
}
|
||||
return nil, errors.New("invalid analyzer")
|
||||
|
||||
return nil, errors.New("invalid analyzer")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
)
|
||||
|
||||
func analyzeHostCPU(hostAnalyzer *troubleshootv1beta2.CPUAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
contents, err := getCollectedFileContents("system/cpu.json")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get collected file")
|
||||
}
|
||||
|
||||
cpuInfo := collect.CPUInfo{}
|
||||
if err := json.Unmarshal(contents, &cpuInfo); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal cpu info")
|
||||
}
|
||||
|
||||
result := AnalyzeResult{}
|
||||
|
||||
title := hostAnalyzer.CheckName
|
||||
if title == "" {
|
||||
title = "Number of CPUs"
|
||||
}
|
||||
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 := compareHostCPUConditionalToActual(outcome.Fail.When, cpuInfo.LogicalCount, cpuInfo.PhysicalCount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare")
|
||||
}
|
||||
|
||||
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 := compareHostCPUConditionalToActual(outcome.Warn.When, cpuInfo.LogicalCount, cpuInfo.PhysicalCount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare")
|
||||
}
|
||||
|
||||
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 := compareHostCPUConditionalToActual(outcome.Pass.When, cpuInfo.LogicalCount, cpuInfo.PhysicalCount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compare")
|
||||
}
|
||||
|
||||
if isMatch {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func compareHostCPUConditionalToActual(conditional string, logicalCount int, physicalCount int) (res bool, err error) {
|
||||
compareLogical := false
|
||||
comparePhysical := false
|
||||
compareUnspecified := false
|
||||
|
||||
comparator := ""
|
||||
desired := ""
|
||||
|
||||
parts := strings.Split(conditional, " ")
|
||||
if len(parts) == 3 {
|
||||
comparator = parts[1]
|
||||
desired = parts[2]
|
||||
if strings.ToLower(parts[0]) == "logical" {
|
||||
compareLogical = true
|
||||
} else if strings.ToLower(parts[0]) == "physical" {
|
||||
comparePhysical = true
|
||||
} else if strings.ToLower(parts[0]) == "count" {
|
||||
compareUnspecified = true
|
||||
}
|
||||
} else if len(parts) == 2 {
|
||||
compareUnspecified = true
|
||||
comparator = parts[0]
|
||||
desired = parts[1]
|
||||
}
|
||||
|
||||
if !compareLogical && !comparePhysical && !compareUnspecified {
|
||||
return false, errors.New("unable to parse conditional")
|
||||
}
|
||||
|
||||
if compareLogical {
|
||||
return doCompareHostCPU(comparator, desired, logicalCount)
|
||||
} else if comparePhysical {
|
||||
return doCompareHostCPU(comparator, desired, physicalCount)
|
||||
} else {
|
||||
actual := logicalCount
|
||||
if physicalCount > logicalCount {
|
||||
actual = physicalCount
|
||||
}
|
||||
|
||||
return doCompareHostCPU(comparator, desired, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func doCompareHostCPU(operator string, desired string, actual int) (bool, error) {
|
||||
desiredInt, err := strconv.ParseInt(desired, 10, 64)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "failed to parse")
|
||||
}
|
||||
|
||||
switch operator {
|
||||
case "<":
|
||||
return actual < int(desiredInt), nil
|
||||
case "<=":
|
||||
return actual <= int(desiredInt), nil
|
||||
case ">":
|
||||
return actual > int(desiredInt), nil
|
||||
case ">=":
|
||||
return actual >= int(desiredInt), nil
|
||||
case "=", "==", "===":
|
||||
return actual == int(desiredInt), nil
|
||||
}
|
||||
|
||||
return false, errors.New("unknown operator")
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_doCompareHostCPU(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
operator string
|
||||
desired string
|
||||
actual int
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "< 16",
|
||||
operator: "<",
|
||||
desired: "16",
|
||||
actual: 8,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "< 8 when actual is 8",
|
||||
operator: "<",
|
||||
desired: "8",
|
||||
actual: 8,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "<= 8 when actual is 8",
|
||||
operator: "<=",
|
||||
desired: "8",
|
||||
actual: 8,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "<= 8 when actual is 16",
|
||||
operator: "<=",
|
||||
desired: "8",
|
||||
actual: 16,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "== 8 when actual is 16",
|
||||
operator: "==",
|
||||
desired: "8",
|
||||
actual: 16,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "== 8 when actual is 8",
|
||||
operator: "==",
|
||||
desired: "8",
|
||||
actual: 8,
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
actual, err := doCompareHostCPU(test.operator, test.desired, test.actual)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_compareHostCPUConditionalToActual(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
when string
|
||||
logicalCount int
|
||||
physicalCount int
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "physical > 4, when physical is 8",
|
||||
when: "physical > 4",
|
||||
logicalCount: 0,
|
||||
physicalCount: 8,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "physical > 4, when physical is 4",
|
||||
when: "physical > 4",
|
||||
logicalCount: 0,
|
||||
physicalCount: 4,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "physical > 4, when physical is 3, logical is 6",
|
||||
when: "physical > 4",
|
||||
logicalCount: 6,
|
||||
physicalCount: 3,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "logical > 4, when physical is 4, logical is 8",
|
||||
when: "logical > 4",
|
||||
logicalCount: 8,
|
||||
physicalCount: 4,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: ">= 4, when physical is 2, logical is 4",
|
||||
when: ">= 4",
|
||||
logicalCount: 4,
|
||||
physicalCount: 2,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "count < 4, when physical is 2, logical is 4",
|
||||
when: "count < 4",
|
||||
logicalCount: 4,
|
||||
physicalCount: 2,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "count <= 4, when physical is 2, logical is 4",
|
||||
when: "count <= 4",
|
||||
logicalCount: 4,
|
||||
physicalCount: 2,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "== 4, physical is 4, logical is 4",
|
||||
when: "== 4",
|
||||
logicalCount: 4,
|
||||
physicalCount: 4,
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
actual, err := compareHostCPUConditionalToActual(test.when, test.logicalCount, test.physicalCount)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
func analyzeHostDiskUsage(hostAnalyzer *troubleshootv1beta2.DiskUsageAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
key := collect.HostDiskUsageKey(hostAnalyzer.CollectorName)
|
||||
contents, err := getCollectedFileContents(key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get collected file %s", key)
|
||||
}
|
||||
|
||||
diskUsageInfo := collect.DiskUsageInfo{}
|
||||
if err := json.Unmarshal(contents, &diskUsageInfo); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to unmarshal disk usage info from %s", key)
|
||||
}
|
||||
|
||||
result := AnalyzeResult{}
|
||||
|
||||
title := hostAnalyzer.CheckName
|
||||
if title == "" {
|
||||
title = fmt.Sprintf("Disk Usage %s", hostAnalyzer.CollectorName)
|
||||
}
|
||||
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 := compareHostDiskUsageConditionalToActual(outcome.Fail.When, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compare %q", 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 := compareHostDiskUsageConditionalToActual(outcome.Warn.When, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compare %q", 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 := compareHostDiskUsageConditionalToActual(outcome.Pass.When, diskUsageInfo.TotalBytes, diskUsageInfo.UsedBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compare %q", 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 compareHostDiskUsageConditionalToActual(conditional string, totalBytes uint64, usedBytes uint64) (res bool, err error) {
|
||||
parts := strings.Split(conditional, " ")
|
||||
if len(parts) != 3 {
|
||||
return false, fmt.Errorf("conditional must have exactly 3 parts, got %d", len(parts))
|
||||
}
|
||||
stat := strings.ToLower(parts[0])
|
||||
comparator := parts[1]
|
||||
desired := parts[2]
|
||||
|
||||
switch stat {
|
||||
case "total":
|
||||
return doCompareHostDiskUsage(comparator, desired, totalBytes)
|
||||
case "used":
|
||||
return doCompareHostDiskUsage(comparator, desired, usedBytes)
|
||||
case "available":
|
||||
return doCompareHostDiskUsage(comparator, desired, totalBytes-usedBytes)
|
||||
case "used/total":
|
||||
used := float64(usedBytes) / float64(totalBytes)
|
||||
return doCompareHostDiskUsagePercent(comparator, desired, used)
|
||||
case "available/total":
|
||||
available := float64(totalBytes-usedBytes) / float64(totalBytes)
|
||||
return doCompareHostDiskUsagePercent(comparator, desired, available)
|
||||
}
|
||||
return false, fmt.Errorf("unknown disk usage statistic %q", stat)
|
||||
}
|
||||
|
||||
func doCompareHostDiskUsage(operator string, desired string, actual uint64) (bool, error) {
|
||||
quantity, err := resource.ParseQuantity(desired)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("could not parse quantity %q", desired)
|
||||
}
|
||||
desiredInt, ok := quantity.AsInt64()
|
||||
if !ok {
|
||||
return false, fmt.Errorf("could not parse quantity %q", desired)
|
||||
}
|
||||
|
||||
switch operator {
|
||||
case "<":
|
||||
return actual < uint64(desiredInt), nil
|
||||
case "<=":
|
||||
return actual <= uint64(desiredInt), nil
|
||||
case ">":
|
||||
return actual > uint64(desiredInt), nil
|
||||
case ">=":
|
||||
return actual >= uint64(desiredInt), nil
|
||||
case "=", "==", "===":
|
||||
return actual == uint64(desiredInt), nil
|
||||
}
|
||||
|
||||
return false, errors.New("unknown operator")
|
||||
}
|
||||
|
||||
func doCompareHostDiskUsagePercent(operator string, desired string, actual float64) (bool, error) {
|
||||
isPercent := false
|
||||
if strings.HasSuffix(desired, "%") {
|
||||
desired = strings.TrimSuffix(desired, "%")
|
||||
isPercent = true
|
||||
}
|
||||
desiredPercent, err := strconv.ParseFloat(desired, 64)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "parsed desired quantity")
|
||||
}
|
||||
if isPercent {
|
||||
desiredPercent = desiredPercent / 100.0
|
||||
}
|
||||
|
||||
switch operator {
|
||||
case "<":
|
||||
return actual < desiredPercent, nil
|
||||
case "<=":
|
||||
return actual <= desiredPercent, nil
|
||||
case ">":
|
||||
return actual > desiredPercent, nil
|
||||
case ">=":
|
||||
return actual >= desiredPercent, nil
|
||||
case "=", "==", "===":
|
||||
return actual == desiredPercent, nil
|
||||
}
|
||||
|
||||
return false, errors.New("unknown operator")
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
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 Test_doCompareHostDiskUsage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
operator string
|
||||
desired string
|
||||
actual uint64
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: ">= 1Gi, when actual is 2Gi",
|
||||
operator: ">=",
|
||||
desired: "1Gi",
|
||||
actual: 2147483648,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "<= 1Gi, when actual is 1GB",
|
||||
operator: "<=",
|
||||
desired: "1Gi",
|
||||
actual: 1000000000,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "< 20Gi, when actual is 15Gi",
|
||||
operator: "<",
|
||||
desired: "20Gi",
|
||||
actual: 15 * 1024 * 1024 * 1024,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "< 20Gi, when actual is 20Gi",
|
||||
operator: "<",
|
||||
desired: "20Gi",
|
||||
actual: 20 * 1024 * 1024 * 1024,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "> 1073741824, when actual is 1024",
|
||||
operator: ">",
|
||||
desired: "1073741824",
|
||||
actual: 1024,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "= 4096, when actual is 4096",
|
||||
operator: "=",
|
||||
desired: "4096",
|
||||
actual: 4096,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "= 4096, when actual is 1024",
|
||||
operator: "=",
|
||||
desired: "4096",
|
||||
actual: 1024,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "= 4096, when actual is 5000",
|
||||
operator: "=",
|
||||
desired: "4096",
|
||||
actual: 5000,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
actual, err := doCompareHostDiskUsage(test.operator, test.desired, test.actual)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_doCompareHostDiskUsagePercent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
operator string
|
||||
desired string
|
||||
actual float64
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: ">= .20, when actual is .30",
|
||||
operator: ">=",
|
||||
desired: ".20",
|
||||
actual: .30,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: ">= .20, when actual is .10",
|
||||
operator: ">=",
|
||||
desired: ".20",
|
||||
actual: .10,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: ">= .20, when actual is .20",
|
||||
operator: ">=",
|
||||
desired: ".20",
|
||||
actual: .20,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "> .20, when actual is .30",
|
||||
operator: ">",
|
||||
desired: ".20",
|
||||
actual: .30,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "> .20, when actual is .10",
|
||||
operator: ">",
|
||||
desired: ".20",
|
||||
actual: .10,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "> .20, when actual is .20",
|
||||
operator: ">",
|
||||
desired: ".20",
|
||||
actual: .20,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "<= .20, when actual is .30",
|
||||
operator: "<=",
|
||||
desired: ".20",
|
||||
actual: .30,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "<= .20, when actual is .10",
|
||||
operator: "<=",
|
||||
desired: ".20",
|
||||
actual: .10,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "<= .20, when actual is .20",
|
||||
operator: "<=",
|
||||
desired: ".20",
|
||||
actual: .20,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "< .20, when actual is .30",
|
||||
operator: "<",
|
||||
desired: ".20",
|
||||
actual: .30,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "< .20, when actual is .10",
|
||||
operator: "<",
|
||||
desired: ".20",
|
||||
actual: .10,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "< .20, when actual is .20",
|
||||
operator: "<",
|
||||
desired: ".20",
|
||||
actual: .20,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "= .20, when actual is .30",
|
||||
operator: "=",
|
||||
desired: ".20",
|
||||
actual: .30,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "= .20, when actual is .10",
|
||||
operator: "=",
|
||||
desired: ".20",
|
||||
actual: .10,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "= .20, when actual is .20",
|
||||
operator: "=",
|
||||
desired: ".20",
|
||||
actual: .20,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "= 20%, when actual is .20",
|
||||
operator: "=",
|
||||
desired: "20%",
|
||||
actual: .20,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "= 20%, when actual is 20",
|
||||
operator: "=",
|
||||
desired: "20%",
|
||||
actual: 20,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
actual, err := doCompareHostDiskUsagePercent(test.operator, test.desired, test.actual)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeHostDiskUsage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
diskUsageInfo *collect.DiskUsageInfo
|
||||
hostAnalyzer *troubleshootv1beta2.DiskUsageAnalyze
|
||||
result *AnalyzeResult
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "Fail on insuffient total ephemeral disk space",
|
||||
diskUsageInfo: &collect.DiskUsageInfo{
|
||||
TotalBytes: 10 * 1024 * 1024 * 1024,
|
||||
UsedBytes: 5 * 1024 * 1024 * 1024,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.DiskUsageAnalyze{
|
||||
CollectorName: "ephemeral",
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "used/total >= 80%",
|
||||
Message: "/var/lib/kubelet is more than 80% full",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "total <= 10Gi",
|
||||
Message: "/var/lib/kubelet requires at least 10Gi",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Disk Usage ephemeral",
|
||||
IsFail: true,
|
||||
Message: "/var/lib/kubelet requires at least 10Gi",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Fail on insuffient available ephemeral disk space percentage",
|
||||
diskUsageInfo: &collect.DiskUsageInfo{
|
||||
TotalBytes: 10 * 1024 * 1024 * 1024,
|
||||
UsedBytes: 8 * 1024 * 1024 * 1024,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.DiskUsageAnalyze{
|
||||
CollectorName: "ephemeral",
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "total < 10Gi",
|
||||
Message: "/var/lib/kubelet requires at least 10Gi",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "used/total >= 80%",
|
||||
Message: "/var/lib/kubelet is more than 80% full",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Disk Usage ephemeral",
|
||||
IsFail: true,
|
||||
Message: "/var/lib/kubelet is more than 80% full",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Warn on high ephemeral disk space usage",
|
||||
diskUsageInfo: &collect.DiskUsageInfo{
|
||||
TotalBytes: 1024 * 1024 * 1024 * 1024,
|
||||
UsedBytes: 100 * 1024 * 1024 * 1024,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.DiskUsageAnalyze{
|
||||
CollectorName: "ephemeral",
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "total < 10Gi",
|
||||
Message: "/var/lib/kubelet requires at least 10Gi",
|
||||
},
|
||||
},
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "used/total >= 80%",
|
||||
Message: "/var/lib/kubelet is more than 80% full",
|
||||
},
|
||||
},
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "used >= 100Gi",
|
||||
Message: "/var/lib/kubelet has more than 100Gi used",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Disk Usage ephemeral",
|
||||
IsWarn: true,
|
||||
Message: "/var/lib/kubelet has more than 100Gi used",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Pass on ephemeral disk space available",
|
||||
diskUsageInfo: &collect.DiskUsageInfo{
|
||||
TotalBytes: 12 * 1024 * 1024 * 1024,
|
||||
UsedBytes: 1 * 1024 * 1024 * 1024,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.DiskUsageAnalyze{
|
||||
CollectorName: "ephemeral",
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "available > 10Gi",
|
||||
Message: "/var/lib/kubelet has at least 10Gi available",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Disk Usage ephemeral",
|
||||
IsPass: true,
|
||||
Message: "/var/lib/kubelet has at least 10Gi available",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
b, err := json.Marshal(test.diskUsageInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
getCollectedFileContents := func(filename string) ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
result, err := analyzeHostDiskUsage(test.hostAnalyzer, getCollectedFileContents)
|
||||
if test.expectErr {
|
||||
req.Error(err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.result, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
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"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
func analyzeHostMemory(hostAnalyzer *troubleshootv1beta2.MemoryAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
contents, err := getCollectedFileContents("system/memory.json")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get collected file")
|
||||
}
|
||||
|
||||
memoryInfo := collect.MemoryInfo{}
|
||||
if err := json.Unmarshal(contents, &memoryInfo); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal memory info")
|
||||
}
|
||||
|
||||
result := AnalyzeResult{}
|
||||
|
||||
title := hostAnalyzer.CheckName
|
||||
if title == "" {
|
||||
title = "Amount of Memory"
|
||||
}
|
||||
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 := compareHostMemoryConditionalToActual(outcome.Fail.When, memoryInfo.Total)
|
||||
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 := compareHostMemoryConditionalToActual(outcome.Warn.When, memoryInfo.Total)
|
||||
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 := compareHostMemoryConditionalToActual(outcome.Pass.When, memoryInfo.Total)
|
||||
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 compareHostMemoryConditionalToActual(conditional string, total uint64) (res bool, err error) {
|
||||
parts := strings.Split(conditional, " ")
|
||||
if len(parts) != 2 {
|
||||
return false, fmt.Errorf("Expected 2 parts in conditional, got %d", len(parts))
|
||||
}
|
||||
|
||||
operator := parts[0]
|
||||
desired := parts[1]
|
||||
quantity, err := resource.ParseQuantity(desired)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("could not parse quantity %q", desired)
|
||||
}
|
||||
desiredInt, ok := quantity.AsInt64()
|
||||
if !ok {
|
||||
return false, fmt.Errorf("could not parse quantity %q", desired)
|
||||
}
|
||||
|
||||
switch operator {
|
||||
case "<":
|
||||
return total < uint64(desiredInt), nil
|
||||
case "<=":
|
||||
return total <= uint64(desiredInt), nil
|
||||
case ">":
|
||||
return total > uint64(desiredInt), nil
|
||||
case ">=":
|
||||
return total >= uint64(desiredInt), nil
|
||||
case "=", "==", "===":
|
||||
return total == uint64(desiredInt), nil
|
||||
}
|
||||
|
||||
return false, errors.New("unknown operator")
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
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 Test_doCompareHostMemory(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
conditional string
|
||||
actual uint64
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "< 16Gi when actual is 8Gi",
|
||||
conditional: "< 16Gi",
|
||||
actual: 8 * 1024 * 1024 * 1024,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "< 8Gi when actual is 8Gi",
|
||||
conditional: "< 8Gi",
|
||||
actual: 8 * 1024 * 1024 * 1024,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "<= 8Gi when actual is 8Gi",
|
||||
conditional: "<= 8Gi",
|
||||
actual: 8 * 1024 * 1024 * 1024,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "<= 8Gi when actual is 16Gi",
|
||||
conditional: "<= 8Gi",
|
||||
actual: 16 * 1024 * 1024 * 1024,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "== 8Gi when actual is 16Gi",
|
||||
conditional: "== 8Gi",
|
||||
actual: 16 * 1024 * 1024 * 1024,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "== 8Gi when actual is 8Gi",
|
||||
conditional: "== 8Gi",
|
||||
actual: 8 * 1024 * 1024 * 1024,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "== 8000000000 when actual is 8000000000",
|
||||
conditional: "== 8000000000",
|
||||
actual: 8 * 1000 * 1000 * 1000,
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
|
||||
actual, err := compareHostMemoryConditionalToActual(test.conditional, test.actual)
|
||||
req.NoError(err)
|
||||
|
||||
assert.Equal(t, test.expected, actual)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeHostMemory(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
memoryInfo *collect.MemoryInfo
|
||||
hostAnalyzer *troubleshootv1beta2.MemoryAnalyze
|
||||
result *AnalyzeResult
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "Pass on memory available",
|
||||
memoryInfo: &collect.MemoryInfo{
|
||||
Total: 8 * 1024 * 1024 * 1024,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.MemoryAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Pass: &troubleshootv1beta2.SingleOutcome{
|
||||
When: ">= 4Gi",
|
||||
Message: "System has at least 4Gi of memory",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Amount of Memory",
|
||||
IsPass: true,
|
||||
Message: "System has at least 4Gi of memory",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Fail on memory available",
|
||||
memoryInfo: &collect.MemoryInfo{
|
||||
Total: 8 * 1024 * 1024 * 1024,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.MemoryAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "< 16Gi",
|
||||
Message: "System requires at least 16Gi of memory",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Amount of Memory",
|
||||
IsFail: true,
|
||||
Message: "System requires at least 16Gi of memory",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Warn on memory available",
|
||||
memoryInfo: &collect.MemoryInfo{
|
||||
Total: 8 * 1024 * 1024 * 1024,
|
||||
},
|
||||
hostAnalyzer: &troubleshootv1beta2.MemoryAnalyze{
|
||||
Outcomes: []*troubleshootv1beta2.Outcome{
|
||||
{
|
||||
Fail: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "< 4Gi",
|
||||
Message: "System requires at least 4Gi of memory",
|
||||
},
|
||||
},
|
||||
{
|
||||
Warn: &troubleshootv1beta2.SingleOutcome{
|
||||
When: "<= 8Gi",
|
||||
Message: "System performs best with more than 8Gi of memory",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
result: &AnalyzeResult{
|
||||
Title: "Amount of Memory",
|
||||
IsWarn: true,
|
||||
Message: "System performs best with more than 8Gi of memory",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := require.New(t)
|
||||
b, err := json.Marshal(test.memoryInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
getCollectedFileContents := func(filename string) ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
result, err := analyzeHostMemory(test.hostAnalyzer, getCollectedFileContents)
|
||||
if test.expectErr {
|
||||
req.Error(err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.result, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
)
|
||||
|
||||
func analyzeHostTCPLoadBalancer(hostAnalyzer *troubleshootv1beta2.TCPLoadBalancerAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
collectorName := hostAnalyzer.CollectorName
|
||||
if collectorName == "" {
|
||||
collectorName = "tcpLoadBalancer"
|
||||
}
|
||||
|
||||
fullPath := path.Join("tcpLoadBalancer", fmt.Sprintf("%s.json", collectorName))
|
||||
|
||||
collected, err := getCollectedFileContents(fullPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
|
||||
}
|
||||
actual := collect.NetworkStatusResult{}
|
||||
if err := json.Unmarshal(collected, &actual); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal collected")
|
||||
}
|
||||
|
||||
result := AnalyzeResult{}
|
||||
|
||||
title := hostAnalyzer.CheckName
|
||||
if title == "" {
|
||||
title = "TCP Load Balancer"
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if string(actual.Status) == outcome.Fail.When {
|
||||
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
|
||||
}
|
||||
|
||||
if string(actual.Status) == outcome.Warn.When {
|
||||
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
|
||||
}
|
||||
|
||||
if string(actual.Status) == outcome.Pass.When {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
)
|
||||
|
||||
func analyzeHostTCPPortStatus(hostAnalyzer *troubleshootv1beta2.TCPPortStatusAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
|
||||
fullPath := path.Join("tcpPortStatus", "tcpPortStatus.json")
|
||||
if hostAnalyzer.CollectorName != "" {
|
||||
fullPath = path.Join("tcpPortStatus", fmt.Sprintf("%s.json", hostAnalyzer.CollectorName))
|
||||
}
|
||||
|
||||
collected, err := getCollectedFileContents(fullPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read collected file name: %s", fullPath)
|
||||
}
|
||||
actual := collect.NetworkStatusResult{}
|
||||
if err := json.Unmarshal(collected, &actual); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal collected")
|
||||
}
|
||||
|
||||
result := AnalyzeResult{}
|
||||
|
||||
title := hostAnalyzer.CheckName
|
||||
if title == "" {
|
||||
title = "TCP Port Status"
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if string(actual.Status) == outcome.Fail.When {
|
||||
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
|
||||
}
|
||||
|
||||
if string(actual.Status) == outcome.Warn.When {
|
||||
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
|
||||
}
|
||||
|
||||
if string(actual.Status) == outcome.Pass.When {
|
||||
result.IsPass = true
|
||||
result.Message = outcome.Pass.Message
|
||||
result.URI = outcome.Pass.URI
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
@@ -4,18 +4,6 @@ import (
|
||||
"github.com/replicatedhq/troubleshoot/pkg/multitype"
|
||||
)
|
||||
|
||||
type SingleOutcome struct {
|
||||
When string `json:"when,omitempty" yaml:"when,omitempty"`
|
||||
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
||||
URI string `json:"uri,omitempty" yaml:"uri,omitempty"`
|
||||
}
|
||||
|
||||
type Outcome struct {
|
||||
Fail *SingleOutcome `json:"fail,omitempty" yaml:"fail,omitempty"`
|
||||
Warn *SingleOutcome `json:"warn,omitempty" yaml:"warn,omitempty"`
|
||||
Pass *SingleOutcome `json:"pass,omitempty" yaml:"pass,omitempty"`
|
||||
}
|
||||
|
||||
type ClusterVersion struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package v1beta2
|
||||
|
||||
type CPUAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type MemoryAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type TCPLoadBalancerAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type TCPPortStatusAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type DiskUsageAnalyze struct {
|
||||
AnalyzeMeta `json:",inline" yaml:",inline"`
|
||||
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
|
||||
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
|
||||
}
|
||||
|
||||
type HostAnalyze struct {
|
||||
CPU *CPUAnalyze `json:"cpu,omitempty" yaml:"cpu,omitempty"`
|
||||
//
|
||||
TCPLoadBalancer *TCPLoadBalancerAnalyze `json:"tcpLoadBalancer,omitempty" yaml:"tcpLoadBalancer,omitempty"`
|
||||
|
||||
DiskUsage *DiskUsageAnalyze `json:"diskUsage,omitempty" yaml:"diskUsage,omitempty"`
|
||||
|
||||
Memory *MemoryAnalyze `json:"memory,omitempty" yaml:"memory,omitempty"`
|
||||
|
||||
TCPPortStatus *TCPPortStatusAnalyze `json:"tcpPortStatus,omitempty" yaml:"tcpPortStatus,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"github.com/replicatedhq/troubleshoot/pkg/multitype"
|
||||
)
|
||||
|
||||
type HostCollectorMeta struct {
|
||||
CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"`
|
||||
// +optional
|
||||
Exclude multitype.BoolOrString `json:"exclude,omitempty" yaml:"exclude,omitempty"`
|
||||
}
|
||||
|
||||
type CPU struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
type Memory struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
type TCPLoadBalancer struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
Address string `json:"address"`
|
||||
Port int `json:"port"`
|
||||
Timeout string `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPLoadBalancer struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
Address string `json:"address"`
|
||||
Port int `json:"port"`
|
||||
Path string `json:"path"`
|
||||
Timeout string `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type TCPPortStatus struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
Interface string `json:"interface,omitempty"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
type Kubernetes struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
type IPV4Interfaces struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
type DiskUsage struct {
|
||||
HostCollectorMeta `json:",inline" yaml:",inline"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type HostCollect struct {
|
||||
CPU *CPU `json:"cpu,omitempty" yaml:"cpu,omitempty"`
|
||||
Memory *Memory `json:"memory,omitempty" yaml:"memory,omitempty"`
|
||||
TCPLoadBalancer *TCPLoadBalancer `json:"tcpLoadBalancer,omitempty" yaml:"tcpLoadBalancer,omitempty"`
|
||||
HTTPLoadBalancer *HTTPLoadBalancer `json:"httpLoadBalancer,omitempty" yaml:"httpLoadBalancer,omitempty"`
|
||||
TCPPortStatus *TCPPortStatus `json:"tcpPortStatus,omitempty" yaml:"tcpPortStatus,omitempty"`
|
||||
Kubernetes *Kubernetes `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty"`
|
||||
IPV4Interfaces *IPV4Interfaces `json:"ipv4Interfaces,omitempty" yaml:"ipv4Interfaces,omitempty"`
|
||||
DiskUsage *DiskUsage `json:"diskUsage,omitempty" yaml:"diskUsage,omitempty"`
|
||||
}
|
||||
|
||||
func (c *HostCollect) GetName() string {
|
||||
var collector string
|
||||
if c.CPU != nil {
|
||||
collector = "cpu"
|
||||
}
|
||||
if c.Memory != nil {
|
||||
collector = "memory"
|
||||
}
|
||||
|
||||
if collector == "" {
|
||||
return "<none>"
|
||||
}
|
||||
|
||||
return collector
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2019 Replicated, Inc..
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// HostPreflightSpec defines the desired state of HostPreflight
|
||||
type HostPreflightSpec struct {
|
||||
Collectors []*HostCollect `json:"collectors,omitempty" yaml:"collectors,omitempty"`
|
||||
Analyzers []*HostAnalyze `json:"analyzers,omitempty" yaml:"analyzers,omitempty"`
|
||||
}
|
||||
|
||||
// HostPreflightStatus defines the observed state of HostPreflight
|
||||
type HostPreflightStatus struct {
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// HostPreflight is the Schema for the hostpreflights API
|
||||
// +k8s:openapi-gen=true
|
||||
type HostPreflight struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
|
||||
Spec HostPreflightSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
Status HostPreflightStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// HostPreflightList contains a list of HostPreflight
|
||||
type HostPreflightList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []HostPreflight `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&HostPreflight{}, &HostPreflightList{})
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package v1beta2
|
||||
|
||||
type SingleOutcome struct {
|
||||
When string `json:"when,omitempty" yaml:"when,omitempty"`
|
||||
Message string `json:"message,omitempty" yaml:"message,omitempty"`
|
||||
URI string `json:"uri,omitempty" yaml:"uri,omitempty"`
|
||||
}
|
||||
|
||||
type Outcome struct {
|
||||
Fail *SingleOutcome `json:"fail,omitempty" yaml:"fail,omitempty"`
|
||||
Warn *SingleOutcome `json:"warn,omitempty" yaml:"warn,omitempty"`
|
||||
Pass *SingleOutcome `json:"pass,omitempty" yaml:"pass,omitempty"`
|
||||
}
|
||||
@@ -287,6 +287,49 @@ func (in *AnalyzerStatus) DeepCopy() *AnalyzerStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CPU) DeepCopyInto(out *CPU) {
|
||||
*out = *in
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CPU.
|
||||
func (in *CPU) DeepCopy() *CPU {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CPU)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CPUAnalyze) DeepCopyInto(out *CPUAnalyze) {
|
||||
*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 CPUAnalyze.
|
||||
func (in *CPUAnalyze) DeepCopy() *CPUAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CPUAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Ceph) DeepCopyInto(out *Ceph) {
|
||||
*out = *in
|
||||
@@ -810,6 +853,49 @@ func (in *DeploymentStatus) DeepCopy() *DeploymentStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DiskUsage) DeepCopyInto(out *DiskUsage) {
|
||||
*out = *in
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiskUsage.
|
||||
func (in *DiskUsage) DeepCopy() *DiskUsage {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DiskUsage)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DiskUsageAnalyze) DeepCopyInto(out *DiskUsageAnalyze) {
|
||||
*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 DiskUsageAnalyze.
|
||||
func (in *DiskUsageAnalyze) DeepCopy() *DiskUsageAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DiskUsageAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Distribution) DeepCopyInto(out *Distribution) {
|
||||
*out = *in
|
||||
@@ -941,6 +1027,260 @@ func (in *HTTP) DeepCopy() *HTTP {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPLoadBalancer) DeepCopyInto(out *HTTPLoadBalancer) {
|
||||
*out = *in
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPLoadBalancer.
|
||||
func (in *HTTPLoadBalancer) DeepCopy() *HTTPLoadBalancer {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPLoadBalancer)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HostAnalyze) DeepCopyInto(out *HostAnalyze) {
|
||||
*out = *in
|
||||
if in.CPU != nil {
|
||||
in, out := &in.CPU, &out.CPU
|
||||
*out = new(CPUAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.TCPLoadBalancer != nil {
|
||||
in, out := &in.TCPLoadBalancer, &out.TCPLoadBalancer
|
||||
*out = new(TCPLoadBalancerAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.DiskUsage != nil {
|
||||
in, out := &in.DiskUsage, &out.DiskUsage
|
||||
*out = new(DiskUsageAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Memory != nil {
|
||||
in, out := &in.Memory, &out.Memory
|
||||
*out = new(MemoryAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.TCPPortStatus != nil {
|
||||
in, out := &in.TCPPortStatus, &out.TCPPortStatus
|
||||
*out = new(TCPPortStatusAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostAnalyze.
|
||||
func (in *HostAnalyze) DeepCopy() *HostAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HostAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HostCollect) DeepCopyInto(out *HostCollect) {
|
||||
*out = *in
|
||||
if in.CPU != nil {
|
||||
in, out := &in.CPU, &out.CPU
|
||||
*out = new(CPU)
|
||||
**out = **in
|
||||
}
|
||||
if in.Memory != nil {
|
||||
in, out := &in.Memory, &out.Memory
|
||||
*out = new(Memory)
|
||||
**out = **in
|
||||
}
|
||||
if in.TCPLoadBalancer != nil {
|
||||
in, out := &in.TCPLoadBalancer, &out.TCPLoadBalancer
|
||||
*out = new(TCPLoadBalancer)
|
||||
**out = **in
|
||||
}
|
||||
if in.HTTPLoadBalancer != nil {
|
||||
in, out := &in.HTTPLoadBalancer, &out.HTTPLoadBalancer
|
||||
*out = new(HTTPLoadBalancer)
|
||||
**out = **in
|
||||
}
|
||||
if in.TCPPortStatus != nil {
|
||||
in, out := &in.TCPPortStatus, &out.TCPPortStatus
|
||||
*out = new(TCPPortStatus)
|
||||
**out = **in
|
||||
}
|
||||
if in.Kubernetes != nil {
|
||||
in, out := &in.Kubernetes, &out.Kubernetes
|
||||
*out = new(Kubernetes)
|
||||
**out = **in
|
||||
}
|
||||
if in.IPV4Interfaces != nil {
|
||||
in, out := &in.IPV4Interfaces, &out.IPV4Interfaces
|
||||
*out = new(IPV4Interfaces)
|
||||
**out = **in
|
||||
}
|
||||
if in.DiskUsage != nil {
|
||||
in, out := &in.DiskUsage, &out.DiskUsage
|
||||
*out = new(DiskUsage)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostCollect.
|
||||
func (in *HostCollect) DeepCopy() *HostCollect {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HostCollect)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HostCollectorMeta) DeepCopyInto(out *HostCollectorMeta) {
|
||||
*out = *in
|
||||
out.Exclude = in.Exclude
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostCollectorMeta.
|
||||
func (in *HostCollectorMeta) DeepCopy() *HostCollectorMeta {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HostCollectorMeta)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HostPreflight) DeepCopyInto(out *HostPreflight) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostPreflight.
|
||||
func (in *HostPreflight) DeepCopy() *HostPreflight {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HostPreflight)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HostPreflight) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HostPreflightList) DeepCopyInto(out *HostPreflightList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]HostPreflight, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostPreflightList.
|
||||
func (in *HostPreflightList) DeepCopy() *HostPreflightList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HostPreflightList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HostPreflightList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HostPreflightSpec) DeepCopyInto(out *HostPreflightSpec) {
|
||||
*out = *in
|
||||
if in.Collectors != nil {
|
||||
in, out := &in.Collectors, &out.Collectors
|
||||
*out = make([]*HostCollect, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(HostCollect)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.Analyzers != nil {
|
||||
in, out := &in.Analyzers, &out.Analyzers
|
||||
*out = make([]*HostAnalyze, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(HostAnalyze)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostPreflightSpec.
|
||||
func (in *HostPreflightSpec) DeepCopy() *HostPreflightSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HostPreflightSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HostPreflightStatus) DeepCopyInto(out *HostPreflightStatus) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostPreflightStatus.
|
||||
func (in *HostPreflightStatus) DeepCopy() *HostPreflightStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HostPreflightStatus)
|
||||
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
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPV4Interfaces.
|
||||
func (in *IPV4Interfaces) DeepCopy() *IPV4Interfaces {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(IPV4Interfaces)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImagePullSecret) DeepCopyInto(out *ImagePullSecret) {
|
||||
*out = *in
|
||||
@@ -1017,6 +1357,22 @@ func (in *Ingress) DeepCopy() *Ingress {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Kubernetes) DeepCopyInto(out *Kubernetes) {
|
||||
*out = *in
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Kubernetes.
|
||||
func (in *Kubernetes) DeepCopy() *Kubernetes {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Kubernetes)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LogLimits) DeepCopyInto(out *LogLimits) {
|
||||
*out = *in
|
||||
@@ -1064,6 +1420,49 @@ func (in *Logs) DeepCopy() *Logs {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Memory) DeepCopyInto(out *Memory) {
|
||||
*out = *in
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Memory.
|
||||
func (in *Memory) DeepCopy() *Memory {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Memory)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MemoryAnalyze) DeepCopyInto(out *MemoryAnalyze) {
|
||||
*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 MemoryAnalyze.
|
||||
func (in *MemoryAnalyze) DeepCopy() *MemoryAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MemoryAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeResourceFilters) DeepCopyInto(out *NodeResourceFilters) {
|
||||
*out = *in
|
||||
@@ -1769,6 +2168,92 @@ func (in *SupportBundleVersionSpec) DeepCopy() *SupportBundleVersionSpec {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TCPLoadBalancer) DeepCopyInto(out *TCPLoadBalancer) {
|
||||
*out = *in
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPLoadBalancer.
|
||||
func (in *TCPLoadBalancer) DeepCopy() *TCPLoadBalancer {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TCPLoadBalancer)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TCPLoadBalancerAnalyze) DeepCopyInto(out *TCPLoadBalancerAnalyze) {
|
||||
*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 TCPLoadBalancerAnalyze.
|
||||
func (in *TCPLoadBalancerAnalyze) DeepCopy() *TCPLoadBalancerAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TCPLoadBalancerAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TCPPortStatus) DeepCopyInto(out *TCPPortStatus) {
|
||||
*out = *in
|
||||
out.HostCollectorMeta = in.HostCollectorMeta
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPPortStatus.
|
||||
func (in *TCPPortStatus) DeepCopy() *TCPPortStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TCPPortStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TCPPortStatusAnalyze) DeepCopyInto(out *TCPPortStatusAnalyze) {
|
||||
*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 TCPPortStatusAnalyze.
|
||||
func (in *TCPPortStatusAnalyze) DeepCopy() *TCPPortStatusAnalyze {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TCPPortStatusAnalyze)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TextAnalyze) DeepCopyInto(out *TextAnalyze) {
|
||||
*out = *in
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2019 Replicated, Inc..
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
// FakeHostPreflights implements HostPreflightInterface
|
||||
type FakeHostPreflights struct {
|
||||
Fake *FakeTroubleshootV1beta2
|
||||
ns string
|
||||
}
|
||||
|
||||
var hostpreflightsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "hostpreflights"}
|
||||
|
||||
var hostpreflightsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "HostPreflight"}
|
||||
|
||||
// Get takes name of the hostPreflight, and returns the corresponding hostPreflight object, and an error if there is any.
|
||||
func (c *FakeHostPreflights) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.HostPreflight, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewGetAction(hostpreflightsResource, c.ns, name), &v1beta2.HostPreflight{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.HostPreflight), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of HostPreflights that match those selectors.
|
||||
func (c *FakeHostPreflights) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.HostPreflightList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewListAction(hostpreflightsResource, hostpreflightsKind, c.ns, opts), &v1beta2.HostPreflightList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1beta2.HostPreflightList{ListMeta: obj.(*v1beta2.HostPreflightList).ListMeta}
|
||||
for _, item := range obj.(*v1beta2.HostPreflightList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested hostPreflights.
|
||||
func (c *FakeHostPreflights) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewWatchAction(hostpreflightsResource, c.ns, opts))
|
||||
|
||||
}
|
||||
|
||||
// Create takes the representation of a hostPreflight and creates it. Returns the server's representation of the hostPreflight, and an error, if there is any.
|
||||
func (c *FakeHostPreflights) Create(ctx context.Context, hostPreflight *v1beta2.HostPreflight, opts v1.CreateOptions) (result *v1beta2.HostPreflight, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewCreateAction(hostpreflightsResource, c.ns, hostPreflight), &v1beta2.HostPreflight{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.HostPreflight), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a hostPreflight and updates it. Returns the server's representation of the hostPreflight, and an error, if there is any.
|
||||
func (c *FakeHostPreflights) Update(ctx context.Context, hostPreflight *v1beta2.HostPreflight, opts v1.UpdateOptions) (result *v1beta2.HostPreflight, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateAction(hostpreflightsResource, c.ns, hostPreflight), &v1beta2.HostPreflight{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.HostPreflight), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakeHostPreflights) UpdateStatus(ctx context.Context, hostPreflight *v1beta2.HostPreflight, opts v1.UpdateOptions) (*v1beta2.HostPreflight, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateSubresourceAction(hostpreflightsResource, "status", c.ns, hostPreflight), &v1beta2.HostPreflight{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.HostPreflight), err
|
||||
}
|
||||
|
||||
// Delete takes name of the hostPreflight and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeHostPreflights) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(hostpreflightsResource, c.ns, name), &v1beta2.HostPreflight{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeHostPreflights) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
action := testing.NewDeleteCollectionAction(hostpreflightsResource, c.ns, listOpts)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1beta2.HostPreflightList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched hostPreflight.
|
||||
func (c *FakeHostPreflights) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.HostPreflight, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(hostpreflightsResource, c.ns, name, pt, data, subresources...), &v1beta2.HostPreflight{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1beta2.HostPreflight), err
|
||||
}
|
||||
+4
@@ -35,6 +35,10 @@ func (c *FakeTroubleshootV1beta2) Collectors(namespace string) v1beta2.Collector
|
||||
return &FakeCollectors{c, namespace}
|
||||
}
|
||||
|
||||
func (c *FakeTroubleshootV1beta2) HostPreflights(namespace string) v1beta2.HostPreflightInterface {
|
||||
return &FakeHostPreflights{c, namespace}
|
||||
}
|
||||
|
||||
func (c *FakeTroubleshootV1beta2) Preflights(namespace string) v1beta2.PreflightInterface {
|
||||
return &FakePreflights{c, namespace}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ type AnalyzerExpansion interface{}
|
||||
|
||||
type CollectorExpansion interface{}
|
||||
|
||||
type HostPreflightExpansion interface{}
|
||||
|
||||
type PreflightExpansion interface{}
|
||||
|
||||
type RedactorExpansion interface{}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
Copyright 2019 Replicated, Inc..
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
scheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
rest "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// HostPreflightsGetter has a method to return a HostPreflightInterface.
|
||||
// A group's client should implement this interface.
|
||||
type HostPreflightsGetter interface {
|
||||
HostPreflights(namespace string) HostPreflightInterface
|
||||
}
|
||||
|
||||
// HostPreflightInterface has methods to work with HostPreflight resources.
|
||||
type HostPreflightInterface interface {
|
||||
Create(ctx context.Context, hostPreflight *v1beta2.HostPreflight, opts v1.CreateOptions) (*v1beta2.HostPreflight, error)
|
||||
Update(ctx context.Context, hostPreflight *v1beta2.HostPreflight, opts v1.UpdateOptions) (*v1beta2.HostPreflight, error)
|
||||
UpdateStatus(ctx context.Context, hostPreflight *v1beta2.HostPreflight, opts v1.UpdateOptions) (*v1beta2.HostPreflight, error)
|
||||
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
|
||||
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
|
||||
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta2.HostPreflight, error)
|
||||
List(ctx context.Context, opts v1.ListOptions) (*v1beta2.HostPreflightList, error)
|
||||
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
|
||||
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.HostPreflight, err error)
|
||||
HostPreflightExpansion
|
||||
}
|
||||
|
||||
// hostPreflights implements HostPreflightInterface
|
||||
type hostPreflights struct {
|
||||
client rest.Interface
|
||||
ns string
|
||||
}
|
||||
|
||||
// newHostPreflights returns a HostPreflights
|
||||
func newHostPreflights(c *TroubleshootV1beta2Client, namespace string) *hostPreflights {
|
||||
return &hostPreflights{
|
||||
client: c.RESTClient(),
|
||||
ns: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// Get takes name of the hostPreflight, and returns the corresponding hostPreflight object, and an error if there is any.
|
||||
func (c *hostPreflights) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.HostPreflight, err error) {
|
||||
result = &v1beta2.HostPreflight{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("hostpreflights").
|
||||
Name(name).
|
||||
VersionedParams(&options, scheme.ParameterCodec).
|
||||
Do(ctx).
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of HostPreflights that match those selectors.
|
||||
func (c *hostPreflights) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.HostPreflightList, err error) {
|
||||
var timeout time.Duration
|
||||
if opts.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||
}
|
||||
result = &v1beta2.HostPreflightList{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("hostpreflights").
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Timeout(timeout).
|
||||
Do(ctx).
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested hostPreflights.
|
||||
func (c *hostPreflights) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||
var timeout time.Duration
|
||||
if opts.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||
}
|
||||
opts.Watch = true
|
||||
return c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("hostpreflights").
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Timeout(timeout).
|
||||
Watch(ctx)
|
||||
}
|
||||
|
||||
// Create takes the representation of a hostPreflight and creates it. Returns the server's representation of the hostPreflight, and an error, if there is any.
|
||||
func (c *hostPreflights) Create(ctx context.Context, hostPreflight *v1beta2.HostPreflight, opts v1.CreateOptions) (result *v1beta2.HostPreflight, err error) {
|
||||
result = &v1beta2.HostPreflight{}
|
||||
err = c.client.Post().
|
||||
Namespace(c.ns).
|
||||
Resource("hostpreflights").
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Body(hostPreflight).
|
||||
Do(ctx).
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a hostPreflight and updates it. Returns the server's representation of the hostPreflight, and an error, if there is any.
|
||||
func (c *hostPreflights) Update(ctx context.Context, hostPreflight *v1beta2.HostPreflight, opts v1.UpdateOptions) (result *v1beta2.HostPreflight, err error) {
|
||||
result = &v1beta2.HostPreflight{}
|
||||
err = c.client.Put().
|
||||
Namespace(c.ns).
|
||||
Resource("hostpreflights").
|
||||
Name(hostPreflight.Name).
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Body(hostPreflight).
|
||||
Do(ctx).
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *hostPreflights) UpdateStatus(ctx context.Context, hostPreflight *v1beta2.HostPreflight, opts v1.UpdateOptions) (result *v1beta2.HostPreflight, err error) {
|
||||
result = &v1beta2.HostPreflight{}
|
||||
err = c.client.Put().
|
||||
Namespace(c.ns).
|
||||
Resource("hostpreflights").
|
||||
Name(hostPreflight.Name).
|
||||
SubResource("status").
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Body(hostPreflight).
|
||||
Do(ctx).
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes name of the hostPreflight and deletes it. Returns an error if one occurs.
|
||||
func (c *hostPreflights) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("hostpreflights").
|
||||
Name(name).
|
||||
Body(&opts).
|
||||
Do(ctx).
|
||||
Error()
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *hostPreflights) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||
var timeout time.Duration
|
||||
if listOpts.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
|
||||
}
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("hostpreflights").
|
||||
VersionedParams(&listOpts, scheme.ParameterCodec).
|
||||
Timeout(timeout).
|
||||
Body(&opts).
|
||||
Do(ctx).
|
||||
Error()
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched hostPreflight.
|
||||
func (c *hostPreflights) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.HostPreflight, err error) {
|
||||
result = &v1beta2.HostPreflight{}
|
||||
err = c.client.Patch(pt).
|
||||
Namespace(c.ns).
|
||||
Resource("hostpreflights").
|
||||
Name(name).
|
||||
SubResource(subresources...).
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Body(data).
|
||||
Do(ctx).
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
@@ -27,6 +27,7 @@ type TroubleshootV1beta2Interface interface {
|
||||
RESTClient() rest.Interface
|
||||
AnalyzersGetter
|
||||
CollectorsGetter
|
||||
HostPreflightsGetter
|
||||
PreflightsGetter
|
||||
RedactorsGetter
|
||||
SupportBundlesGetter
|
||||
@@ -45,6 +46,10 @@ func (c *TroubleshootV1beta2Client) Collectors(namespace string) CollectorInterf
|
||||
return newCollectors(c, namespace)
|
||||
}
|
||||
|
||||
func (c *TroubleshootV1beta2Client) HostPreflights(namespace string) HostPreflightInterface {
|
||||
return newHostPreflights(c, namespace)
|
||||
}
|
||||
|
||||
func (c *TroubleshootV1beta2Client) Preflights(namespace string) PreflightInterface {
|
||||
return newPreflights(c, namespace)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
)
|
||||
|
||||
type HostCollector struct {
|
||||
Collect *troubleshootv1beta2.HostCollect
|
||||
}
|
||||
|
||||
type HostCollectors []*HostCollector
|
||||
|
||||
func (c *HostCollector) RunCollectorSync() (result map[string][]byte, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.Errorf("recovered rom panic: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
if c.Collect.CPU != nil {
|
||||
result, err = HostCPU(c)
|
||||
} else if c.Collect.Memory != nil {
|
||||
result, err = HostMemory(c)
|
||||
} else if c.Collect.TCPLoadBalancer != nil {
|
||||
result, err = HostTCPLoadBalancer(c)
|
||||
} else if c.Collect.DiskUsage != nil {
|
||||
result, err = HostDiskUsage(c)
|
||||
} else if c.Collect.TCPPortStatus != nil {
|
||||
result, err = HostTCPPortStatus(c)
|
||||
} else {
|
||||
err = errors.New("no spec found to run")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *HostCollector) GetDisplayName() string {
|
||||
return c.Collect.GetName()
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
)
|
||||
|
||||
type CPUInfo struct {
|
||||
LogicalCount int `json:"logicalCount"`
|
||||
PhysicalCount int `json:"physicalCount"`
|
||||
}
|
||||
|
||||
func HostCPU(c *HostCollector) (map[string][]byte, error) {
|
||||
cpuInfo := CPUInfo{}
|
||||
|
||||
logicalCount, err := cpu.Counts(true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to count logical cpus")
|
||||
}
|
||||
cpuInfo.LogicalCount = logicalCount
|
||||
|
||||
physicalCount, err := cpu.Counts(false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to count physical cpus")
|
||||
}
|
||||
cpuInfo.PhysicalCount = physicalCount
|
||||
|
||||
b, err := json.Marshal(cpuInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal cpu info")
|
||||
}
|
||||
|
||||
return map[string][]byte{
|
||||
"system/cpu.json": b,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
)
|
||||
|
||||
type DiskUsageInfo struct {
|
||||
TotalBytes uint64 `json:"total_bytes"`
|
||||
UsedBytes uint64 `json:"used_bytes"`
|
||||
}
|
||||
|
||||
func HostDiskUsage(c *HostCollector) (map[string][]byte, error) {
|
||||
result := map[string][]byte{}
|
||||
|
||||
if c.Collect.DiskUsage == nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
du, err := disk.Usage(c.Collect.DiskUsage.Path)
|
||||
if err != nil {
|
||||
return result, errors.Wrapf(err, "collect disk usage for %s", c.Collect.DiskUsage.Path)
|
||||
}
|
||||
diskSpaceInfo := DiskUsageInfo{
|
||||
TotalBytes: du.Total,
|
||||
UsedBytes: du.Used,
|
||||
}
|
||||
b, err := json.Marshal(diskSpaceInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal disk space info")
|
||||
}
|
||||
key := HostDiskUsageKey(c.Collect.DiskUsage.CollectorName)
|
||||
result[key] = b
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func HostDiskUsageKey(name string) string {
|
||||
return fmt.Sprintf("diskUsage/%s.json", name)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
)
|
||||
|
||||
type MemoryInfo struct {
|
||||
Total uint64 `json:"total"`
|
||||
}
|
||||
|
||||
func HostMemory(c *HostCollector) (map[string][]byte, error) {
|
||||
memoryInfo := MemoryInfo{}
|
||||
|
||||
vmstat, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read virtual memory")
|
||||
}
|
||||
memoryInfo.Total = vmstat.Available
|
||||
|
||||
b, err := json.Marshal(memoryInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal memory info")
|
||||
}
|
||||
|
||||
return map[string][]byte{
|
||||
"system/memory.json": b,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/segmentio/ksuid"
|
||||
)
|
||||
|
||||
type NetworkStatus string
|
||||
|
||||
const (
|
||||
NetworkStatusAddressInUse = "address-in-use"
|
||||
NetworkStatusConnectionRefused = "connection-refused"
|
||||
NetworkStatusConnectionTimeout = "connection-timeout"
|
||||
NetworkStatusConnected = "connected"
|
||||
NetworkStatusErrorOther = "error"
|
||||
)
|
||||
|
||||
type NetworkStatusResult struct {
|
||||
Status NetworkStatus `json:"status"`
|
||||
}
|
||||
|
||||
func checkTCPConnection(listenAddress string, dialAddress string, timeout time.Duration) (NetworkStatus, error) {
|
||||
lstn, err := net.Listen("tcp", listenAddress)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "address already in use") {
|
||||
return NetworkStatusAddressInUse, nil
|
||||
}
|
||||
|
||||
return NetworkStatusErrorOther, errors.Wrap(err, "failed to create listener")
|
||||
}
|
||||
defer lstn.Close()
|
||||
|
||||
// The server may receive requests from other clients and the client request may be forwarded to
|
||||
// other servers. The client must continue to initiate new connections and send its request
|
||||
// token until the server responds with its token.
|
||||
requestToken := ksuid.New().Bytes()
|
||||
responseToken := ksuid.New().Bytes()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := lstn.Accept()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if handleTestConnection(conn, requestToken, responseToken) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
stopAfter := time.Now().Add(timeout)
|
||||
|
||||
for {
|
||||
if time.Now().After(stopAfter) {
|
||||
return NetworkStatusConnectionTimeout, nil
|
||||
}
|
||||
|
||||
conn, err := net.DialTimeout("tcp", dialAddress, 50*time.Millisecond)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "i/o timeout") {
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
continue
|
||||
}
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return NetworkStatusConnectionRefused, nil
|
||||
}
|
||||
return NetworkStatusErrorOther, errors.Wrap(err, "failed to dial")
|
||||
}
|
||||
|
||||
if verifyConnectionToServer(conn, requestToken, responseToken) {
|
||||
return NetworkStatusConnected, nil
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
}
|
||||
}
|
||||
|
||||
func handleTestConnection(conn net.Conn, requestToken []byte, responseToken []byte) bool {
|
||||
defer func() {
|
||||
if err := conn.Close(); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if err := conn.SetReadDeadline(time.Now().Add(50 * time.Millisecond)); err != nil {
|
||||
fmt.Printf("Server failed to set read deadline: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
_, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
fmt.Printf("Server failed to read: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !bytes.Contains(buf, requestToken) {
|
||||
return false
|
||||
}
|
||||
|
||||
if err := conn.SetWriteDeadline(time.Now().Add(50 * time.Millisecond)); err != nil {
|
||||
fmt.Printf("Server failed to set write deadline: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err := conn.Write(responseToken); err != nil {
|
||||
fmt.Printf("Server failed to write: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func verifyConnectionToServer(conn net.Conn, requestToken []byte, responseToken []byte) bool {
|
||||
defer func() {
|
||||
if err := conn.Close(); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if err := conn.SetWriteDeadline(time.Now().Add(50 * time.Millisecond)); err != nil {
|
||||
fmt.Printf("Client failed to set write deadline: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
_, err := conn.Write(requestToken)
|
||||
if err != nil {
|
||||
fmt.Printf("Client failed to write: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if err := conn.SetReadDeadline(time.Now().Add(50 * time.Millisecond)); err != nil {
|
||||
fmt.Printf("Client failed to set read deadline: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
_, err = conn.Read(buf)
|
||||
if err != nil {
|
||||
fmt.Printf("Client failed to read: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !bytes.Contains(buf, responseToken) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type ConnectionResult int
|
||||
|
||||
const (
|
||||
ConnectionRefused ConnectionResult = iota
|
||||
Connected
|
||||
ConnectionTimeout
|
||||
ConnectionAddressInUse
|
||||
ErrorOther
|
||||
)
|
||||
|
||||
func HostTCPLoadBalancer(c *HostCollector) (map[string][]byte, error) {
|
||||
listenAddress := fmt.Sprintf("0.0.0.0:%d", c.Collect.TCPLoadBalancer.Port)
|
||||
dialAddress := c.Collect.TCPLoadBalancer.Address
|
||||
|
||||
timeout := 60 * time.Minute
|
||||
if c.Collect.TCPLoadBalancer.Timeout != "" {
|
||||
var err error
|
||||
timeout, err = time.ParseDuration(c.Collect.TCPLoadBalancer.Timeout)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse durection")
|
||||
}
|
||||
}
|
||||
|
||||
networkStatus, err := checkTCPConnection(listenAddress, dialAddress, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := NetworkStatusResult{
|
||||
Status: networkStatus,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal result")
|
||||
}
|
||||
|
||||
name := path.Join("tcpLoadBalancer", "tcpLoadBalancer.json")
|
||||
if c.Collect.TCPLoadBalancer.CollectorName != "" {
|
||||
name = path.Join("tcpLoadBalancer", fmt.Sprintf("%s.json", c.Collect.TCPLoadBalancer.CollectorName))
|
||||
}
|
||||
|
||||
return map[string][]byte{
|
||||
name: b,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func HostTCPPortStatus(c *HostCollector) (map[string][]byte, error) {
|
||||
dialAddress := ""
|
||||
listenAddress := fmt.Sprintf("0.0.0.0:%d", c.Collect.TCPPortStatus.Port)
|
||||
|
||||
if c.Collect.TCPPortStatus.Interface != "" {
|
||||
iface, err := net.InterfaceByName(c.Collect.TCPPortStatus.Interface)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "lookup interface %s", c.Collect.TCPPortStatus.Interface)
|
||||
}
|
||||
ip, err := getIPv4FromInterface(iface)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get ipv4 address for interface %s", c.Collect.TCPPortStatus.Interface)
|
||||
}
|
||||
listenAddress = fmt.Sprintf("%s:%d", ip, c.Collect.TCPPortStatus.Port)
|
||||
dialAddress = listenAddress
|
||||
}
|
||||
|
||||
if dialAddress == "" {
|
||||
ip, err := getLocalIPv4()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dialAddress = fmt.Sprintf("%s:%d", ip, c.Collect.TCPPortStatus.Port)
|
||||
}
|
||||
|
||||
networkStatus, err := checkTCPConnection(listenAddress, dialAddress, 10*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := NetworkStatusResult{
|
||||
Status: networkStatus,
|
||||
}
|
||||
b, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal result")
|
||||
}
|
||||
|
||||
name := path.Join("tcpPortStatus", "tcpPortStatus.json")
|
||||
if c.Collect.TCPPortStatus.CollectorName != "" {
|
||||
name = path.Join("tcpPortStatus", fmt.Sprintf("%s.json", c.Collect.TCPPortStatus.CollectorName))
|
||||
}
|
||||
return map[string][]byte{
|
||||
name: b,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getIPv4FromInterface(iface *net.Interface) (net.IP, error) {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "list interface addresses")
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
ip, _, err := net.ParseCIDR(addr.String())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "parse interface address %q", addr.String())
|
||||
}
|
||||
ip = ip.To4()
|
||||
if ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("interface does not have an ipv4 address")
|
||||
}
|
||||
|
||||
func getLocalIPv4() (net.IP, error) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "list host network interfaces")
|
||||
}
|
||||
|
||||
for _, iface := range interfaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue
|
||||
}
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
continue
|
||||
}
|
||||
ip, _ := getIPv4FromInterface(&iface)
|
||||
if ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("No network interface has an IPv4 address")
|
||||
}
|
||||
@@ -6,12 +6,22 @@ import (
|
||||
"strings"
|
||||
|
||||
analyze "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
)
|
||||
|
||||
// Analyze runs the analyze phase of preflight checks
|
||||
func (c CollectResult) Analyze() []*analyze.AnalyzeResult {
|
||||
func (c ClusterCollectResult) Analyze() []*analyze.AnalyzeResult {
|
||||
return doAnalyze(c.AllCollectedData, c.Spec.Spec.Analyzers, nil)
|
||||
}
|
||||
|
||||
// Analyze runs the analysze phase of host preflight checks
|
||||
func (c HostCollectResult) Analyze() []*analyze.AnalyzeResult {
|
||||
return doAnalyze(c.AllCollectedData, nil, c.Spec.Spec.Analyzers)
|
||||
}
|
||||
|
||||
func doAnalyze(allCollectedData map[string][]byte, analyzers []*troubleshootv1beta2.Analyze, hostAnalyzers []*troubleshootv1beta2.HostAnalyze) []*analyze.AnalyzeResult {
|
||||
getCollectedFileContents := func(fileName string) ([]byte, error) {
|
||||
contents, ok := c.AllCollectedData[fileName]
|
||||
contents, ok := allCollectedData[fileName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("file %s was not collected", fileName)
|
||||
}
|
||||
@@ -20,13 +30,13 @@ func (c CollectResult) Analyze() []*analyze.AnalyzeResult {
|
||||
}
|
||||
getChildCollectedFileContents := func(prefix string) (map[string][]byte, error) {
|
||||
matching := make(map[string][]byte)
|
||||
for k, v := range c.AllCollectedData {
|
||||
for k, v := range allCollectedData {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
matching[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range c.AllCollectedData {
|
||||
for k, v := range allCollectedData {
|
||||
if ok, _ := filepath.Match(prefix, k); ok {
|
||||
matching[k] = v
|
||||
}
|
||||
@@ -36,7 +46,7 @@ func (c CollectResult) Analyze() []*analyze.AnalyzeResult {
|
||||
}
|
||||
|
||||
analyzeResults := []*analyze.AnalyzeResult{}
|
||||
for _, analyzer := range c.Spec.Spec.Analyzers {
|
||||
for _, analyzer := range analyzers {
|
||||
analyzeResult, err := analyze.Analyze(analyzer, getCollectedFileContents, getChildCollectedFileContents)
|
||||
if err != nil {
|
||||
analyzeResult = []*analyze.AnalyzeResult{
|
||||
@@ -53,5 +63,21 @@ func (c CollectResult) Analyze() []*analyze.AnalyzeResult {
|
||||
}
|
||||
}
|
||||
|
||||
for _, hostAnalyzer := range hostAnalyzers {
|
||||
analyzeResult, err := analyze.HostAnalyze(hostAnalyzer, getCollectedFileContents, getChildCollectedFileContents)
|
||||
if err != nil {
|
||||
analyzeResult = []*analyze.AnalyzeResult{
|
||||
{
|
||||
IsFail: true,
|
||||
Title: "Analyzer Failed",
|
||||
Message: err.Error(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if analyzeResult != nil {
|
||||
analyzeResults = append(analyzeResults, analyzeResult...)
|
||||
}
|
||||
}
|
||||
return analyzeResults
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
analyze "github.com/replicatedhq/troubleshoot/pkg/analyze"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
"github.com/replicatedhq/troubleshoot/pkg/collect"
|
||||
"k8s.io/client-go/rest"
|
||||
@@ -17,13 +18,73 @@ type CollectOpts struct {
|
||||
ProgressChan chan interface{}
|
||||
}
|
||||
|
||||
type CollectResult struct {
|
||||
type CollectResult interface {
|
||||
Analyze() []*analyze.AnalyzeResult
|
||||
IsRBACAllowed() bool
|
||||
}
|
||||
|
||||
type ClusterCollectResult struct {
|
||||
AllCollectedData map[string][]byte
|
||||
Collectors collect.Collectors
|
||||
IsRBACAllowed bool
|
||||
isRBACAllowed bool
|
||||
Spec *troubleshootv1beta2.Preflight
|
||||
}
|
||||
|
||||
func (cr ClusterCollectResult) IsRBACAllowed() bool {
|
||||
return cr.isRBACAllowed
|
||||
}
|
||||
|
||||
type HostCollectResult struct {
|
||||
AllCollectedData map[string][]byte
|
||||
Collectors collect.HostCollectors
|
||||
Spec *troubleshootv1beta2.HostPreflight
|
||||
}
|
||||
|
||||
func (cr HostCollectResult) IsRBACAllowed() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CollectHost runs the collection phase of host preflight checks
|
||||
func CollectHost(opts CollectOpts, p *troubleshootv1beta2.HostPreflight) (CollectResult, error) {
|
||||
collectSpecs := make([]*troubleshootv1beta2.HostCollect, 0, 0)
|
||||
collectSpecs = append(collectSpecs, p.Spec.Collectors...)
|
||||
collectSpecs = ensureHostCollectorInList(collectSpecs, troubleshootv1beta2.HostCollect{CPU: &troubleshootv1beta2.CPU{}})
|
||||
collectSpecs = ensureHostCollectorInList(collectSpecs, troubleshootv1beta2.HostCollect{Memory: &troubleshootv1beta2.Memory{}})
|
||||
|
||||
allCollectedData := make(map[string][]byte)
|
||||
|
||||
var collectors collect.HostCollectors
|
||||
for _, desiredCollector := range collectSpecs {
|
||||
collector := collect.HostCollector{
|
||||
Collect: desiredCollector,
|
||||
}
|
||||
collectors = append(collectors, &collector)
|
||||
}
|
||||
|
||||
collectResult := HostCollectResult{
|
||||
Collectors: collectors,
|
||||
Spec: p,
|
||||
}
|
||||
|
||||
for _, collector := range collectors {
|
||||
result, err := collector.RunCollectorSync()
|
||||
if err != nil {
|
||||
opts.ProgressChan <- errors.Errorf("failed to run collector: %s: %v\n", collector.GetDisplayName(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
for k, v := range result {
|
||||
allCollectedData[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectResult.AllCollectedData = allCollectedData
|
||||
|
||||
return collectResult, nil
|
||||
}
|
||||
|
||||
// Collect runs the collection phase of preflight checks
|
||||
func Collect(opts CollectOpts, p *troubleshootv1beta2.Preflight) (CollectResult, error) {
|
||||
collectSpecs := make([]*troubleshootv1beta2.Collect, 0, 0)
|
||||
@@ -44,7 +105,7 @@ func Collect(opts CollectOpts, p *troubleshootv1beta2.Preflight) (CollectResult,
|
||||
collectors = append(collectors, &collector)
|
||||
}
|
||||
|
||||
collectResult := CollectResult{
|
||||
collectResult := ClusterCollectResult{
|
||||
Collectors: collectors,
|
||||
Spec: p,
|
||||
}
|
||||
@@ -62,7 +123,7 @@ func Collect(opts CollectOpts, p *troubleshootv1beta2.Preflight) (CollectResult,
|
||||
}
|
||||
|
||||
if foundForbidden && !opts.IgnorePermissionErrors {
|
||||
collectResult.IsRBACAllowed = false
|
||||
collectResult.isRBACAllowed = false
|
||||
return collectResult, errors.New("insufficient permissions to run all collectors")
|
||||
}
|
||||
|
||||
@@ -71,7 +132,7 @@ func Collect(opts CollectOpts, p *troubleshootv1beta2.Preflight) (CollectResult,
|
||||
if len(collector.RBACErrors) > 0 {
|
||||
// don't skip clusterResources collector due to RBAC issues
|
||||
if collector.Collect.ClusterResources == nil {
|
||||
collectResult.IsRBACAllowed = false // not failing, but going to report this
|
||||
collectResult.isRBACAllowed = false // not failing, but going to report this
|
||||
opts.ProgressChan <- fmt.Sprintf("skipping collector %s with insufficient RBAC permissions", collector.GetDisplayName())
|
||||
continue
|
||||
}
|
||||
@@ -106,3 +167,16 @@ func ensureCollectorInList(list []*troubleshootv1beta2.Collect, collector troubl
|
||||
|
||||
return append(list, &collector)
|
||||
}
|
||||
|
||||
func ensureHostCollectorInList(list []*troubleshootv1beta2.HostCollect, collector troubleshootv1beta2.HostCollect) []*troubleshootv1beta2.HostCollect {
|
||||
for _, inList := range list {
|
||||
if collector.CPU != nil && inList.CPU != nil {
|
||||
return list
|
||||
}
|
||||
if collector.Memory != nil && inList.Memory != nil {
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
return append(list, &collector)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user