Skip to content

Commit d0bbc49

Browse files
author
Simon Emms
committed
[installer]: create function to add custom annotations for k8s resources
1 parent 3e14918 commit d0bbc49

File tree

2 files changed

+355
-0
lines changed

2 files changed

+355
-0
lines changed

install/installer/pkg/common/common.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111
"io"
1212
"math/rand"
13+
"sort"
1314
"strconv"
1415
"strings"
1516

@@ -35,6 +36,61 @@ func DefaultLabels(component string) map[string]string {
3536
}
3637
}
3738

39+
func ComponentAnnotation(ctx *RenderContext, component string, typeMeta metav1.TypeMeta, existingAnnotations ...func() map[string]string) map[string]string {
40+
// Get the metadata kind in lowercase
41+
kind := strings.ToLower(typeMeta.Kind)
42+
43+
annotations := make(map[string]string)
44+
45+
// Start with any existing annotations
46+
for _, e := range existingAnnotations {
47+
for k, v := range e() {
48+
annotations[k] = v
49+
}
50+
}
51+
52+
if ctx.Config.Components != nil && ctx.Config.Components.Annotations != nil {
53+
annotationKeys := make([]string, 0, len(*ctx.Config.Components.Annotations))
54+
for k := range *ctx.Config.Components.Annotations {
55+
annotationKeys = append(annotationKeys, k)
56+
}
57+
// Ensure that "*" comes first
58+
sort.Strings(annotationKeys)
59+
60+
for _, annotationKindKey := range annotationKeys {
61+
annotationKindData := (*ctx.Config.Components.Annotations)[annotationKindKey]
62+
63+
if annotationKindKey == "*" || annotationKindKey == kind {
64+
// The key here matches this resource's "kind"
65+
componentKeys := make([]string, 0, len(annotationKindData))
66+
for k := range annotationKindData {
67+
componentKeys = append(componentKeys, k)
68+
}
69+
// Ensure that "*" comes first
70+
sort.Strings(componentKeys)
71+
72+
for _, componentName := range componentKeys {
73+
annotationMap := annotationKindData[componentName]
74+
if componentName == "*" || componentName == component {
75+
// The key here match this resource's "name" - we can now look to add these to the annotation map
76+
for k, v := range annotationMap {
77+
if v == "" {
78+
// Delete the key/value pair
79+
delete(annotations, k)
80+
} else {
81+
// Add the key/value
82+
annotations[k] = v
83+
}
84+
}
85+
}
86+
}
87+
}
88+
}
89+
}
90+
91+
return annotations
92+
}
93+
3894
func MergeEnv(envs ...[]corev1.EnvVar) (res []corev1.EnvVar) {
3995
for _, e := range envs {
4096
res = append(res, e...)

install/installer/pkg/common/common_test.go

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ package common_test
55

66
import (
77
"fmt"
8+
"reflect"
9+
"strings"
810
"testing"
911

1012
"github.com/gitpod-io/gitpod/installer/pkg/common"
1113
"github.com/gitpod-io/gitpod/installer/pkg/config/v1"
1214
"github.com/gitpod-io/gitpod/installer/pkg/config/versions"
1315
"github.com/stretchr/testify/require"
1416
corev1 "k8s.io/api/core/v1"
17+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1518
)
1619

1720
func TestKubeRBACProxyContainer_DefaultPorts(t *testing.T) {
@@ -44,3 +47,299 @@ func TestKubeRBACProxyContainerWithConfig(t *testing.T) {
4447
{Name: "metrics", ContainerPort: listenPort},
4548
}, container.Ports)
4649
}
50+
51+
func TestComponentAnnotation(t *testing.T) {
52+
testCases := []struct {
53+
Annotations config.CustomOverride
54+
Name string
55+
Component string
56+
TypeMeta metav1.TypeMeta
57+
ExistingAnnotations []func() map[string]string
58+
Expect map[string]string
59+
}{
60+
{
61+
Annotations: nil,
62+
Name: "no annotations",
63+
Component: "component",
64+
TypeMeta: common.TypeMetaDeployment,
65+
Expect: map[string]string{},
66+
},
67+
{
68+
Annotations: config.CustomOverride{},
69+
Name: "empty annotations",
70+
Component: "component",
71+
TypeMeta: common.TypeMetaDeployment,
72+
Expect: map[string]string{},
73+
},
74+
{
75+
Annotations: config.CustomOverride{
76+
strings.ToLower(common.TypeMetaDeployment.Kind): {},
77+
},
78+
Name: "empty kind annotations",
79+
Component: "component",
80+
TypeMeta: common.TypeMetaDeployment,
81+
Expect: map[string]string{},
82+
},
83+
{
84+
Annotations: config.CustomOverride{
85+
strings.ToLower(common.TypeMetaDeployment.Kind): {
86+
"component": {},
87+
},
88+
},
89+
Name: "empty component annotations",
90+
Component: "component",
91+
TypeMeta: common.TypeMetaDeployment,
92+
Expect: map[string]string{},
93+
},
94+
{
95+
Annotations: config.CustomOverride{
96+
strings.ToLower(common.TypeMetaBatchCronJob.Kind): {
97+
"component": {
98+
"hello": "world",
99+
},
100+
},
101+
},
102+
Name: "ignore different kind annotations",
103+
Component: "component",
104+
TypeMeta: common.TypeMetaDeployment,
105+
Expect: map[string]string{},
106+
},
107+
{
108+
Annotations: config.CustomOverride{
109+
strings.ToLower(common.TypeMetaDeployment.Kind): {
110+
"component2": {
111+
"hello": "world",
112+
},
113+
},
114+
},
115+
Name: "ignore same kind different name annotations",
116+
Component: "component",
117+
TypeMeta: common.TypeMetaDeployment,
118+
Expect: map[string]string{},
119+
},
120+
{
121+
Annotations: config.CustomOverride{
122+
strings.ToLower(common.TypeMetaDeployment.Kind): {
123+
"component": {
124+
"hello": "world",
125+
},
126+
},
127+
},
128+
Name: "single component annotations",
129+
Component: "component",
130+
TypeMeta: common.TypeMetaDeployment,
131+
Expect: map[string]string{
132+
"hello": "world",
133+
},
134+
},
135+
{
136+
Annotations: config.CustomOverride{
137+
strings.ToLower(common.TypeMetaDeployment.Kind): {
138+
"component": {
139+
"key1": "value1",
140+
"key2": "value2",
141+
},
142+
},
143+
},
144+
Name: "multiple component annotations",
145+
Component: "component",
146+
TypeMeta: common.TypeMetaDeployment,
147+
Expect: map[string]string{
148+
"key1": "value1",
149+
"key2": "value2",
150+
},
151+
},
152+
{
153+
Annotations: config.CustomOverride{
154+
"*": {},
155+
},
156+
Name: "empty wildcard kind annotations",
157+
Component: "component",
158+
TypeMeta: common.TypeMetaDeployment,
159+
Expect: map[string]string{},
160+
},
161+
{
162+
Annotations: config.CustomOverride{
163+
"*": {
164+
"component": {},
165+
},
166+
},
167+
Name: "empty wildcard kind component annotations",
168+
Component: "component",
169+
TypeMeta: common.TypeMetaDeployment,
170+
Expect: map[string]string{},
171+
},
172+
{
173+
Annotations: config.CustomOverride{
174+
"*": {
175+
"component": {
176+
"hello": "world",
177+
},
178+
},
179+
},
180+
Name: "single wildcard kind named component annotations",
181+
Component: "component",
182+
TypeMeta: common.TypeMetaDeployment,
183+
Expect: map[string]string{
184+
"hello": "world",
185+
},
186+
},
187+
{
188+
Annotations: config.CustomOverride{
189+
"*": {
190+
"component": {
191+
"key1": "value1",
192+
"key2": "value2",
193+
},
194+
},
195+
},
196+
Name: "multiple wildcard kind named component annotations",
197+
Component: "component",
198+
TypeMeta: common.TypeMetaDeployment,
199+
Expect: map[string]string{
200+
"key1": "value1",
201+
"key2": "value2",
202+
},
203+
},
204+
{
205+
Annotations: config.CustomOverride{
206+
"*": {
207+
"*": {
208+
"hello": "world",
209+
},
210+
},
211+
},
212+
Name: "single wildcard component annotations",
213+
Component: "component",
214+
TypeMeta: common.TypeMetaDeployment,
215+
Expect: map[string]string{
216+
"hello": "world",
217+
},
218+
},
219+
{
220+
Annotations: config.CustomOverride{
221+
"*": {
222+
"*": {
223+
"key1": "value1",
224+
},
225+
"component": {
226+
"key2": "value2",
227+
},
228+
},
229+
},
230+
Name: "single wildcard component annotations",
231+
Component: "component",
232+
TypeMeta: common.TypeMetaDeployment,
233+
Expect: map[string]string{
234+
"key1": "value1",
235+
"key2": "value2",
236+
},
237+
},
238+
{
239+
Annotations: config.CustomOverride{
240+
"*": {
241+
"*": {
242+
"key1": "value1",
243+
},
244+
"component": {
245+
"key2": "value2",
246+
},
247+
},
248+
strings.ToLower(common.TypeMetaDeployment.Kind): {
249+
"*": {
250+
"key3": "value3",
251+
},
252+
"component": {
253+
"key4": "value4",
254+
},
255+
},
256+
},
257+
Name: "wildcard and named annotations",
258+
Component: "component",
259+
TypeMeta: common.TypeMetaDeployment,
260+
Expect: map[string]string{
261+
"key1": "value1",
262+
"key2": "value2",
263+
"key3": "value3",
264+
"key4": "value4",
265+
},
266+
},
267+
{
268+
Annotations: config.CustomOverride{
269+
"*": {
270+
"*": {
271+
"key1": "value1",
272+
"override": "override1",
273+
},
274+
"component": {
275+
"key2": "value2",
276+
"override": "override2",
277+
},
278+
},
279+
strings.ToLower(common.TypeMetaDeployment.Kind): {
280+
"component": {
281+
"key4": "value4",
282+
"override": "override4",
283+
},
284+
"*": {
285+
"key3": "value3",
286+
"override": "override3",
287+
},
288+
},
289+
},
290+
Name: "wildcard and named annotations",
291+
Component: "component",
292+
TypeMeta: common.TypeMetaDeployment,
293+
Expect: map[string]string{
294+
"key1": "value1",
295+
"key2": "value2",
296+
"key3": "value3",
297+
"key4": "value4",
298+
"override": "override4",
299+
},
300+
},
301+
{
302+
Annotations: config.CustomOverride{
303+
"*": {
304+
"*": {
305+
"override": "override",
306+
"delete": "",
307+
},
308+
},
309+
},
310+
Name: "existing annotations overriden",
311+
Component: "component",
312+
TypeMeta: common.TypeMetaDeployment,
313+
ExistingAnnotations: []func() map[string]string{
314+
func() map[string]string {
315+
return map[string]string{
316+
"delete": "original",
317+
"nooverride": "original",
318+
"override": "original",
319+
}
320+
},
321+
},
322+
Expect: map[string]string{
323+
"nooverride": "original",
324+
"override": "override",
325+
},
326+
},
327+
}
328+
329+
for _, testCase := range testCases {
330+
t.Run(testCase.Name, func(t *testing.T) {
331+
ctx, err := common.NewRenderContext(config.Config{
332+
Components: &config.Components{
333+
Annotations: &testCase.Annotations,
334+
},
335+
}, versions.Manifest{}, "test_namespace")
336+
require.NoError(t, err)
337+
338+
result := common.ComponentAnnotation(ctx, testCase.Component, testCase.TypeMeta, testCase.ExistingAnnotations...)
339+
340+
if !reflect.DeepEqual(testCase.Expect, result) {
341+
t.Errorf("expected %v but got %v", testCase.Expect, result)
342+
}
343+
})
344+
}
345+
}

0 commit comments

Comments
 (0)