• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zalando-incubator / stackset-controller / 7049646362

30 Nov 2023 04:47PM UTC coverage: 73.237% (+0.05%) from 73.186%
7049646362

Pull #520

github

linki
also check that the data is correctly copied
Pull Request #520: Reference ConfigMap for per stack versioning

79 of 107 new or added lines in 4 files covered. (73.83%)

2 existing lines in 1 file now uncovered.

2326 of 3176 relevant lines covered (73.24%)

0.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.09
/controller/stack_resources.go
1
package controller
2

3
import (
4
        "context"
5
        "fmt"
6
        "reflect"
7
        "sort"
8

9
        rgv1 "github.com/szuecs/routegroup-client/apis/zalando.org/v1"
10
        zv1 "github.com/zalando-incubator/stackset-controller/pkg/apis/zalando.org/v1"
11
        "github.com/zalando-incubator/stackset-controller/pkg/core"
12
        apps "k8s.io/api/apps/v1"
13
        v2 "k8s.io/api/autoscaling/v2"
14
        apiv1 "k8s.io/api/core/v1"
15
        networking "k8s.io/api/networking/v1"
16
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
)
18

19
func pint32Equal(p1, p2 *int32) bool {
1✔
20
        if p1 == nil && p2 == nil {
1✔
21
                return true
×
22
        }
×
23
        if p1 != nil && p2 != nil {
2✔
24
                return *p1 == *p2
1✔
25
        }
1✔
26
        return false
×
27
}
28

29
// There are HPA metrics that depend on annotations to work properly,
30
// e.g. External RPS metric, this verification provides a way to verify
31
// all relevant annotations are actually up to date.
32
func areHPAAnnotationsUpToDate(updated, existing *v2.HorizontalPodAutoscaler) bool {
1✔
33
        if len(updated.Annotations) != len(existing.Annotations) {
1✔
34
                return false
×
35
        }
×
36

37
        for k, v := range updated.Annotations {
2✔
38
                if k == "stackset-controller.zalando.org/stack-generation" {
2✔
39
                        continue
1✔
40
                }
41

42
                existingValue, ok := existing.Annotations[k]
1✔
43
                if ok && existingValue == v {
2✔
44
                        continue
1✔
45
                }
46

47
                return false
1✔
48
        }
49

50
        return true
1✔
51
}
52

53
// syncObjectMeta copies metadata elements such as labels or annotations from source to target
54
func syncObjectMeta(target, source metav1.Object) {
1✔
55
        target.SetLabels(source.GetLabels())
1✔
56
        target.SetAnnotations(source.GetAnnotations())
1✔
57
}
1✔
58

59
// equalResourceList compares existing Resources with list of
60
// ConfigurationResources to be created for Stack
61
func equalResourceList(
62
        existing []*apiv1.ConfigMap,
63
        defined []zv1.ConfigurationResourcesSpec,
64
) bool {
1✔
65
        var existingName []string
1✔
66
        for _, e := range existing {
2✔
67
                existingName = append(existingName, e.Name)
1✔
68
        }
1✔
69
        var crName []string
1✔
70
        for _, cr := range defined {
2✔
71
                crName = append(crName, cr.ConfigMapRef.Name)
1✔
72
        }
1✔
73

74
        sort.Strings(existingName)
1✔
75
        sort.Strings(crName)
1✔
76

1✔
77
        return reflect.DeepEqual(existingName, crName)
1✔
78
}
79

80
func (c *StackSetController) ReconcileStackDeployment(ctx context.Context, stack *zv1.Stack, existing *apps.Deployment, generateUpdated func() *apps.Deployment) error {
1✔
81
        deployment := generateUpdated()
1✔
82

1✔
83
        // Create new deployment
1✔
84
        if existing == nil {
2✔
85
                _, err := c.client.AppsV1().Deployments(deployment.Namespace).Create(ctx, deployment, metav1.CreateOptions{})
1✔
86
                if err != nil {
1✔
87
                        return err
×
88
                }
×
89
                c.recorder.Eventf(
1✔
90
                        stack,
1✔
91
                        apiv1.EventTypeNormal,
1✔
92
                        "CreatedDeployment",
1✔
93
                        "Created Deployment %s",
1✔
94
                        deployment.Name)
1✔
95
                return nil
1✔
96
        }
97

98
        // Check if we need to update the deployment
99
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) && pint32Equal(existing.Spec.Replicas, deployment.Spec.Replicas) {
2✔
100
                return nil
1✔
101
        }
