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

zalando-incubator / stackset-controller / 8156967012

05 Mar 2024 01:19PM UTC coverage: 52.317% (+0.004%) from 52.313%
8156967012

Pull #542

github

linki
temporarily drop feature flags for smaller diff and predictable tests
Pull Request #542: Prevent scaling up stacks without traffic

3 of 3 new or added lines in 1 file covered. (100.0%)

121 existing lines in 2 files now uncovered.

3060 of 5849 relevant lines covered (52.32%)

0.59 hits per line

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

81.76
/controller/stack_resources.go
1
package controller
2

3
import (
4
        "context"
5
        "fmt"
6

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

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

28
// syncObjectMeta copies metadata elements such as labels or annotations from source to target
29
func syncObjectMeta(target, source metav1.Object) {
1✔
30
        target.SetLabels(source.GetLabels())
1✔
31
        target.SetAnnotations(source.GetAnnotations())
1✔
32
}
1✔
33

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

1✔
37
        // Create new deployment
1✔
38
        if existing == nil {
2✔
39
                _, err := c.client.AppsV1().Deployments(deployment.Namespace).Create(ctx, deployment, metav1.CreateOptions{})
1✔
40
                if err != nil {
1✔
UNCOV
41
                        return err
×
UNCOV
42
                }
×
43
                c.recorder.Eventf(
1✔
44
                        stack,
1✔
45
                        apiv1.EventTypeNormal,
1✔
46
                        "CreatedDeployment",
1✔
47
                        "Created Deployment %s",
1✔
48
                        deployment.Name)
1✔
49
                return nil
1✔
50
        }
51

52
        // Check if we need to update the deployment
53
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) && pint32Equal(existing.Spec.Replicas, deployment.Spec.Replicas) {
2✔
54
                return nil
1✔
55
        }
1✔
56

57
        updated := existing.DeepCopy()
1✔
58
        syncObjectMeta(updated, deployment)
1✔
59
        updated.Spec = deployment.Spec
1✔
60
        updated.Spec.Selector = existing.Spec.Selector
1✔
61

1✔
62
        _, err := c.client.AppsV1().Deployments(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
63
        if err != nil {
1✔
UNCOV
64
                return err
×
UNCOV
65
        }
×
66
        c.recorder.Eventf(
1✔
67
                stack,
1✔
68
                apiv1.EventTypeNormal,
1✔
69
                "UpdatedDeployment",
1✔
70
                "Updated Deployment %s",
1✔
71
                deployment.Name)
1✔
72
        return nil
1✔
73
}
74

75
func (c *StackSetController) ReconcileStackHPA(ctx context.Context, stack *zv1.Stack, existing *v2.HorizontalPodAutoscaler, generateUpdated func() (*v2.HorizontalPodAutoscaler, error)) error {
1✔
76
        hpa, err := generateUpdated()
1✔
77
        if err != nil {
1✔
UNCOV
78
                return err
×
UNCOV
79
        }
×
80

81
        // HPA removed
82
        if hpa == nil {
2✔
83
                if existing != nil {
2✔
84
                        err := c.client.AutoscalingV2().HorizontalPodAutoscalers(existing.Namespace).Delete(ctx, existing.Name, metav1.DeleteOptions{})
1✔
85
                        if err != nil {
1✔
UNCOV
86
                                return err
×
UNCOV
87
                        }
×
88
                        c.recorder.Eventf(
1✔
89
                                stack,
1✔
90
                                apiv1.EventTypeNormal,
1✔
91
                                "DeletedHPA",
1✔
92
                                "Deleted HPA %s",
1✔
93
                                existing.Namespace)
1✔
94
                }
95
                return nil
1✔
96
        }
97

98
        // Create new HPA
99
        if existing == nil {
2✔
100
                _, err := c.client.AutoscalingV2().HorizontalPodAutoscalers(hpa.Namespace).Create(ctx, hpa, metav1.CreateOptions{})
1✔
101
                if err != nil {
1✔
UNCOV
102
                        return err
×
UNCOV
103
                }
×
104
                c.recorder.Eventf(
1✔
105
                        stack,
1✔
106
                        apiv1.EventTypeNormal,
1✔
107
                        "CreatedHPA",
1✔
108
                        "Created HPA %s",
1✔
109
                        hpa.Name)
1✔
110
                return nil
1✔
111
        }
112

113
        // Check if we need to update the HPA
114
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) &&
1✔
115
                pint32Equal(existing.Spec.MinReplicas, hpa.Spec.MinReplicas) &&
