From 25481aba1caf035d775aeec2ee7bbdc30e498103 Mon Sep 17 00:00:00 2001
From: Luke Reed
Date: Fri, 22 Oct 2021 13:46:32 -0400
Subject: [PATCH] Support multi controller (frontend) (#376)
---
e2e/tests/99_cleanup.yaml | 1 +
pkg/dashboard/templates/namespace.gohtml | 22 +-
pkg/summary/constants_test.go | 252 +++++++++++++++++++++--
pkg/summary/options.go | 2 +
pkg/summary/summary.go | 110 ++++++----
pkg/summary/summary_test.go | 44 +++-
pkg/utils/utils.go | 2 +-
7 files changed, 351 insertions(+), 82 deletions(-)
diff --git a/e2e/tests/99_cleanup.yaml b/e2e/tests/99_cleanup.yaml
index 5fe6dfdd..a91308a8 100644
--- a/e2e/tests/99_cleanup.yaml
+++ b/e2e/tests/99_cleanup.yaml
@@ -10,3 +10,4 @@ testcases:
kubectl delete ns metrics-server
helm -n vpa delete vpa
kubectl delete ns vpa
+ kubectl delete ns statefulset-demo
diff --git a/pkg/dashboard/templates/namespace.gohtml b/pkg/dashboard/templates/namespace.gohtml
index 4919be09..a7070f2f 100644
--- a/pkg/dashboard/templates/namespace.gohtml
+++ b/pkg/dashboard/templates/namespace.gohtml
@@ -1,11 +1,11 @@
{{define "namespace"}}
-{{ $foundFirstDeployment := false }}
+{{ $foundFirstWorkload := false }}
- Namespace
{{ $.Namespace }}
@@ -20,29 +20,29 @@
- {{ if lt (len $.Deployments) 1 }}
+ {{ if lt (len $.Workloads) 1 }}
- No deployments found in this namespace.
+ No workloads found in this namespace.
{{ else }}
- {{ range $deployment := $.Deployments }}
+ {{ range $workload := $.Workloads }}
- Deployment{{ $workload.ControllerType }}
- {{ $deployment.DeploymentName }}
+ {{ $workload.ControllerName }}
- {{ range $cName, $cSummary := $deployment.Containers }}
+ {{ range $cName, $cSummary := $workload.Containers }}
diff --git a/pkg/summary/constants_test.go b/pkg/summary/constants_test.go
index 7cef1612..78f65bd2 100644
--- a/pkg/summary/constants_test.go
+++ b/pkg/summary/constants_test.go
@@ -15,11 +15,11 @@
package summary
import (
- appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
"github.com/fairwindsops/goldilocks/pkg/utils"
@@ -73,10 +73,56 @@ var testVPABasic = &vpav1.VerticalPodAutoscaler{
},
},
}
-var testDeploymentBasic = &appsv1.Deployment{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-basic",
- Namespace: "testing",
+
+var testDeploymentBasicUnstructured = &unstructured.Unstructured{
+ Object: map[string]interface{}{
+ "kind": "Deployment",
+ "apiVersion": "apps/v1",
+ "metadata": map[string]interface{}{
+ "name": "test-basic",
+ "namespace": "testing",
+ },
+ "spec": map[string]interface{}{},
+ },
+}
+
+var testDeploymentBasicReplicaSetUnstructured = &unstructured.Unstructured{
+ Object: map[string]interface{}{
+ "kind": "ReplicaSet",
+ "apiVersion": "apps/v1",
+ "metadata": map[string]interface{}{
+ "ownerReferences": []interface{}{
+ map[string]interface{}{
+ "apiVersion": "apps/v1",
+ "kind": "Deployment",
+ "controller": true,
+ "name": "test-basic",
+ },
+ },
+ "name": "test-basic-0123456789",
+ "namespace": "testing",
+ },
+ "spec": map[string]interface{}{},
+ },
+}
+
+var testDeploymentBasicPodUnstructured = &unstructured.Unstructured{
+ Object: map[string]interface{}{
+ "kind": "Pod",
+ "apiVersion": "v1",
+ "metadata": map[string]interface{}{
+ "ownerReferences": []interface{}{
+ map[string]interface{}{
+ "apiVersion": "apps/v1",
+ "kind": "ReplicaSet",
+ "controller": true,
+ "name": "test-basic-0123456789",
+ },
+ },
+ "name": "test-basic-0123456789-01234",
+ "namespace": "testing",
+ },
+ "spec": map[string]interface{}{},
},
}
@@ -131,20 +177,30 @@ var testVPAWithReco = &vpav1.VerticalPodAutoscaler{
},
}
-var testDeploymentWithReco = &appsv1.Deployment{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-vpa-with-reco",
- Namespace: "testing",
- },
- Spec: appsv1.DeploymentSpec{
- Template: v1.PodTemplateSpec{
- Spec: v1.PodSpec{
- Containers: []v1.Container{
- {
- Name: "container",
- Resources: v1.ResourceRequirements{
- Limits: targetResources,
- Requests: targetResources,
+var testDeploymentWithRecoUnstructured = &unstructured.Unstructured{
+ Object: map[string]interface{}{
+ "kind": "Deployment",
+ "apiVersion": "apps/v1",
+ "metadata": map[string]interface{}{
+ "name": "test-vpa-with-reco",
+ "namespace": "testing",
+ },
+ "spec": map[string]interface{}{
+ "template": map[string]interface{}{
+ "spec": map[string]interface{}{
+ "containers": []interface{}{
+ map[string]interface{}{
+ "name": "container",
+ "resources": map[string]interface{}{
+ "limits": map[string]interface{}{
+ "cpu": "100m",
+ "memory": "100Mi",
+ },
+ "requests": map[string]interface{}{
+ "cpu": "100m",
+ "memory": "100Mi",
+ },
+ },
},
},
},
@@ -153,19 +209,171 @@ var testDeploymentWithReco = &appsv1.Deployment{
},
}
+var testDeploymentWithRecoReplicaSetUnstructured = &unstructured.Unstructured{
+ Object: map[string]interface{}{
+ "kind": "ReplicaSet",
+ "apiVersion": "apps/v1",
+ "metadata": map[string]interface{}{
+ "ownerReferences": []interface{}{
+ map[string]interface{}{
+ "apiVersion": "apps/v1",
+ "kind": "Deployment",
+ "controller": true,
+ "name": "test-vpa-with-reco",
+ },
+ },
+ "name": "test-vpa-with-reco-0123456789",
+ "namespace": "testing",
+ },
+ "spec": map[string]interface{}{},
+ },
+}
+
+var testDeploymentWithRecoPodUnstructured = &unstructured.Unstructured{
+ Object: map[string]interface{}{
+ "kind": "Pod",
+ "apiVersion": "v1",
+ "metadata": map[string]interface{}{
+ "ownerReferences": []interface{}{
+ map[string]interface{}{
+ "apiVersion": "apps/v1",
+ "kind": "ReplicaSet",
+ "controller": true,
+ "name": "test-vpa-with-reco-0123456789",
+ },
+ },
+ "name": "test-vpa-with-reco-0123456789-01234",
+ "namespace": "testing",
+ },
+ "spec": map[string]interface{}{},
+ },
+}
+
// The summary of these objects
var testSummary = Summary{
Namespaces: map[string]namespaceSummary{
"testing": {
Namespace: "testing",
- Deployments: map[string]deploymentSummary{
+ Workloads: map[string]workloadSummary{
"test-basic": {
- DeploymentName: "test-basic",
+ ControllerName: "test-basic",
+ ControllerType: "Deployment",
Containers: map[string]containerSummary{},
},
"test-vpa-with-reco": {
- DeploymentName: "test-vpa-with-reco",
+ ControllerName: "test-vpa-with-reco",
+ ControllerType: "Deployment",
+ Containers: map[string]containerSummary{
+ "container": {
+ ContainerName: "container",
+ LowerBound: lowerBound,
+ UpperBound: upperBound,
+ Target: targetResources,
+ Limits: targetResources,
+ Requests: targetResources,
+ },
+ },
+ },
+ },
+ },
+ },
+}
+
+// DaemonSet test and VPA
+
+var testDaemonSettWithRecoUnstructured = &unstructured.Unstructured{
+ Object: map[string]interface{}{
+ "kind": "DaemonSet",
+ "apiVersion": "apps/v1",
+ "metadata": map[string]interface{}{
+ "name": "test-ds-with-reco",
+ "namespace": "testing-daemonset",
+ },
+ "spec": map[string]interface{}{
+ "template": map[string]interface{}{
+ "spec": map[string]interface{}{
+ "containers": []interface{}{
+ map[string]interface{}{
+ "name": "container",
+ "resources": map[string]interface{}{
+ "limits": map[string]interface{}{
+ "cpu": "100m",
+ "memory": "100Mi",
+ },
+ "requests": map[string]interface{}{
+ "cpu": "100m",
+ "memory": "100Mi",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+}
+
+var testDaemonSetWithRecoPodUnstructured = &unstructured.Unstructured{
+ Object: map[string]interface{}{
+ "kind": "Pod",
+ "apiVersion": "v1",
+ "metadata": map[string]interface{}{
+ "ownerReferences": []interface{}{
+ map[string]interface{}{
+ "apiVersion": "apps/v1",
+ "kind": "DaemonSet",
+ "controller": true,
+ "name": "test-ds-with-reco",
+ },
+ },
+ "name": "test-ds-with-reco-01234",
+ "namespace": "testing-daemonset",
+ },
+ "spec": map[string]interface{}{},
+ },
+}
+
+var testDaemonSetVPAWithReco = &vpav1.VerticalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-ds-with-reco",
+ Namespace: "testing-daemonset",
+ Labels: utils.VPALabels,
+ },
+ Spec: vpav1.VerticalPodAutoscalerSpec{
+ TargetRef: &autoscalingv1.CrossVersionObjectReference{
+ APIVersion: "apps/v1",
+ Kind: "DaemonSet",
+ Name: "test-ds-with-reco",
+ },
+ UpdatePolicy: &vpav1.PodUpdatePolicy{
+ UpdateMode: &updateMode,
+ },
+ },
+ Status: vpav1.VerticalPodAutoscalerStatus{
+ Recommendation: &vpav1.RecommendedPodResources{
+ ContainerRecommendations: []vpav1.RecommendedContainerResources{
+ {
+ ContainerName: "container",
+ Target: targetResources,
+ UpperBound: upperBound,
+ LowerBound: lowerBound,
+ },
+ },
+ },
+ },
+}
+
+// The summary of the daemonset
+
+var testSummaryDaemonSet = Summary{
+ Namespaces: map[string]namespaceSummary{
+ "testing-daemonset": {
+ Namespace: "testing-daemonset",
+ Workloads: map[string]workloadSummary{
+ "test-ds-with-reco": {
+ ControllerName: "test-ds-with-reco",
+ ControllerType: "DaemonSet",
Containers: map[string]containerSummary{
"container": {
ContainerName: "container",
diff --git a/pkg/summary/options.go b/pkg/summary/options.go
index be5cce10..e8812a58 100644
--- a/pkg/summary/options.go
+++ b/pkg/summary/options.go
@@ -12,6 +12,7 @@ type Option func(*options)
type options struct {
kubeClient *kube.ClientInstance
vpaClient *kube.VPAClientInstance
+ dynamicClient *kube.DynamicClientInstance
namespace string
vpaLabels map[string]string
excludedContainers sets.String
@@ -22,6 +23,7 @@ func defaultOptions() *options {
return &options{
kubeClient: kube.GetInstance(),
vpaClient: kube.GetVPAInstance(),
+ dynamicClient: kube.GetDynamicInstance(),
namespace: namespaceAllNamespaces,
vpaLabels: utils.VPALabels,
excludedContainers: sets.NewString(),
diff --git a/pkg/summary/summary.go b/pkg/summary/summary.go
index 49322e1e..f0b44a81 100644
--- a/pkg/summary/summary.go
+++ b/pkg/summary/summary.go
@@ -18,10 +18,12 @@ import (
"context"
"strings"
- appsv1 "k8s.io/api/apps/v1"
+ controllerUtils "github.com/fairwindsops/controller-utils/pkg/controller"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
"k8s.io/klog"
@@ -33,18 +35,19 @@ const (
namespaceAllNamespaces = ""
)
-// Summary is for storing a summary of recommendation data by namespace/deployment/container
+// Summary is for storing a summary of recommendation data by namespace/controller type/container
type Summary struct {
Namespaces map[string]namespaceSummary
}
type namespaceSummary struct {
- Namespace string `json:"namespace"`
- Deployments map[string]deploymentSummary `json:"deployments"`
+ Namespace string `json:"namespace"`
+ Workloads map[string]workloadSummary `json:"workloads"`
}
-type deploymentSummary struct {
- DeploymentName string `json:"deploymentName"`
+type workloadSummary struct {
+ ControllerName string `json:"controllerName"`
+ ControllerType string `json:"controllerType"`
Containers map[string]containerSummary `json:"containers"`
}
@@ -67,8 +70,8 @@ type Summarizer struct {
// cached list of vpas
vpas []vpav1.VerticalPodAutoscaler
- // cached map of deploy/vpa name -> deployment
- deploymentForVPANamed map[string]*appsv1.Deployment
+ // cached map of vpa name -> workload
+ workloadForVPANamed map[string]*controllerUtils.Workload
}
// NewSummarizer returns a Summarizer for all goldilocks managed VPAs in all Namespaces
@@ -104,13 +107,13 @@ func (s Summarizer) GetSummary() (Summary, error) {
// then add that namespace by default to the blank summary
if s.namespace != namespaceAllNamespaces {
summary.Namespaces[s.namespace] = namespaceSummary{
- Namespace: s.namespace,
- Deployments: map[string]deploymentSummary{},
+ Namespace: s.namespace,
+ Workloads: map[string]workloadSummary{},
}
}
- // cached vpas and deployments
- if s.vpas == nil || s.deploymentForVPANamed == nil {
+ // cached vpas and workloads
+ if s.vpas == nil || s.workloadForVPANamed == nil {
err := s.Update()
if err != nil {
return summary, err
@@ -132,53 +135,70 @@ func (s Summarizer) GetSummary() (Summary, error) {
nsSummary = val
} else {
nsSummary = namespaceSummary{
- Namespace: namespace,
- Deployments: map[string]deploymentSummary{},
+ Namespace: namespace,
+ Workloads: map[string]workloadSummary{},
}
summary.Namespaces[namespace] = nsSummary
}
- // VPA.Name := Deployment.Name, as that's how goldilocks works
- dSummary := deploymentSummary{
- DeploymentName: vpa.Name,
+ wSummary := workloadSummary{
+ ControllerName: vpa.Name,
+ ControllerType: vpa.Spec.TargetRef.Kind,
Containers: map[string]containerSummary{},
}
- deployment, ok := s.deploymentForVPANamed[vpa.Name]
+ workload, ok := s.workloadForVPANamed[vpa.Name]
if !ok {
- klog.Errorf("no matching Deployment found for VPA/%s", vpa.Name)
+ klog.Errorf("no matching Workloads found for VPA/%s", vpa.Name)
continue
}
if vpa.Status.Recommendation == nil {
- klog.V(2).Infof("Empty status on %v", dSummary.DeploymentName)
- nsSummary.Deployments[dSummary.DeploymentName] = dSummary
+ klog.V(2).Infof("Empty status on %v", wSummary.ControllerName)
+ nsSummary.Workloads[wSummary.ControllerName] = wSummary
summary.Namespaces[nsSummary.Namespace] = nsSummary
continue
}
if len(vpa.Status.Recommendation.ContainerRecommendations) <= 0 {
- klog.V(2).Infof("No container recommendations found in the %v vpa.", dSummary.DeploymentName)
- nsSummary.Deployments[dSummary.DeploymentName] = dSummary
+ klog.V(2).Infof("No container recommendations found in the %v vpa.", wSummary.ControllerName)
+ nsSummary.Workloads[wSummary.ControllerName] = wSummary
summary.Namespaces[nsSummary.Namespace] = nsSummary
continue
}
- // get the full set of excluded containers for this Deployment
+ // get the full set of excluded containers for this workload
excludedContainers := sets.NewString().Union(s.excludedContainers)
- if val, exists := deployment.GetAnnotations()[utils.DeploymentExcludeContainersAnnotation]; exists {
+ if val, exists := workload.TopController.GetAnnotations()[utils.WorkloadExcludeContainersAnnotation]; exists {
excludedContainers.Insert(strings.Split(val, ",")...)
}
CONTAINER_REC_LOOP:
for _, containerRecommendation := range vpa.Status.Recommendation.ContainerRecommendations {
if excludedContainers.Has(containerRecommendation.ContainerName) {
- klog.V(2).Infof("Excluding container Deployment/%s/%s", dSummary.DeploymentName, containerRecommendation.ContainerName)
+ klog.V(2).Infof("Excluding container %s/%s/%s", wSummary.ControllerType, wSummary.ControllerName, containerRecommendation.ContainerName)
continue CONTAINER_REC_LOOP
}
var cSummary containerSummary
- for _, c := range deployment.Spec.Template.Spec.Containers {
- // find the matching container on the deployment
+ workloadPodSpecUnstructured, workloadPodSpecFound, err := unstructured.NestedMap(workload.TopController.UnstructuredContent(), "spec", "template", "spec")
+ if err != nil {
+ klog.Errorf("unable to parse spec.template.spec from unstructured workload. Namespace: '%s', Kind: '%s', Name: '%s'", workload.TopController.GetNamespace(), workload.TopController.GetKind(), workload.TopController.GetName())
+ continue CONTAINER_REC_LOOP
+ }
+ if !workloadPodSpecFound {
+ klog.Errorf("no spec.template.spec field from unstructured workload. Namespace: '%s', Kind: '%s', Name: '%s'", workload.TopController.GetNamespace(), workload.TopController.GetKind(), workload.TopController.GetName())
+ continue CONTAINER_REC_LOOP
+ }
+
+ var workloadPodSpec corev1.PodSpec
+ err = runtime.DefaultUnstructuredConverter.FromUnstructured(workloadPodSpecUnstructured, &workloadPodSpec)
+ if err != nil {
+ klog.Errorf("unable to convert unstructured pod spec to PodSpec struct. Namespace: '%s', Kind: '%s', Name: '%s'", workload.TopController.GetNamespace(), workload.TopController.GetKind(), workload.TopController.GetName())
+ continue CONTAINER_REC_LOOP
+ }
+
+ for _, c := range workloadPodSpec.Containers {
+ // find the matching container on the workload
if c.Name == containerRecommendation.ContainerName {
cSummary = containerSummary{
ContainerName: containerRecommendation.ContainerName,
@@ -189,21 +209,21 @@ func (s Summarizer) GetSummary() (Summary, error) {
Limits: utils.FormatResourceList(c.Resources.Limits),
Requests: utils.FormatResourceList(c.Resources.Requests),
}
- klog.V(6).Infof("Resources for Deployment/%s/%s: Requests: %v Limits: %v", dSummary.DeploymentName, c.Name, cSummary.Requests, cSummary.Limits)
- dSummary.Containers[cSummary.ContainerName] = cSummary
+ klog.V(6).Infof("Resources for %s/%s/%s: Requests: %v Limits: %v", wSummary.ControllerType, wSummary.ControllerName, c.Name, cSummary.Requests, cSummary.Limits)
+ wSummary.Containers[cSummary.ContainerName] = cSummary
continue CONTAINER_REC_LOOP
}
}
}
// update summary maps
- nsSummary.Deployments[dSummary.DeploymentName] = dSummary
+ nsSummary.Workloads[wSummary.ControllerName] = wSummary
summary.Namespaces[nsSummary.Namespace] = nsSummary
}
return summary, nil
}
-// Update the set of VPAs and Deployments that the Summarizer uses for creating a summary
+// Update the set of VPAs and Workloads that the Summarizer uses for creating a summary
func (s *Summarizer) Update() error {
err := s.updateVPAs()
if err != nil {
@@ -211,7 +231,7 @@ func (s *Summarizer) Update() error {
return err
}
- err = s.updateDeployments()
+ err = s.updateWorkloads()
if err != nil {
klog.Error(err.Error())
return err
@@ -251,33 +271,33 @@ func getVPAListOptionsForLabels(vpaLabels map[string]string) metav1.ListOptions
}
}
-func (s *Summarizer) updateDeployments() error {
+func (s *Summarizer) updateWorkloads() error {
nsLog := s.namespace
if s.namespace == namespaceAllNamespaces {
nsLog = "all namespaces"
}
- klog.V(3).Infof("Looking for Deployments in %s", nsLog)
- deployments, err := s.listDeployments(metav1.ListOptions{})
+ klog.V(3).Infof("Looking for Workloads in %s", nsLog)
+ workloads, err := s.listWorkloads()
if err != nil {
return err
}
- klog.V(10).Infof("Found deployments: %v", deployments)
+ klog.V(10).Infof("Found workloads in namespace '%s': %v", s.namespace, workloads)
- // map the deployment.name -> &deployment for easy vpa lookup (since vpa.Name == deployment.Name for matching vpas/deployments)
- s.deploymentForVPANamed = map[string]*appsv1.Deployment{}
- for _, d := range deployments {
- d := d
- s.deploymentForVPANamed[d.Name] = &d
+ // map the workload.name -> &controllerUtils.Workload{} for easy vpa lookup (since vpa.Name == workload.Name)
+ s.workloadForVPANamed = map[string]*controllerUtils.Workload{}
+ for _, w := range workloads {
+ w := w
+ s.workloadForVPANamed[w.TopController.GetName()] = &w
}
return nil
}
-func (s Summarizer) listDeployments(listOptions metav1.ListOptions) ([]appsv1.Deployment, error) {
- deployments, err := s.kubeClient.Client.AppsV1().Deployments(s.namespace).List(context.TODO(), listOptions)
+func (s Summarizer) listWorkloads() ([]controllerUtils.Workload, error) {
+ workloads, err := controllerUtils.GetAllTopControllers(context.TODO(), s.dynamicClient.Client, s.dynamicClient.RESTMapper, s.namespace)
if err != nil {
return nil, err
}
- return deployments.Items, nil
+ return workloads, nil
}
diff --git a/pkg/summary/summary_test.go b/pkg/summary/summary_test.go
index 853279f8..df47e230 100644
--- a/pkg/summary/summary_test.go
+++ b/pkg/summary/summary_test.go
@@ -21,24 +21,38 @@ import (
"github.com/fairwindsops/goldilocks/pkg/kube"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime/schema"
)
-func TestSummarizer(t *testing.T) {
+func Test_Summarizer(t *testing.T) {
kubeClientVPA := kube.GetMockVPAClient()
kubeClient := kube.GetMockClient()
+ dynamicClient := kube.GetMockDynamicClient()
summarizer := NewSummarizer()
summarizer.kubeClient = kubeClient
summarizer.vpaClient = kubeClientVPA
+ summarizer.dynamicClient = dynamicClient
- _, _ = kubeClient.Client.AppsV1().Deployments("testing").Create(context.TODO(), testDeploymentBasic, metav1.CreateOptions{})
+ // _, _ = dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}).Create(context.TODO(), nsLabeledTrueUnstructured, metav1.CreateOptions{})
+ _, err := dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}).Namespace("testing").Create(context.TODO(), testDeploymentBasicUnstructured, metav1.CreateOptions{})
+ assert.NoError(t, err)
+ _, err = dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "replicasets"}).Namespace("testing").Create(context.TODO(), testDeploymentBasicReplicaSetUnstructured, metav1.CreateOptions{})
+ assert.NoError(t, err)
+ _, err = dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}).Namespace("testing").Create(context.TODO(), testDeploymentBasicPodUnstructured, metav1.CreateOptions{})
+ assert.NoError(t, err)
_, errOk := kubeClientVPA.Client.AutoscalingV1().VerticalPodAutoscalers("testing").Create(context.TODO(), testVPABasic, metav1.CreateOptions{})
assert.NoError(t, errOk)
_, errOk2 := kubeClientVPA.Client.AutoscalingV1().VerticalPodAutoscalers("testing").Create(context.TODO(), testVPANoLabels, metav1.CreateOptions{})
assert.NoError(t, errOk2)
- _, _ = kubeClient.Client.AppsV1().Deployments("testing").Create(context.TODO(), testDeploymentWithReco, metav1.CreateOptions{})
+ _, err = dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}).Namespace("testing").Create(context.TODO(), testDeploymentWithRecoUnstructured, metav1.CreateOptions{})
+ assert.NoError(t, err)
+ _, err = dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "replicasets"}).Namespace("testing").Create(context.TODO(), testDeploymentWithRecoReplicaSetUnstructured, metav1.CreateOptions{})
+ assert.NoError(t, err)
+ _, err = dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}).Namespace("testing").Create(context.TODO(), testDeploymentWithRecoPodUnstructured, metav1.CreateOptions{})
+ assert.NoError(t, err)
_, errOk3 := kubeClientVPA.Client.AutoscalingV1().VerticalPodAutoscalers("testing").Create(context.TODO(), testVPAWithReco, metav1.CreateOptions{})
assert.NoError(t, errOk3)
@@ -47,3 +61,27 @@ func TestSummarizer(t *testing.T) {
assert.EqualValues(t, testSummary, got)
}
+
+func Test_Summarizer_Daemonset(t *testing.T) {
+ kubeClientVPA := kube.GetMockVPAClient()
+ kubeClient := kube.GetMockClient()
+ dynamicClient := kube.GetMockDynamicClient()
+
+ summarizer := NewSummarizer()
+ summarizer.kubeClient = kubeClient
+ summarizer.vpaClient = kubeClientVPA
+ summarizer.dynamicClient = dynamicClient
+
+ // _, _ = dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}).Create(context.TODO(), nsLabeledTrueUnstructured, metav1.CreateOptions{})
+ _, err := dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "daemonsets"}).Namespace("testing-daemonset").Create(context.TODO(), testDaemonSettWithRecoUnstructured, metav1.CreateOptions{})
+ assert.NoError(t, err)
+ _, err = dynamicClient.Client.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}).Namespace("testing-daemonset").Create(context.TODO(), testDaemonSetWithRecoPodUnstructured, metav1.CreateOptions{})
+ assert.NoError(t, err)
+ _, errOk := kubeClientVPA.Client.AutoscalingV1().VerticalPodAutoscalers("testing-daemonset").Create(context.TODO(), testDaemonSetVPAWithReco, metav1.CreateOptions{})
+ assert.NoError(t, errOk)
+
+ got, err := summarizer.GetSummary()
+ assert.NoError(t, err)
+
+ assert.EqualValues(t, testSummaryDaemonSet, got)
+}
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
index 32679fb3..5d9ef56c 100644
--- a/pkg/utils/utils.go
+++ b/pkg/utils/utils.go
@@ -27,7 +27,7 @@ var (
// VpaUpdateModeKey is the label used to indicate the vpa update mode.
VpaUpdateModeKey = LabelOrAnnotationBase + "/" + "vpa-update-mode"
// DeploymentExcludeContainersAnnotation is the label used to exclude container names from being reported.
- DeploymentExcludeContainersAnnotation = LabelOrAnnotationBase + "/" + "exclude-containers"
+ WorkloadExcludeContainersAnnotation = LabelOrAnnotationBase + "/" + "exclude-containers"
// VpaResourcePolicyAnnotation is the annotation use to define the json configuration of PodResourcePolicy section of a vpa
VpaResourcePolicyAnnotation = LabelOrAnnotationBase + "/" + "vpa-resource-policy"
)