1✔
102

103
        updated := existing.DeepCopy()
1✔
104
        syncObjectMeta(updated, deployment)
1✔
105
        updated.Spec = deployment.Spec
1✔
106
        updated.Spec.Selector = existing.Spec.Selector
1✔
107

1✔
108
        _, err := c.client.AppsV1().Deployments(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
109
        if err != nil {
1✔
110
                return err
×
111
        }
×
112
        c.recorder.Eventf(
1✔
113
                stack,
1✔
114
                apiv1.EventTypeNormal,
1✔
115
                "UpdatedDeployment",
1✔
116
                "Updated Deployment %s",
1✔
117
                deployment.Name)
1✔
118
        return nil
1✔
119
}
120

121
func (c *StackSetController) ReconcileStackHPA(ctx context.Context, stack *zv1.Stack, existing *v2.HorizontalPodAutoscaler, generateUpdated func() (*v2.HorizontalPodAutoscaler, error)) error {
1✔
122
        hpa, err := generateUpdated()
1✔
123
        if err != nil {
1✔
124
                return err
×
125
        }
×
126

127
        // HPA removed
128
        if hpa == nil {
2✔
129
                if existing != nil {
2✔
130
                        err := c.client.AutoscalingV2().HorizontalPodAutoscalers(existing.Namespace).Delete(ctx, existing.Name, metav1.DeleteOptions{})
1✔
131
                        if err != nil {
1✔
132
                                return err
×
133
                        }
×
134
                        c.recorder.Eventf(
1✔
135
                                stack,
1✔
136
                                apiv1.EventTypeNormal,
1✔
137
                                "DeletedHPA",
1✔
138
                                "Deleted HPA %s",
1✔
139
                                existing.Namespace)
1✔
140
                }
141
                return nil
1✔
142
        }
143

144
        // Create new HPA
145
        if existing == nil {
2✔
146
                _, err := c.client.AutoscalingV2().HorizontalPodAutoscalers(hpa.Namespace).Create(ctx, hpa, metav1.CreateOptions{})
1✔
147
                if err != nil {
1✔
148
                        return err
×
149
                }
×
150
                c.recorder.Eventf(
1✔
151
                        stack,
1✔
152
                        apiv1.EventTypeNormal,
1✔
153
                        "CreatedHPA",
1✔
154
                        "Created HPA %s",
1✔
155
                        hpa.Name)
1✔
156
                return nil
1✔
157
        }
158

159
        // Check if we need to update the HPA
160
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) &&
1✔
161
                pint32Equal(existing.Spec.MinReplicas, hpa.Spec.MinReplicas) &&
1✔
162
                areHPAAnnotationsUpToDate(hpa, existing) {
2✔
163
                return nil
1✔
164
        }
1✔
165

166
        updated := existing.DeepCopy()
1✔
167
        syncObjectMeta(updated, hpa)
1✔
168
        updated.Spec = hpa.Spec
1✔
169

1✔
170
        _, err = c.client.AutoscalingV2().HorizontalPodAutoscalers(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
171
        if err != nil {
1✔
172
                return err
×
173
        }
×
174
        c.recorder.Eventf(
1✔
175
                stack,
1✔
176
                apiv1.EventTypeNormal,
1✔
177
                "UpdatedHPA",
1✔
178
                "Updated HPA %s",
1✔
179
                hpa.Name)
1✔
180
        return nil
1✔
181
}
182

183
func (c *StackSetController) ReconcileStackService(ctx context.Context, stack *zv1.Stack, existing *apiv1.Service, generateUpdated func() (*apiv1.Service, error)) error {
1✔
184
        service, err := generateUpdated()
1✔
185
        if err != nil {
1✔
186
                return err
×
187
        }
×
188

189
        // Create new service
190
        if existing == nil {
2✔
191
                _, err := c.client.CoreV1().Services(service.Namespace).Create(ctx, service, metav1.CreateOptions{})
1✔
192
                if err != nil {
1✔
193
                        return err
×
194
                }
×
195
                c.recorder.Eventf(
1✔
196
                        stack,
1✔
197
                        apiv1.EventTypeNormal,
1✔
198
                        "CreatedService",
1✔
199
                        "Created Service %s",
1✔
200
                        service.Name)
1✔
201
                return nil
1✔
202
        }
203

204
        // Check if we need to update the service
205
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) {
2✔
206
                return nil
1✔
207
        }