1✔
116
                core.AreAnnotationsUpToDate(hpa.ObjectMeta, existing.ObjectMeta) {
2✔
117
                return nil
1✔
118
        }
1✔
119

120
        updated := existing.DeepCopy()
1✔
121
        syncObjectMeta(updated, hpa)
1✔
122
        updated.Spec = hpa.Spec
1✔
123

1✔
124
        _, err = c.client.AutoscalingV2().HorizontalPodAutoscalers(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
125
        if err != nil {
1✔
UNCOV
126
                return err
×
UNCOV
127
        }
×
128
        c.recorder.Eventf(
1✔
129
                stack,
1✔
130
                apiv1.EventTypeNormal,
1✔
131
                "UpdatedHPA",
1✔
132
                "Updated HPA %s",
1✔
133
                hpa.Name)
1✔
134
        return nil
1✔
135
}
136

137
func (c *StackSetController) ReconcileStackService(ctx context.Context, stack *zv1.Stack, existing *apiv1.Service, generateUpdated func() (*apiv1.Service, error)) error {
1✔
138
        service, err := generateUpdated()
1✔
139
        if err != nil {
1✔
UNCOV
140
                return err
×
UNCOV
141
        }
×
142

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

158
        // Check if we need to update the service
159
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) {
2✔
160
                return nil
1✔
161
        }
1✔
162

163
        updated := existing.DeepCopy()
1✔
164
        syncObjectMeta(updated, service)
1✔
165
        updated.Spec = service.Spec
1✔
166
        updated.Spec.ClusterIP = existing.Spec.ClusterIP // ClusterIP is immutable
1✔
167

1✔
168
        _, err = c.client.CoreV1().Services(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
169
        if err != nil {
1✔
UNCOV
170
                return err
×
UNCOV
171
        }
×
172
        c.recorder.Eventf(
1✔
173
                stack,
1✔
174
                apiv1.EventTypeNormal,
1✔
175
                "UpdatedService",
1✔
176
                "Updated Service %s",
1✔
177
                service.Name)
1✔
178
        return nil
1✔
179
}
180

181
func (c *StackSetController) ReconcileStackIngress(ctx context.Context, stack *zv1.Stack, existing *networking.Ingress, generateUpdated func() (*networking.Ingress, error)) error {
1✔
182
        ingress, err := generateUpdated()
1✔
183
        if err != nil {
1✔
UNCOV
184
                return err
×
UNCOV
185
        }
×
186

187
        // Ingress removed
188
        if ingress == nil {
2✔
189
                if existing != nil {
2✔
190
                        err := c.client.NetworkingV1().Ingresses(existing.Namespace).Delete(ctx, existing.Name, metav1.DeleteOptions{})
1✔
191
                        if err != nil {
1✔
UNCOV
192
                                return err
×
UNCOV
193
                        }
×
194
                        c.recorder.Eventf(
1✔
195
                                stack,
1✔
196
                                apiv1.EventTypeNormal,
1✔
197
                                "DeletedIngress",
1✔
198
                                "Deleted Ingress %s",
1✔
199
                                existing.Namespace)
1✔
200
                }
201
                return nil
1✔
202
        }
203

204
        // Create new Ingress
205
        if existing == nil {
2✔
206
                _, err := c.client.NetworkingV1().Ingresses(ingress.Namespace).Create(ctx, ingress, metav1.CreateOptions{})
1✔
207
                if err != nil {
1✔
UNCOV
208
                        return err
×
UNCOV
209
                }
×
210
                c.recorder.Eventf(
1✔
211
                        stack,
1✔
212
                        apiv1.EventTypeNormal,
1✔
213
                        "CreatedIngress",
1✔
214
                        "Created Ingress %s",
1✔
215
                        ingress.Name)
1✔
216
                return nil
1✔
217
        }
218

219
        // Check if we need to update the Ingress
220
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) &&
1✔
221
                core.AreAnnotationsUpToDate(ingress.ObjectMeta, existing.ObjectMeta) {
2✔
222

1✔
223
                return nil
1✔
224
        }
1✔
225

226
        updated := existing.DeepCopy()
1✔
227
        syncObjectMeta(updated, ingress)
1✔
228
        updated.Spec = ingress.Spec
1✔
229

1✔
230
        _, err = c.client.NetworkingV1().Ingresses(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
231
        if err != nil {
1✔
UNCOV
232
                return err
×
UNCOV
233
        }
×
234
        c.recorder.Eventf(
1✔
235
                stack,
1✔
236
                apiv1.EventTypeNormal,
1✔
237
                "UpdatedIngress",
1✔
238
                "Updated Ingress %s",
1✔
239
                ingress.Name)
1✔
240
        return nil
1✔
241
}
242

