Skip to content

Commit e9e0970

Browse files
committed
add unit tests
1 parent eae5682 commit e9e0970

4 files changed

Lines changed: 227 additions & 41 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ require (
552552

553553
require (
554554
github.com/DATA-DOG/go-sqlmock v1.5.2
555-
github.com/DataDog/agent-payload/v5 v5.0.168
555+
github.com/DataDog/agent-payload/v5 v5.0.170
556556
github.com/DataDog/datadog-agent/comp/api/api/def v0.68.3
557557
github.com/DataDog/datadog-agent/comp/core/config v0.69.2
558558
github.com/DataDog/datadog-agent/comp/core/flare/types v0.69.2

pkg/clusteragent/autoscaling/cluster/config_retriever_values.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"encoding/json"
1212

1313
kubeAutoscaling "github.com/DataDog/agent-payload/v5/autoscaling/kubernetes"
14+
corev1 "k8s.io/api/core/v1"
1415

1516
"github.com/DataDog/datadog-agent/pkg/remoteconfig/state"
1617
"github.com/DataDog/datadog-agent/pkg/util/log"
@@ -78,3 +79,25 @@ func (avp *autoscalingValuesProcessor) postProcess() {
7879
}
7980
}
8081
}
82+
83+
func convertLabels(input []*kubeAutoscaling.DomainLabels) map[string]string {
84+
output := make(map[string]string)
85+
for _, elem := range input {
86+
output[elem.Key] = elem.Value
87+
}
88+
89+
return output
90+
}
91+
92+
func convertTaints(input []*kubeAutoscaling.Taints) []corev1.Taint {
93+
output := []corev1.Taint{}
94+
for _, elem := range input {
95+
output = append(output, corev1.Taint{
96+
Key: elem.Key,
97+
Value: elem.Value,
98+
Effect: corev1.TaintEffect(elem.Effect),
99+
})
100+
}
101+
102+
return output
103+
}

pkg/clusteragent/autoscaling/cluster/controller.go

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"k8s.io/client-go/util/workqueue"
2424
"k8s.io/utils/clock"
2525

