mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-04-15 07:16:34 +00:00
* feat: node metrics analyser The analyser only checks PVC usage at the moment. More analysers can be added on a need to have basis * Add tests * Fix flaky test by waiting for goldpinger pods to start * Fix how outcomes get checked * Fix catch all outcome condition * Fix test * feat: node metrics analyser The analyser only checks PVC usage at the moment. More analysers can be added on a need to have basis * Add tests * Fix flaky test by waiting for goldpinger pods to start * Fix how outcomes get checked * Fix catch all outcome condition * Fix test * Regenerate schemas * Fix failing test --------- Co-authored-by: Dexter Yan <yanshaocong@gmail.com>
289 lines
7.0 KiB
Go
289 lines
7.0 KiB
Go
package analyzer
|
|
|
|
import (
|
|
"testing"
|
|
|
|
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
|
"github.com/stretchr/testify/assert"
|
|
kubeletv1alpha1 "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
|
|
utilptr "k8s.io/utils/ptr"
|
|
)
|
|
|
|
func TestAnalyzeNodeMetrics_findPVCUsageStats(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
analyzer troubleshootv1beta2.NodeMetricsAnalyze
|
|
summaries []kubeletv1alpha1.Summary
|
|
want []pvcUsageStats
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "no summaries",
|
|
summaries: []kubeletv1alpha1.Summary{},
|
|
want: []pvcUsageStats{},
|
|
},
|
|
{
|
|
name: "one summary",
|
|
summaries: []kubeletv1alpha1.Summary{
|
|
{
|
|
Pods: []kubeletv1alpha1.PodStats{
|
|
{
|
|
PodRef: kubeletv1alpha1.PodReference{
|
|
Namespace: "default",
|
|
Name: "my-pod",
|
|
},
|
|
VolumeStats: []kubeletv1alpha1.VolumeStats{
|
|
{
|
|
Name: "volume-1",
|
|
PVCRef: &kubeletv1alpha1.PVCReference{
|
|
Namespace: "default",
|
|
Name: "my-pvc",
|
|
},
|
|
FsStats: kubeletv1alpha1.FsStats{
|
|
AvailableBytes: utilptr.To(uint64(20)),
|
|
UsedBytes: utilptr.To(uint64(80)),
|
|
CapacityBytes: utilptr.To(uint64(100)),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []pvcUsageStats{
|
|
{
|
|
Used: 80,
|
|
PvcName: "default/my-pvc",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "one summary with namespace filter",
|
|
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
|
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{
|
|
PVC: &troubleshootv1beta2.PVCRef{
|
|
Namespace: "another-namespace",
|
|
},
|
|
},
|
|
},
|
|
summaries: []kubeletv1alpha1.Summary{
|
|
{
|
|
Pods: []kubeletv1alpha1.PodStats{
|
|
{
|
|
PodRef: kubeletv1alpha1.PodReference{
|
|
Namespace: "default",
|
|
Name: "my-pod",
|
|
},
|
|
VolumeStats: []kubeletv1alpha1.VolumeStats{
|
|
{
|
|
Name: "volume-1",
|
|
PVCRef: &kubeletv1alpha1.PVCReference{
|
|
Namespace: "default",
|
|
Name: "my-pvc",
|
|
},
|
|
FsStats: kubeletv1alpha1.FsStats{
|
|
AvailableBytes: utilptr.To(uint64(20)),
|
|
UsedBytes: utilptr.To(uint64(80)),
|
|
CapacityBytes: utilptr.To(uint64(100)),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []pvcUsageStats{},
|
|
},
|
|
{
|
|
name: "one summary with name regex filter",
|
|
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
|
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{
|
|
PVC: &troubleshootv1beta2.PVCRef{
|
|
NameRegex: ".*other.*",
|
|
},
|
|
},
|
|
},
|
|
summaries: []kubeletv1alpha1.Summary{
|
|
{
|
|
Pods: []kubeletv1alpha1.PodStats{
|
|
{
|
|
PodRef: kubeletv1alpha1.PodReference{
|
|
Namespace: "default",
|
|
Name: "my-pod",
|
|
},
|
|
VolumeStats: []kubeletv1alpha1.VolumeStats{
|
|
{
|
|
Name: "volume-1",
|
|
PVCRef: &kubeletv1alpha1.PVCReference{
|
|
Namespace: "default",
|
|
Name: "my-pvc",
|
|
},
|
|
FsStats: kubeletv1alpha1.FsStats{
|
|
AvailableBytes: utilptr.To(uint64(20)),
|
|
UsedBytes: utilptr.To(uint64(80)),
|
|
CapacityBytes: utilptr.To(uint64(100)),
|
|
},
|
|
},
|
|
{
|
|
Name: "volume-1",
|
|
PVCRef: &kubeletv1alpha1.PVCReference{
|
|
Namespace: "default",
|
|
Name: "my-other-pvc",
|
|
},
|
|
FsStats: kubeletv1alpha1.FsStats{
|
|
AvailableBytes: utilptr.To(uint64(25)),
|
|
UsedBytes: utilptr.To(uint64(75)),
|
|
CapacityBytes: utilptr.To(uint64(100)),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
want: []pvcUsageStats{
|
|
{
|
|
Used: 75,
|
|
PvcName: "default/my-other-pvc",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
a := &AnalyzeNodeMetrics{
|
|
analyzer: &tt.analyzer,
|
|
}
|
|
got, err := a.findPVCUsageStats(tt.summaries)
|
|
assert.Equalf(t, tt.wantErr, err != nil, "AnalyzeNodeMetrics.findPVCUsageStats() error = %v, wantErr %v", err, tt.wantErr)
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAnalyzeNodeMetrics_Analyze(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
analyzer troubleshootv1beta2.NodeMetricsAnalyze
|
|
nodeMetrics string
|
|
want []*AnalyzeResult
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "no node metrics",
|
|
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
|
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{},
|
|
},
|
|
nodeMetrics: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid node metrics",
|
|
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
|
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{},
|
|
},
|
|
nodeMetrics: "invalid",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "no summaries",
|
|
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
|
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{},
|
|
},
|
|
nodeMetrics: "{}",
|
|
want: []*AnalyzeResult{},
|
|
},
|
|
{
|
|
name: "one summary with name regex filter",
|
|
analyzer: troubleshootv1beta2.NodeMetricsAnalyze{
|
|
Outcomes: []*troubleshootv1beta2.Outcome{
|
|
{
|
|
Fail: &troubleshootv1beta2.SingleOutcome{
|
|
When: "pvcUsedPercentage >= 75",
|
|
Message: "PVC space usage is too high for pvcs [{{ .PVC.ConcatenatedNames }}]",
|
|
},
|
|
},
|
|
{
|
|
Pass: &troubleshootv1beta2.SingleOutcome{
|
|
Message: "No PVCs are using more than 80% of storage",
|
|
},
|
|
},
|
|
},
|
|
Filters: troubleshootv1beta2.NodeMetricsAnalyzeFilters{
|
|
PVC: &troubleshootv1beta2.PVCRef{
|
|
NameRegex: ".*other.*",
|
|
},
|
|
},
|
|
},
|
|
nodeMetrics: `{
|
|
"pods": [
|
|
{
|
|
"podRef": {
|
|
"name": "my-pod",
|
|
"namespace": "my-namespace"
|
|
},
|
|
"volume": [
|
|
{
|
|
"capacityBytes": 100,
|
|
"usedBytes": 80,
|
|
"pvcRef": {
|
|
"name": "backup-pvc",
|
|
"namespace": "my-namespace"
|
|
}
|
|
},
|
|
{
|
|
"capacityBytes": 100,
|
|
"usedBytes": 75,
|
|
"pvcRef": {
|
|
"name": "another-pvc",
|
|
"namespace": "my-namespace"
|
|
}
|
|
},
|
|
{
|
|
"capacityBytes": 100,
|
|
"usedBytes": 80,
|
|
"pvcRef": {
|
|
"name": "the-other-pvc",
|
|
"namespace": "my-namespace"
|
|
}
|
|
},
|
|
{
|
|
"capacityBytes": 100,
|
|
"usedBytes": 65,
|
|
"pvcRef": {
|
|
"name": "to-other-pvc",
|
|
"namespace": "my-namespace"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}`,
|
|
want: []*AnalyzeResult{
|
|
{
|
|
Title: "Node Metrics",
|
|
IsFail: true,
|
|
Message: "PVC space usage is too high for pvcs [my-namespace/another-pvc, my-namespace/the-other-pvc]",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
a := &AnalyzeNodeMetrics{
|
|
analyzer: &tt.analyzer,
|
|
}
|
|
filesFn := func(string, []string) (map[string][]byte, error) {
|
|
return map[string][]byte{
|
|
"node-metrics.json": []byte(tt.nodeMetrics),
|
|
}, nil
|
|
}
|
|
|
|
got, err := a.Analyze(nil, filesFn)
|
|
assert.Equalf(t, tt.wantErr, err != nil, "AnalyzeNodeMetrics.Analyze() error = %v, wantErr %v", err, tt.wantErr)
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|