243
func (c *StackSetController) ReconcileStackRouteGroup(ctx context.Context, stack *zv1.Stack, existing *rgv1.RouteGroup, generateUpdated func() (*rgv1.RouteGroup, error)) error {
1✔
244
        routegroup, err := generateUpdated()
1✔
245
        if err != nil {
1✔
UNCOV
246
                return err
×
UNCOV
247
        }
×
248

249
        // RouteGroup removed
250
        if routegroup == nil {
2✔
251
                if existing != nil {
2✔
252
                        err := c.client.RouteGroupV1().RouteGroups(existing.Namespace).Delete(ctx, existing.Name, metav1.DeleteOptions{})
1✔
253
                        if err != nil {
1✔
UNCOV
254
                                return err
×
UNCOV
255
                        }
×
256
                        c.recorder.Eventf(
1✔
257
                                stack,
1✔
258
                                apiv1.EventTypeNormal,
1✔
259
                                "DeletedRouteGroup",
1✔
260
                                "Deleted RouteGroup %s",
1✔
261
                                existing.Namespace)
1✔
262
                }
263
                return nil
1✔
264
        }
265

266
        // Create new RouteGroup
267
        if existing == nil {
2✔
268
                _, err := c.client.RouteGroupV1().RouteGroups(routegroup.Namespace).Create(ctx, routegroup, metav1.CreateOptions{})
1✔
269
                if err != nil {
1✔
UNCOV
270
                        return err
×
UNCOV
271
                }
×
272
                c.recorder.Eventf(
1✔
273
                        stack,
1✔
274
                        apiv1.EventTypeNormal,
1✔
275
                        "CreatedRouteGroup",
1✔
276
                        "Created RouteGroup %s",
1✔
277
                        routegroup.Name)
1✔
278
                return nil
1✔
279
        }
280

281
        // Check if we need to update the RouteGroup
282
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) &&
1✔
283
                equality.Semantic.DeepEqual(routegroup.Spec, existing.Spec) &&
1✔
284
                core.AreAnnotationsUpToDate(
1✔
285
                        routegroup.ObjectMeta,
1✔
286
                        existing.ObjectMeta,
1✔
287
                ) {
2✔
288

1✔
289
                return nil
1✔
290
        }
1✔
291

292
        updated := existing.DeepCopy()
1✔
293
        syncObjectMeta(updated, routegroup)
1✔
294
        updated.Spec = routegroup.Spec
1✔
295

1✔
296
        _, err = c.client.RouteGroupV1().RouteGroups(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
297
        if err != nil {
1✔
UNCOV
298
                return err
×
UNCOV
299
        }
×
300
        c.recorder.Eventf(
1✔
301
                stack,
1✔
302
                apiv1.EventTypeNormal,
1✔
303
                "UpdatedRouteGroup",
1✔
304
                "Updated RouteGroup %s",
1✔
305
                routegroup.Name)
1✔
306
        return nil
1✔
307
}
308