26-
kubeAutoscaling "github.com/DataDog/agent-payload/v5/autoscaling/kubernetes"
2726
"k8s.io/client-go/dynamic"
2827
"k8s.io/client-go/dynamic/dynamicinformer"
2928
karpenterv1 "sigs.k8s.io/karpenter/pkg/apis/v1"
@@ -311,7 +310,7 @@ func (c *Controller) deleteNodePool(ctx context.Context, name string) error {
311310

312311
type minNodePool struct {
313312
name string `json:"name"`
314-
nodePoolHash string `json:"target_hash"`
313+
nodePoolHash string `json:"target_hash"` // TODO utilize once this is part of payload
315314
recommendedInstanceTypes []string `json:"recommended_instance_types"`
316315
labels map[string]string `json:"labels"`
317316
taints []corev1.Taint `json:"taints"`
@@ -370,16 +369,16 @@ func buildNodePoolSpec(mnp minNodePool, nodeClassName string) karpenterv1.NodePo
370369
func buildNodePoolPatch(np *karpenterv1.NodePool, mnp minNodePool) map[string]interface{} {
371370

372371
// Build requirements patch, only updating values for the instance types
373-
newRequirements := []map[string]interface{}{}
372+
updatedRequirements := []map[string]interface{}{}
374373
for _, r := range np.Spec.Template.Spec.Requirements {
375374
if r.Key == corev1.LabelInstanceTypeStable {
376375
r.Operator = "In"
377376
r.Values = mnp.recommendedInstanceTypes
378377
}
379378

380-
newRequirements = append(newRequirements, map[string]interface{}{
379+
updatedRequirements = append(updatedRequirements, map[string]interface{}{
381380
"key": r.Key,
382-
"operator": r.Operator,
381+
"operator": string(r.Operator),
383382
"values": r.Values,
384383
})
385384
}
@@ -393,35 +392,13 @@ func buildNodePoolPatch(np *karpenterv1.NodePool, mnp minNodePool) map[string]in
393392
"spec": map[string]interface{}{
394393
"template": map[string]interface{}{
395394
"spec": map[string]interface{}{
396-
"requirements": newRequirements,
395+
"requirements": updatedRequirements,
397396
},
398397
},
399398
},
400399
}
401400
}
402401

403-
func convertLabels(input []*kubeAutoscaling.DomainLabels) map[string]string {
404-
output := make(map[string]string)
405-
for _, elem := range input {
406-
output[elem.Name] = elem.Value
407-
}
408-
409-
return output
410-
}
411-
412-
func convertTaints(input []*kubeAutoscaling.Taints) []corev1.Taint {
413-
output := []corev1.Taint{}
414-
for _, elem := range input {
415-
output = append(output, corev1.Taint{
416-
Key: elem.Key,
417-
Value: elem.Value,
418-
Effect: corev1.TaintEffect(elem.Effect),
419-
})
420-
}
421-
422-
return output
423-
}
424-
425402
func isCreatedByDatadog(labels map[string]string) bool {
426403
if _, ok := labels[datadogCreatedLabelKey]; ok {
427404
return true

pkg/clusteragent/autoscaling/cluster/controller_test.go

Lines changed: 198 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,120 @@ import (
1111
"testing"
1212

1313
corev1 "k8s.io/api/core/v1"
14-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15-
1614
karpenterv1 "sigs.k8s.io/karpenter/pkg/apis/v1"
15+
16+
"github.com/stretchr/testify/assert"
1717
)
1818

19-
func TestIsCreatedByDatadog(t *testing.T) {
19+
func TestBuildNodePoolSpec(t *testing.T) {
2020
tests := []struct {
21-
name string
22-
nodePool karpenterv1.NodePool
23-
expected bool
21+
name string
22+
minNodePool minNodePool
23+
nodeClassName string
24+
expected karpenterv1.NodePoolSpec
2425
}{
2526
{
26-
name: "no labels are present",
27-
nodePool: karpenterv1.NodePool{
28-
ObjectMeta: metav1.ObjectMeta{
29-
Name: "TestNodePool",
30-
Labels: map[string]string{},
27+
name: "basic",
28+
minNodePool: minNodePool{
29+
name: "default",
30+
recommendedInstanceTypes: []string{"m5.large", "t3.micro"},
31+
labels: map[string]string{"kubernetes.io/arch": "amd64", "kubernetes.io/os": "linux"},
32+
taints: []corev1.Taint{
33+
{
34+
Key: "node",
35+
Value: "test",
36+
Effect: corev1.TaintEffectNoSchedule,
37+
},
3138
},
39+
},
40+
nodeClassName: "default",
41+
expected: karpenterv1.NodePoolSpec{
42+
Template: karpenterv1.NodeClaimTemplate{
43+
Spec: karpenterv1.NodeClaimTemplateSpec{
44+
Taints: []corev1.Taint{
45+
{
46+
Key: "node",
47+
Value: "test",
48+
Effect: corev1.TaintEffectNoSchedule,
49+
},
50+
},
51+
Requirements: []karpenterv1.NodeSelectorRequirementWithMinValues{
52+
{
53+
NodeSelectorRequirement: corev1.NodeSelectorRequirement{
54+
Key: "kubernetes.io/os",
55+
Operator: corev1.NodeSelectorOpIn,
56+
Values: []string{"linux"},
57+
},
58+
},
59+
{
60+
NodeSelectorRequirement: corev1.NodeSelectorRequirement{
61+
Key: "kubernetes.io/arch",
62+
Operator: corev1.NodeSelectorOpIn,
63+
Values: []string{"amd64"},
64+
},
65+
},
66+
{
67+
NodeSelectorRequirement: corev1.NodeSelectorRequirement{
68+
Key: corev1.LabelInstanceTypeStable,
69+
Operator: corev1.NodeSelectorOpIn,
70+
Values: []string{"m5.large", "t3.micro"},
71+
},
72+
},
73+
},
74+
NodeClassRef: &karpenterv1.NodeClassReference{
75+
Kind: "EC2NodeClass",
76+
Name: "default",
77+
Group: "karpenter.k8s.aws",
78+
},
79+
},
80+
},
81+
},
82+
},
83+
}
84+
85+
for _, tt := range tests {
86+
t.Run(tt.name, func(t *testing.T) {
87+
result := buildNodePoolSpec(tt.minNodePool, tt.nodeClassName)
88+
assert.Equal(t, tt.expected.Template.Spec.Taints, result.Template.Spec.Taints, "Resulting NodePool does not match expected NodePool")
89+
assert.Equal(t, tt.expected.Template.Spec.NodeClassRef, result.Template.Spec.NodeClassRef, "Resulting NodePool does not match expected NodePool")
90+
assert.ElementsMatch(t, tt.expected.Template.Spec.Requirements, result.Template.Spec.Requirements, "Resulting NodePool does not match expected NodePool")
91+
})
92+
}
93+
94+
}
95+
96+
func TestBuildNodePoolPatch(t *testing.T) {
97+
tests := []struct {
98+
name string
99+
nodePool karpenterv1.NodePool
100+
minNodePool minNodePool
101+
expected map[string]interface{}
102+
}{
103+
{
104+
name: "basic",
105+
minNodePool: minNodePool{
106+
name: "default",
107+
recommendedInstanceTypes: []string{"c5.xlarge", "t3.micro"},
108+
labels: map[string]string{"kubernetes.io/arch": "amd64", "kubernetes.io/os": "linux"},
109+
taints: []corev1.Taint{
110+
{
111+
Key: "node",
112+
Value: "test",
113+
Effect: corev1.TaintEffectNoSchedule,
114+
},
115+
},
116+
},
117+
nodePool: karpenterv1.NodePool{
32118
Spec: karpenterv1.NodePoolSpec{
33119
Template: karpenterv1.NodeClaimTemplate{
34120
Spec: karpenterv1.NodeClaimTemplateSpec{
121+
Taints: []corev1.Taint{
122+
{
123+
Key: "node",
124+
Value: "test",
125+
Effect: corev1.TaintEffectNoSchedule,
126+
},
127+
},
35128
Requirements: []karpenterv1.NodeSelectorRequirementWithMinValues{
36129
{
37130
NodeSelectorRequirement: corev1.NodeSelectorRequirement{
@@ -40,17 +133,110 @@ func TestIsCreatedByDatadog(t *testing.T) {
40133
Values: []string{"amd64"},
41134
},
42135
},
136+
{
137+
NodeSelectorRequirement: corev1.NodeSelectorRequirement{
138+
Key: "kubernetes.io/os",
139+
Operator: corev1.NodeSelectorOpIn,
140+
Values: []string{"linux"},
141+
},
142+
},
143+
{
144+
NodeSelectorRequirement: corev1.NodeSelectorRequirement{
145+
Key: corev1.LabelInstanceTypeStable,
146+
Operator: corev1.NodeSelectorOpIn,
147+
Values: []string{"m5.large", "t3.micro"},
148+
},
149+
},
150+
},
151+
NodeClassRef: &karpenterv1.NodeClassReference{
152+
Kind: "EC2NodeClass",
153+
Name: "default",
154+
Group: "karpenter.k8s.aws",
43155
},
44156
},
45157
},
46158
},
47159
},
160+
expected: map[string]interface{}{
161+
"metadata": map[string]interface{}{
162+
"labels": map[string]interface{}{
163+
datadogModifiedLabelKey: "true",
164+
},
165+
},
166+
"spec": map[string]interface{}{
167+
"template": map[string]interface{}{
168+
"spec": map[string]interface{}{
169+
"requirements": []map[string]interface{}{
170+
{
171+
"key": "kubernetes.io/arch",
172+
"operator": "In",
173+
"values": []string{"amd64"},
174+
},
175+
{
176+
"key": "kubernetes.io/os",
177+
"operator": "In",
178+
"values": []string{"linux"},
179+
},
180+
{
181+
"key": corev1.LabelInstanceTypeStable,
182+
"operator": "In",
183+
"values": []string{"c5.xlarge", "t3.micro"},
184+
},
185+
},
186+
},
187+
},
188+
},
189+
},
190+
},
191+
}
192+
193+
for _, tt := range tests {
194+
t.Run(tt.name, func(t *testing.T) {
195+
result := buildNodePoolPatch(&tt.nodePool, tt.minNodePool)
196+
assert.Equal(t, tt.expected, result, "Resulting patch does not match expected patch")
197+
})
198+
}
199+
200+
}
201+
202+
func TestIsCreatedByDatadog(t *testing.T) {
203+
tests := []struct {
204+
name string
205+
labels map[string]string
206+
expected bool
207+
}{
208+
{
209+
name: "no labels are present",
210+
labels: map[string]string{},
211+
expected: false,
212+
},
213+
{
214+
name: "other labels are present",
215+
labels: map[string]string{
216+
"otherLabel": "otherValue",
217+
},
218+
expected: false,
219+
},
220+
{
221+
name: "created label is present",
222+
labels: map[string]string{
223+
datadogCreatedLabelKey: "true",
224+
},
225+
expected: true,
226+
},
227+
{
228+
name: "created and other label is present",
229+
labels: map[string]string{
230+
datadogCreatedLabelKey: "true",
231+
"otherLabel": "otherValue",
232+
},
233+
expected: true,
48234
},
49235
}
50236

51237
for _, tt := range tests {
52238
t.Run(tt.name, func(t *testing.T) {
53-
result := isCreatedByDatadog(tt.nodePool)
239+
result := isCreatedByDatadog(tt.labels)
54240
if result != tt.expected {
55241
t.Errorf("isCreatedByDatadog() = %v, want %v", result, tt.expected)
56242
}

0 commit comments

Comments
 (0)