1✔
208

209
        updated := existing.DeepCopy()
1✔
210
        syncObjectMeta(updated, service)
1✔
211
        updated.Spec = service.Spec
1✔
212
        updated.Spec.ClusterIP = existing.Spec.ClusterIP // ClusterIP is immutable
1✔
213

1✔
214
        _, err = c.client.CoreV1().Services(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
215
        if err != nil {
1✔
216
                return err
×
217
        }
×
218
        c.recorder.Eventf(
1✔
219
                stack,
1✔
220
                apiv1.EventTypeNormal,
1✔
221
                "UpdatedService",
1✔
222
                "Updated Service %s",
1✔
223
                service.Name)
1✔
224
        return nil
1✔
225
}
226

227
func (c *StackSetController) ReconcileStackIngress(ctx context.Context, stack *zv1.Stack, existing *networking.Ingress, generateUpdated func() (*networking.Ingress, error)) error {
1✔
228
        ingress, err := generateUpdated()
1✔
229
        if err != nil {
1✔
230
                return err
×
231
        }
×
232

233
        // Ingress removed
234
        if ingress == nil {
2✔
235
                if existing != nil {
2✔
236
                        err := c.client.NetworkingV1().Ingresses(existing.Namespace).Delete(ctx, existing.Name, metav1.DeleteOptions{})
1✔
237
                        if err != nil {
1✔
238
                                return err
×
239
                        }
×
240
                        c.recorder.Eventf(
1✔
241
                                stack,
1✔
242
                                apiv1.EventTypeNormal,
1✔
243
                                "DeletedIngress",
1✔
244
                                "Deleted Ingress %s",
1✔
245
                                existing.Namespace)
1✔
246
                }
247
                return nil
1✔
248
        }
249

250
        // Create new Ingress
251
        if existing == nil {
2✔
252
                _, err := c.client.NetworkingV1().Ingresses(ingress.Namespace).Create(ctx, ingress, metav1.CreateOptions{})
1✔
253
                if err != nil {
1✔
254
                        return err
×
255
                }
×
256
                c.recorder.Eventf(
1✔
257
                        stack,
1✔
258
                        apiv1.EventTypeNormal,
1✔
259
                        "CreatedIngress",
1✔
260
                        "Created Ingress %s",
1✔
261
                        ingress.Name)
1✔
262
                return nil
1✔
263
        }
264

265
        // Check if we need to update the Ingress
266
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) {
2✔
267
                return nil
1✔
268
        }
1✔
269

270
        updated := existing.DeepCopy()
1✔
271
        syncObjectMeta(updated, ingress)
1✔
272
        updated.Spec = ingress.Spec
1✔
273

1✔
274
        _, err = c.client.NetworkingV1().Ingresses(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
275
        if err != nil {
1✔
276
                return err
×
277
        }
×
278
        c.recorder.Eventf(
1✔
279
                stack,
1✔
280
                apiv1.EventTypeNormal,
1✔
281
                "UpdatedIngress",
1✔
282
                "Updated Ingress %s",
1✔
283
                ingress.Name)
1✔
284
        return nil
1✔
285
}
286

287
func (c *StackSetController) ReconcileStackRouteGroup(ctx context.Context, stack *zv1.Stack, existing *rgv1.RouteGroup, generateUpdated func() (*rgv1.RouteGroup, error)) error {
1✔
288
        routegroup, err := generateUpdated()
1✔
289
        if err != nil {
1✔
290
                return err
×
291
        }
×
292

293
        // RouteGroup removed
294
        if routegroup == nil {
2✔
295
                if existing != nil {
2✔
296
                        err := c.client.RouteGroupV1().RouteGroups(existing.Namespace).Delete(ctx, existing.Name, metav1.DeleteOptions{})
1✔
297
                        if err != nil {
1✔
298
                                return err
×
299
                        }
×
300
                        c.recorder.Eventf(
1✔
301
                                stack,
1✔
302
                                apiv1.EventTypeNormal,
1✔
303
                                "DeletedRouteGroup",
1✔
304
                                "Deleted RouteGroup %s",
1✔
305
                                existing.Namespace)
1✔
306
                }
307
                return nil
1✔
308
        }
309

310
        // Create new RouteGroup
311
        if existing == nil {
2✔
312
                _, err := c.client.RouteGroupV1().RouteGroups(routegroup.Namespace).Create(ctx, routegroup, metav1.CreateOptions{})
1✔
313
                if err != nil {
1✔
314
                        return err
×
315
                }
×
316
                c.recorder.Eventf(
1✔
317
                        stack,
1✔
318
                        apiv1.EventTypeNormal,
1✔
319
                        "CreatedRouteGroup",
1✔
320
                        "Created RouteGroup %s",
1✔
321
                        routegroup.Name)
1✔
322
                return nil
1✔
323
        }
324

325
        // Check if we need to update the RouteGroup
326
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) {
2✔
327
                return nil
1✔
328
        }