309
// ReconcileStackConfigMapRefs will update the named user-provided ConfigMap to be
310
// attached to the Stack by ownerReferences, when a list of Configuration
311
// Resources are defined on the Stack template.
312
//
313
// The provided ConfigMap name must be prefixed by the Stack name.
314
// eg: Stack: myapp-v1 ConfigMap: myapp-v1-my-config
315
//
316
// User update of running versioned ConfigMaps is not encouraged but is allowed
317
// on consideration of emergency needs. Similarly, addition of ConfigMaps to
318
// running resources is also allowed, so the method checks for changes on the
319
// ConfigurationResources to ensure all listed ConfigMaps are properly linked
320
// to the Stack.
321
func (c *StackSetController) ReconcileStackConfigMapRefs(
322
        ctx context.Context,
323
        stack *zv1.Stack,
324
        existing []*apiv1.ConfigMap,
325
        updateObjMeta func(*metav1.ObjectMeta) *metav1.ObjectMeta,
326
) error {
1✔
327
        for _, rsc := range stack.Spec.ConfigurationResources {
2✔
328
                if !rsc.IsConfigMapRef() {
1✔
UNCOV
329
                        continue
×
330
                }
331

332
                if err := validateConfigurationResourceName(stack.Name, rsc.GetName()); err != nil {
1✔
UNCOV
333
                        return err
×
UNCOV
334
                }
×
335

336
                configMap, err := c.client.CoreV1().ConfigMaps(stack.Namespace).
1✔
337
                        Get(ctx, rsc.GetName(), metav1.GetOptions{})
1✔
338
                if err != nil {
1✔
UNCOV
339
                        return err
×
UNCOV
340
                }
×
341

342
                if configMap.OwnerReferences != nil {
2✔
343
                        for _, owner := range configMap.OwnerReferences {
2✔
344
                                if owner.UID != stack.UID {
1✔
UNCOV
345
                                        return fmt.Errorf("ConfigMap already owned by other resource. "+
×
346
                                                "ConfigMap: %s, Stack: %s", rsc.GetName(), stack.Name)
×
347
                                }
×
348
                        }
349
                        continue
1✔
350
                }
351

352
                objectMeta := updateObjMeta(&configMap.ObjectMeta)
1✔
353
                configMap.ObjectMeta = *objectMeta
1✔
354

1✔
355
                _, err = c.client.CoreV1().ConfigMaps(configMap.Namespace).
1✔
356
                        Update(ctx, configMap, metav1.UpdateOptions{})
1✔
357
                if err != nil {
1✔
UNCOV
358
                        return err
×
UNCOV
359
                }
×
360
                c.recorder.Eventf(
1✔
361
                        stack,
1✔
362
                        apiv1.EventTypeNormal,
1✔
363
                        "UpdatedConfigMap",
1✔
364
                        "Updated ConfigMap %s",
1✔
365
                        configMap.Name,
1✔
366
                )
1✔
367
        }
368
        return nil
1✔
369
}
370

371
// ReconcileStackSecretRefs will update the named user-provided Secret to be
372
// attached to the Stack by ownerReferences, when a list of Configuration
373
// Resources are defined on the Stack template.
374
//
375
// The provided Secret name must be prefixed by the Stack name.
376
// eg: Stack: myapp-v1 Secret: myapp-v1-my-secret
377
//
378
// User update of running versioned Secrets is not encouraged but is allowed
379
// on consideration of emergency needs. Similarly, addition of Secrets to
380
// running resources is also allowed, so the method checks for changes on the
381
// ConfigurationResources to ensure all listed Secrets are properly linked
382
// to the Stack.
383
func (c *StackSetController) ReconcileStackSecretRefs(
384
        ctx context.Context,
385
        stack *zv1.Stack,
386
        existing []*apiv1.Secret,
387
        updateObjMeta func(*metav1.ObjectMeta) *metav1.ObjectMeta,
388
) error {
1✔
389
        for _, rsc := range stack.Spec.ConfigurationResources {
2✔
390
                if !rsc.IsSecretRef() {
2✔
391
                        continue
1✔
392
                }
393

394
                if err := validateConfigurationResourceName(stack.Name, rsc.GetName()); err != nil {
1✔
UNCOV
395
                        return err
×
UNCOV
396
                }
×
397

398
                secret, err := c.client.CoreV1().Secrets(stack.Namespace).
1✔
399
                        Get(ctx, rsc.GetName(), metav1.GetOptions{})
1✔
400
                if err != nil {
1✔
UNCOV
401
                        return err
×
UNCOV
402
                }
×
403

404
                if secret.OwnerReferences != nil {
2✔
405
                        for _, owner := range secret.OwnerReferences {
2✔
406
                                if owner.UID != stack.UID {
1✔
UNCOV
407
                                        return fmt.Errorf("Secret already owned by other resource. "+
×
UNCOV
408
                                                "Secret: %s, Stack: %s", rsc.GetName(), stack.Name)
×
UNCOV
409
                                }
×
410
                        }
411
                        continue
1✔
412
                }
413

414
                objectMeta := updateObjMeta(&secret.ObjectMeta)
1✔
415
                secret.ObjectMeta = *objectMeta
1✔
416

1✔
417
                _, err = c.client.CoreV1().Secrets(secret.Namespace).
1✔
418
                        Update(ctx, secret, metav1.UpdateOptions{})
1✔
419
                if err != nil {
1✔
UNCOV
420
                        return err
×
421
                }
×
422
                c.recorder.Eventf(
1✔
423
                        stack,
1✔
424
                        apiv1.EventTypeNormal,
1✔
425
                        "UpdatedSecret",
1✔
426
                        "Updated Secret %s",
1✔
427
                        secret.Name,
1✔
428
                )
1✔
429
        }
430
        return nil
1✔
431
}
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

© 2026 Coveralls, Inc