Skip to content

Commit 54ed99b

Browse files
authored
Reduce Metrics Payload Size [2] (#298)
1 parent ea3252f commit 54ed99b

File tree

7 files changed

+235
-61
lines changed

7 files changed

+235
-61
lines changed

charts/metrics-agent/Chart.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ type: application
1414

1515
# This is the chart version. This version number should be incremented each time you make changes
1616
# to the chart and its templates, including the app version.
17-
version: 2.11.40
17+
version: 2.12.0
1818

1919
# This is the version number of the application being deployed. This version number should be
2020
# incremented each time you make changes to the application.
21-
appVersion: 2.11.40
21+
appVersion: 2.12.0

charts/metrics-agent/values.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ uploadRegion: "us-west-2"
2626

2727
image:
2828
name: cloudability/metrics-agent
29-
tag: 2.11.40
29+
tag: 2.12.0
3030
pullPolicy: Always
3131

3232
imagePullSecrets: []

kubernetes/kubernetes.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func CollectKubeMetrics(config KubeAgentConfig) {
162162
informerStopCh := make(chan struct{})
163163
// start up informers for each of the k8s resources that metrics are being collected on
164164
kubeAgent.Informers, err = k8s_stats.StartUpInformers(kubeAgent.Clientset, kubeAgent.ClusterVersion.version,
165-
config.InformerResyncInterval, informerStopCh)
165+
config.InformerResyncInterval, config.ParseMetricData, informerStopCh)
166166
if err != nil {
167167
log.Warnf("Warning: Informers failed to start up: %s", err)
168168
}
@@ -336,7 +336,7 @@ func (ka KubeAgentConfig) collectMetrics(ctx context.Context, config KubeAgentCo
336336
}
337337

338338
// export k8s resource metrics (ex: pods.jsonl) using informers to the metric sample directory
339-
err = k8s_stats.GetK8sMetricsFromInformer(config.Informers, metricSampleDir, config.ParseMetricData)
339+
err = k8s_stats.GetK8sMetricsFromInformer(config.Informers, metricSampleDir)
340340
if err != nil {
341341
return fmt.Errorf("unable to export k8s metrics: %s", err)
342342
}

kubernetes/kubernetes_test.go

+69-26
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package kubernetes
22

33
import (
4+
"bytes"
45
"context"
56
"encoding/json"
7+
"github.com/cloudability/metrics-agent/retrieval/k8s"
68
"net/http"
79
"net/http/httptest"
810
"os"
@@ -268,12 +270,12 @@ func TestCollectMetrics(t *testing.T) {
268270
ka.NodeMetrics.SetAvailability(NodeStatsSummaryEndpoint, Direct, true)
269271

270272
stopCh := make(chan struct{})
271-
ka.Informers, err = getMockInformers(ka.ClusterVersion.version, stopCh)
273+
ka.Informers, err = getMockInformers(ka.ClusterVersion.version, false, stopCh)
272274
if err != nil {
273275
t.Error(err)
274276
}
275277
parseStopCh := make(chan struct{})
276-
parseInformers, err := getMockInformers(1.22, parseStopCh)
278+
parseInformers, err := getMockInformers(1.22, true, parseStopCh)
277279
if err != nil {
278280
t.Error(err)
279281
}
@@ -364,7 +366,7 @@ func TestCollectMetrics(t *testing.T) {
364366
}
365367
})
366368
t.Run("Ensure collection occurs with parseMetrics is disabled"+
367-
" ensure sensitive data is not stripped", func(t *testing.T) {
369+
" ensure only expected data is stripped", func(t *testing.T) {
368370

369371
filepath.Walk(ka.msExportDirectory.Name(), func(path string, info os.FileInfo, err error) error {
370372
if err != nil {
@@ -376,30 +378,51 @@ func TestCollectMetrics(t *testing.T) {
376378
t.Error(err)
377379
}
378380
if strings.Contains(info.Name(), "pods") {
379-
// check if secrets were not stripped from pods if parseMetrics is false
380-
if !strings.Contains(string(in), "ReallySecretStuff") {
381-
t.Error("Original file should have contained secret, but did not")
381+
// check if secrets were still stripped from pods if parseMetrics is false
382+
if strings.Contains(string(in), "ReallySecretStuff") {
383+
t.Error("file should not have contained secret, but did")
384+
}
385+
if !strings.Contains(string(in), "\"imagePullPolicy\":\"Always\"") {
386+
t.Error("file should have contained imagePullPolicy, but did not")
382387
}
383388
} else if strings.Contains(info.Name(), "namespaces") {
384-
// check if secrets were not stripped from namespaces if parseMetrics is false
385-
if !strings.Contains(string(in), "ManageFieldToBeDeleted") {
386-
t.Error("Original file should have contained ManagedField data, but did not")
389+
// check if secrets were still stripped from namespaces if parseMetrics is false
390+
if strings.Contains(string(in), "ManageFieldToBeDeleted") {
391+
t.Error("file should not have contained ManagedField data, but did")
387392
}
388393
} else if strings.Contains(info.Name(), "deployments") {
389-
// check if sensitive fields were not stripped from deployments if parseMetrics is false
390-
if !strings.Contains(string(in), "DangThisIsSecret") {
391-
t.Error("Original file should have contained sensitive data, but did not")
394+
// check if sensitive fields was still stripped from deployments if parseMetrics is false
395+
if strings.Contains(string(in), "DangThisIsSecret") {
396+
t.Error("file should not have contained sensitive data, but did")
392397
}
393398
} else {
394-
// all other jsonl share the same annotation that should not be removed
395-
if !strings.Contains(string(in), "IAmSecretEnvVariables") {
396-
t.Error("Original file should have contained sensitive data, but did not")
399+
// all other jsonl share the same annotation that should be removed
400+
if strings.Contains(string(in), "IAmSecretEnvVariables") {
401+
t.Error("file should not have contained sensitive data, but did")
397402
}
398403
}
399404
}
400405
return nil
401406
})
402407
})
408+
t.Run("Ensure that resources are properly filtered out", func(t *testing.T) {
409+
filepath.Walk(ka.msExportDirectory.Name(), func(path string, info os.FileInfo, err error) error {
410+
if err != nil {
411+
t.Error(err)
412+
}
413+
if strings.Contains(info.Name(), "jsonl") {
414+
in, err := os.ReadFile(path)
415+
if err != nil {
416+
t.Error(err)
417+
}
418+
fileLen := strings.Count(string(in), "\n")
419+
if fileLen != 1 {
420+
t.Errorf("Expected 1 entry in file %s, got %d", info.Name(), fileLen)
421+
}
422+
}
423+
return nil
424+
})
425+
})
403426
t.Run("Ensure collection occurs with parseMetrics enabled"+
404427
"ensure sensitive data is stripped", func(t *testing.T) {
405428
err = kubeAgentParseMetrics.collectMetrics(context.TODO(), kubeAgentParseMetrics, cs, fns)
@@ -420,6 +443,9 @@ func TestCollectMetrics(t *testing.T) {
420443
if strings.Contains(string(in), "ReallySecretStuff") {
421444
t.Error("Stripped file should not have contained secret, but did")
422445
}
446+
if strings.Contains(string(in), "\"imagePullPolicy\":\"Always\"") {
447+
t.Error("file should not have contained imagePullPolicy, but did")
448+
}
423449
} else if strings.Contains(info.Name(), "namespaces") {
424450
// check if sensitive fields were stripped from namespaces if parseMetrics is true
425451
if strings.Contains(string(in), "ManageFieldToBeDeleted") {
@@ -524,7 +550,8 @@ func NewTestServer() *httptest.Server {
524550
}
525551

526552
// nolint: lll
527-
func getMockInformers(clusterVersion float64, stopCh chan struct{}) (map[string]*cache.SharedIndexInformer, error) {
553+
func getMockInformers(clusterVersion float64, parseMetricsData bool,
554+
stopCh chan struct{}) (map[string]*cache.SharedIndexInformer, error) {
528555
// create mock informers for each resource we collect k8s metrics on
529556
replicationControllers := fcache.NewFakeControllerSource()
530557
rcinformer := cache.NewSharedInformer(replicationControllers, &v1.ReplicationController{}, 1*time.Second).(cache.SharedIndexInformer)
@@ -581,7 +608,7 @@ func getMockInformers(clusterVersion float64, stopCh chan struct{}) (map[string]
581608
"cronjobs": &cjinformer,
582609
}
583610
// Call the Run function for each Informer, allowing the informers to listen for Add events
584-
startMockInformers(mockInformers, stopCh)
611+
startMockInformers(mockInformers, parseMetricsData, stopCh)
585612

586613
// Private Annotation to be removed if ParseMetrics is enabled
587614
annotation := map[string]string{
@@ -594,25 +621,40 @@ func getMockInformers(clusterVersion float64, stopCh chan struct{}) (map[string]
594621
nodes.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "n1", Annotations: annotation}})
595622
persistentVolumes.Add(&v1.PersistentVolume{ObjectMeta: metav1.ObjectMeta{Name: "pv1", Annotations: annotation}})
596623
persistentVolumeClaims.Add(&v1.PersistentVolumeClaim{ObjectMeta: metav1.ObjectMeta{Name: "pvc1", Annotations: annotation}})
597-
replicaSets.Add(&v1apps.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Name: "rs1", Annotations: annotation}})
624+
replicaSets.Add(&v1apps.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Name: "rs1", Annotations: annotation},
625+
Status: v1apps.ReplicaSetStatus{Replicas: int32(1)}})
626+
// should not be exported as replicaset is empty
627+
replicaSets.Add(&v1apps.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Name: "rs2", Annotations: annotation}})
598628
daemonSets.Add(&v1apps.DaemonSet{ObjectMeta: metav1.ObjectMeta{Name: "ds1", Annotations: annotation}})
599629
jobs.Add(&v1batch.Job{ObjectMeta: metav1.ObjectMeta{Name: "job1", Annotations: annotation}})
630+
oneDayAgo := metav1.NewTime(time.Now().Add(-24 * time.Hour))
631+
// should not be exported as job was completed some time ago
632+
jobs.Add(&v1batch.Job{ObjectMeta: metav1.ObjectMeta{Name: "job2", Annotations: annotation},
633+
Status: v1batch.JobStatus{CompletionTime: &oneDayAgo}})
634+
// should not be exported as job was completed some time ago
635+
jobs.Add(&v1batch.Job{ObjectMeta: metav1.ObjectMeta{Name: "job3", Annotations: annotation},
636+
Status: v1batch.JobStatus{
637+
Failed: int32(1),
638+
Conditions: []v1batch.JobCondition{{Type: v1batch.JobFailed, LastTransitionTime: oneDayAgo}},
639+
}})
600640
if clusterVersion > 1.20 {
601641
cronJobs.Add(&v1batch.CronJob{ObjectMeta: metav1.ObjectMeta{Name: "cj1", Annotations: annotation}})
602642
}
603643

604-
// pods is unique as we use this pod file for parseMetrics testing
605-
// for parseMetricData testing, add a cldy metrics-agent pod to the mock informers
644+
// adds 3 pods, two of which completed long ago and will not be added to export sample
606645
podData, err := os.ReadFile("../testdata/pods.jsonl")
607646
if err != nil {
608647
return nil, err
609648
}
610-
var myPod *v1.Pod
611-
err = json.Unmarshal(podData, &myPod)
612-
if err != nil {
613-
return nil, err
649+
dec := json.NewDecoder(bytes.NewReader(podData))
650+
for dec.More() {
651+
var pod v1.Pod
652+
if err := dec.Decode(&pod); err != nil {
653+
return nil, err
654+
}
655+
pods.Add(&pod)
614656
}
615-
pods.Add(myPod)
657+
616658
// namespace also used in testing
617659
namespaceData, err := os.ReadFile("../testdata/namespaces.jsonl")
618660
if err != nil {
@@ -639,11 +681,12 @@ func getMockInformers(clusterVersion float64, stopCh chan struct{}) (map[string]
639681
return mockInformers, nil
640682
}
641683

642-
func startMockInformers(mockInformers map[string]*cache.SharedIndexInformer, stopCh chan struct{}) {
684+
func startMockInformers(mockInformers map[string]*cache.SharedIndexInformer, parseMetrics bool, stopCh chan struct{}) {
643685
for _, informer := range mockInformers {
644686
if (*informer) == nil {
645687
continue
646688
}
689+
_ = (*informer).SetTransform(k8s.GetTransformFunc(parseMetrics))
647690
go (*informer).Run(stopCh)
648691
}
649692
}

0 commit comments

Comments
 (0)