1✔
329

330
        updated := existing.DeepCopy()
1✔
331
        syncObjectMeta(updated, routegroup)
1✔
332
        updated.Spec = routegroup.Spec
1✔
333

1✔
334
        _, err = c.client.RouteGroupV1().RouteGroups(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
335
        if err != nil {
1✔
336
                return err
×
337
        }
×
338
        c.recorder.Eventf(
1✔
339
                stack,
1✔
340
                apiv1.EventTypeNormal,
1✔
341
                "UpdatedRouteGroup",
1✔
342
                "Updated RouteGroup %s",
1✔
343
                routegroup.Name)
1✔
344
        return nil
1✔
345
}
346

347
// ReconcileStackConfigMap will update the named user-provided ConfigMap to be
348
// attached to the Stack by ownerReferences, when a list of Configuration
349
// Resources are defined on the Stack template.
350
//
351
// The provided ConfigMap name must be prefixed by the Stack name.
352
// eg: Stack: myapp-v1 ConfigMap: myapp-v1-my-config
353
//
354
// User update of running versioned ConfigMaps is not encouraged but is allowed
355
// on consideration of emergency needs. Similarly, addition of ConfigMaps to
356
// running resources is also allowed, so the method checks for changes on the
357
// ConfigurationResources to ensure all listed ConfigMaps are properly linked
358
// to the Stack.
359
func (c *StackSetController) ReconcileStackConfigMap(
360
        ctx context.Context,
361
        stack *zv1.Stack,
362
        existing []*apiv1.ConfigMap,
363
        updateObjMeta func(*metav1.ObjectMeta) *metav1.ObjectMeta,
364
) error {
1✔
365
        if stack.Spec.ConfigurationResources == nil {
1✔
NEW
366
                return nil
×
NEW
367
        }
×
368

369
        if equalResourceList(existing, stack.Spec.ConfigurationResources) {
2✔
370
                return nil
1✔
371
        }
1✔
372

373
        for _, rsc := range stack.Spec.ConfigurationResources {
2✔
374
                rscName := rsc.ConfigMapRef.Name
1✔
375

1✔
376
                // ensure that ConfigurationResources are prefixed by Stack name.
1✔
377
                if err := validateConfigurationResourceNames(stack); err != nil {
1✔
NEW
378
                        return err
×
NEW
379
                }
×
380

381
                configMap, err := c.client.CoreV1().ConfigMaps(stack.Namespace).
1✔
382
                        Get(ctx, rscName, metav1.GetOptions{})
1✔
383
                if err != nil {
1✔
NEW
384
                        return err
×
NEW
385
                }
×
386

387
                if configMap.OwnerReferences != nil {
1✔
NEW
388
                        for _, owner := range configMap.OwnerReferences {
×
NEW
389
                                if owner.UID != stack.UID {
×
NEW
390
                                        return fmt.Errorf("ConfigMap already owned by other resource. "+
×
NEW
391
                                                "ConfigMap: %s, Stack: %s", rscName, stack.Name)
×
NEW
392
                                }
×
393
                        }
NEW
394
                        continue
×
395
                }
396

397
                objectMeta := updateObjMeta(&configMap.ObjectMeta)
1✔
398
                configMap.ObjectMeta = *objectMeta
1✔
399

1✔
400
                _, err = c.client.CoreV1().ConfigMaps(configMap.Namespace).
1✔
401
                        Update(ctx, configMap, metav1.UpdateOptions{})
1✔
402
                if err != nil {
1✔
NEW
403
                        return err
×
NEW
404
                }
×
405
                c.recorder.Eventf(
1✔
406
                        stack,
1✔
407
                        apiv1.EventTypeNormal,
1✔
408
                        "UpdatedConfigMap",
1✔
409
                        "Updated ConfigMap %s",
1✔
410
                        configMap.Name,
1✔
411
                )
1✔
412
        }
413
        return nil
1✔
414
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc