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

zalando-incubator / stackset-controller / 8648607775

11 Apr 2024 02:39PM UTC coverage: 50.491% (-1.4%) from 51.844%
8648607775

push

github

web-flow
Remove central ingress/routegroup logic (#620)

* Remove central ingress/routegroup logic; remove ingress source switch ttl logic.

Signed-off-by: Rodrigo Reis <rodrigo.gargravarr@gmail.com>

* Remove split HPA generation; rename StackSet Ingress function.

Signed-off-by: Rodrigo Reis <rodrigo.gargravarr@gmail.com>

* Update end-2-end test configuration.

Signed-off-by: Rodrigo Reis <rodrigo.gargravarr@gmail.com>

* Remove comment.

Signed-off-by: Rodrigo Reis <rodrigo.gargravarr@gmail.com>

---------

Signed-off-by: Rodrigo Reis <rodrigo.gargravarr@gmail.com>

17 of 57 new or added lines in 5 files covered. (29.82%)

136 existing lines in 4 files now uncovered.

2826 of 5597 relevant lines covered (50.49%)

0.57 hits per line

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

82.82
/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
        "k8s.io/apimachinery/pkg/types"
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
// syncObjectMeta copies metadata elements such as labels or annotations from source to target
30
func syncObjectMeta(target, source metav1.Object) {
1✔
31
        target.SetLabels(source.GetLabels())
1✔
32
        target.SetAnnotations(source.GetAnnotations())
1✔
33
}
1✔
34

35
// isOwned checks if the resource is owned and returns the UID of the owner.
36
func isOwned(ownerReferences []metav1.OwnerReference) (bool, types.UID) {
1✔
37
        for _, ownerRef := range ownerReferences {
2✔
38
                return true, ownerRef.UID
1✔
39
        }
1✔
40

41
        return false, ""
1✔
42
}
43

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

1✔
47
        // Create new deployment
1✔
48
        if existing == nil {
2✔
49
                _, err := c.client.AppsV1().Deployments(deployment.Namespace).Create(ctx, deployment, metav1.CreateOptions{})
1✔
50
                if err != nil {
1✔
51
                        return err
×
52
                }
×
53
                c.recorder.Eventf(
1✔
54
                        stack,
1✔
55
                        apiv1.EventTypeNormal,
1✔
56
                        "CreatedDeployment",
1✔
57
                        "Created Deployment %s",
1✔
58
                        deployment.Name)
1✔
59
                return nil
1✔
60
        }
61

62
        // Check if we need to update the deployment
63
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) && pint32Equal(existing.Spec.Replicas, deployment.Spec.Replicas) {
2✔
64
                return nil
1✔
65
        }
1✔
66

67
        updated := existing.DeepCopy()
1✔
68
        syncObjectMeta(updated, deployment)
1✔
69
        updated.Spec = deployment.Spec
1✔
70
        updated.Spec.Selector = existing.Spec.Selector
1✔
71

1✔
72
        _, err := c.client.AppsV1().Deployments(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
73
        if err != nil {
1✔
74
                return err
×
75
        }
×
76
        c.recorder.Eventf(
1✔
77
                stack,
1✔
78
                apiv1.EventTypeNormal,
1✔
79
                "UpdatedDeployment",
1✔
80
                "Updated Deployment %s",
1✔
81
                deployment.Name)
1✔
82
        return nil
1✔
83
}
84

85
func (c *StackSetController) ReconcileStackHPA(ctx context.Context, stack *zv1.Stack, existing *v2.HorizontalPodAutoscaler, generateUpdated func() (*v2.HorizontalPodAutoscaler, error)) error {
1✔
86
        hpa, err := generateUpdated()
1✔
87
        if err != nil {
1✔
88
                return err
×
89
        }
×
90

91
        // HPA removed
92
        if hpa == nil {
2✔
93
                if existing != nil {
2✔
94
                        err := c.client.AutoscalingV2().HorizontalPodAutoscalers(existing.Namespace).Delete(ctx, existing.Name, metav1.DeleteOptions{})
1✔
95
                        if err != nil {
1✔
96
                                return err
×
97
                        }
×
98
                        c.recorder.Eventf(
1✔
99
                                stack,
1✔
100
                                apiv1.EventTypeNormal,
1✔
101
                                "DeletedHPA",
1✔
102
                                "Deleted HPA %s",
1✔
103
                                existing.Namespace)
1✔
104
                }
105
                return nil
1✔
106
        }
107

108
        // Create new HPA
109
        if existing == nil {
2✔
110
                _, err := c.client.AutoscalingV2().HorizontalPodAutoscalers(hpa.Namespace).Create(ctx, hpa, metav1.CreateOptions{})
1✔
111
                if err != nil {
1✔
112
                        return err
×
113
                }
×
114
                c.recorder.Eventf(
1✔
115
                        stack,
1✔
116
                        apiv1.EventTypeNormal,
1✔
117
                        "CreatedHPA",
1✔
118
                        "Created HPA %s",
1✔
119
                        hpa.Name)
1✔
120
                return nil
1✔
121
        }
122

123
        // Check if we need to update the HPA
124
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) &&
1✔
125
                pint32Equal(existing.Spec.MinReplicas, hpa.Spec.MinReplicas) &&
1✔
126
                core.AreAnnotationsUpToDate(hpa.ObjectMeta, existing.ObjectMeta) {
2✔
127
                return nil
1✔
128
        }
1✔
129

130
        updated := existing.DeepCopy()
1✔
131
        syncObjectMeta(updated, hpa)
1✔
132
        updated.Spec = hpa.Spec
1✔
133

1✔
134
        _, err = c.client.AutoscalingV2().HorizontalPodAutoscalers(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
135
        if err != nil {
1✔
136
                return err
×
137
        }
×
138
        c.recorder.Eventf(
1✔
139
                stack,
1✔
140
                apiv1.EventTypeNormal,
1✔
141
                "UpdatedHPA",
1✔
142
                "Updated HPA %s",
1✔
143
                hpa.Name)
1✔
144
        return nil
1✔
145
}
146

147
func (c *StackSetController) ReconcileStackService(ctx context.Context, stack *zv1.Stack, existing *apiv1.Service, generateUpdated func() (*apiv1.Service, error)) error {
1✔
148
        service, err := generateUpdated()
1✔
149
        if err != nil {
1✔
150
                return err
×
151
        }
×
152

153
        // Create new service
154
        if existing == nil {
2✔
155
                _, err := c.client.CoreV1().Services(service.Namespace).Create(ctx, service, metav1.CreateOptions{})
1✔
156
                if err != nil {
1✔
157
                        return err
×
158
                }
×
159
                c.recorder.Eventf(
1✔
160
                        stack,
1✔
161
                        apiv1.EventTypeNormal,
1✔
162
                        "CreatedService",
1✔
163
                        "Created Service %s",
1✔
164
                        service.Name)
1✔
165
                return nil
1✔
166
        }
167

168
        // Check if we need to update the service
169
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) {
2✔
170
                return nil
1✔
171
        }
1✔
172

173
        updated := existing.DeepCopy()
1✔
174
        syncObjectMeta(updated, service)
1✔
175
        updated.Spec = service.Spec
1✔
176
        updated.Spec.ClusterIP = existing.Spec.ClusterIP // ClusterIP is immutable
1✔
177

1✔
178
        _, err = c.client.CoreV1().Services(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
179
        if err != nil {
1✔
180
                return err
×
181
        }
×
182
        c.recorder.Eventf(
1✔
183
                stack,
1✔
184
                apiv1.EventTypeNormal,
1✔
185
                "UpdatedService",
1✔
186
                "Updated Service %s",
1✔
187
                service.Name)
1✔
188
        return nil
1✔
189
}
190

191
func (c *StackSetController) ReconcileStackIngress(ctx context.Context, stack *zv1.Stack, existing *networking.Ingress, generateUpdated func() (*networking.Ingress, error)) error {
1✔
192
        ingress, err := generateUpdated()
1✔
193
        if err != nil {
1✔
194
                return err
×
195
        }
×
196

197
        // Ingress removed
198
        if ingress == nil {
2✔
199
                if existing != nil {
2✔
200
                        err := c.client.NetworkingV1().Ingresses(existing.Namespace).Delete(ctx, existing.Name, metav1.DeleteOptions{})
1✔
201
                        if err != nil {
1✔
202
                                return err
×
203
                        }
×
204
                        c.recorder.Eventf(
1✔
205
                                stack,
1✔
206
                                apiv1.EventTypeNormal,
1✔
207
                                "DeletedIngress",
1✔
208
                                "Deleted Ingress %s",
1✔
209
                                existing.Namespace)
1✔
210
                }
211
                return nil
1✔
212
        }
213

214
        // Create new Ingress
215
        if existing == nil {
2✔
216
                _, err := c.client.NetworkingV1().Ingresses(ingress.Namespace).Create(ctx, ingress, metav1.CreateOptions{})
1✔
217
                if err != nil {
1✔
218
                        return err
×
219
                }
×
220
                c.recorder.Eventf(
1✔
221
                        stack,
1✔
222
                        apiv1.EventTypeNormal,
1✔
223
                        "CreatedIngress",
1✔
224
                        "Created Ingress %s",
1✔
225
                        ingress.Name)
1✔
226
                return nil
1✔
227
        }
228

229
        // Check if we need to update the Ingress
230
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) &&
1✔
231
                core.AreAnnotationsUpToDate(ingress.ObjectMeta, existing.ObjectMeta) {
2✔
232

1✔
233
                return nil
1✔
234
        }
1✔
235

236
        updated := existing.DeepCopy()
1✔
237
        syncObjectMeta(updated, ingress)
1✔
238
        updated.Spec = ingress.Spec
1✔
239

1✔
240
        _, err = c.client.NetworkingV1().Ingresses(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
241
        if err != nil {
1✔
242
                return err
×
243
        }
×
244
        c.recorder.Eventf(
1✔
245
                stack,
1✔
246
                apiv1.EventTypeNormal,
1✔
247
                "UpdatedIngress",
1✔
248
                "Updated Ingress %s",
1✔
249
                ingress.Name)
1✔
250
        return nil
1✔
251
}
252

253
func (c *StackSetController) ReconcileStackRouteGroup(ctx context.Context, stack *zv1.Stack, existing *rgv1.RouteGroup, generateUpdated func() (*rgv1.RouteGroup, error)) error {
1✔
254
        routegroup, err := generateUpdated()
1✔
255
        if err != nil {
1✔
256
                return err
×
257
        }
×
258

259
        // RouteGroup removed
260
        if routegroup == nil {
2✔
261
                if existing != nil {
2✔
262
                        err := c.client.RouteGroupV1().RouteGroups(existing.Namespace).Delete(ctx, existing.Name, metav1.DeleteOptions{})
1✔
263
                        if err != nil {
1✔
264
                                return err
×
265
                        }
×
266
                        c.recorder.Eventf(
1✔
267
                                stack,
1✔
268
                                apiv1.EventTypeNormal,
1✔
269
                                "DeletedRouteGroup",
1✔
270
                                "Deleted RouteGroup %s",
1✔
271
                                existing.Namespace)
1✔
272
                }
273
                return nil
1✔
274
        }
275

276
        // Create new RouteGroup
277
        if existing == nil {
2✔
278
                _, err := c.client.RouteGroupV1().RouteGroups(routegroup.Namespace).Create(ctx, routegroup, metav1.CreateOptions{})
1✔
279
                if err != nil {
1✔
280
                        return err
×
281
                }
×
282
                c.recorder.Eventf(
1✔
283
                        stack,
1✔
284
                        apiv1.EventTypeNormal,
1✔
285
                        "CreatedRouteGroup",
1✔
286
                        "Created RouteGroup %s",
1✔
287
                        routegroup.Name)
1✔
288
                return nil
1✔
289
        }
290

291
        // Check if we need to update the RouteGroup
292
        if core.IsResourceUpToDate(stack, existing.ObjectMeta) &&
1✔
293
                equality.Semantic.DeepEqual(routegroup.Spec, existing.Spec) &&
1✔
294
                core.AreAnnotationsUpToDate(
1✔
295
                        routegroup.ObjectMeta,
1✔
296
                        existing.ObjectMeta,
1✔
297
                ) {
2✔
298

1✔
299
                return nil
1✔
300
        }
1✔
301

302
        updated := existing.DeepCopy()
1✔
303
        syncObjectMeta(updated, routegroup)
1✔
304
        updated.Spec = routegroup.Spec
1✔
305

1✔
306
        _, err = c.client.RouteGroupV1().RouteGroups(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
307
        if err != nil {
1✔
308
                return err
×
309
        }
×
310
        c.recorder.Eventf(
1✔
311
                stack,
1✔
312
                apiv1.EventTypeNormal,
1✔
313
                "UpdatedRouteGroup",
1✔
314
                "Updated RouteGroup %s",
1✔
315
                routegroup.Name)
1✔
316
        return nil
1✔
317
}
318

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

341
                if err := validateConfigurationResourceName(stack.Name, rsc.GetName()); err != nil {
2✔
342
                        return err
1✔
343
                }
1✔
344

345
                if err := c.ReconcileStackConfigMapRef(ctx, stack, rsc, updateObjMeta); err != nil {
1✔
346
                        return err
×
347
                }
×
348
        }
349

350
        return nil
1✔
351
}
352

353
func (c *StackSetController) ReconcileStackConfigMapRef(
354
        ctx context.Context,
355
        stack *zv1.Stack,
356
        rsc zv1.ConfigurationResourcesSpec,
357
        updateObjMeta func(*metav1.ObjectMeta) *metav1.ObjectMeta,
358
) error {
1✔
359
        configMap, err := c.client.CoreV1().ConfigMaps(stack.Namespace).
1✔
360
                Get(ctx, rsc.GetName(), metav1.GetOptions{})
1✔
361
        if err != nil {
1✔
362
                return err
×
363
        }
×
364

365
        // Check if the ConfigMap is already owned by us or another resource.
366
        isOwned, owner := isOwned(configMap.OwnerReferences)
1✔
367
        if isOwned {
2✔
368
                // If the ConfigMap is already owned by us, we don't need to do anything.
1✔
369
                if owner == stack.UID {
2✔
370
                        return nil
1✔
371
                }
1✔
372

373
                // If the ConfigMap is owned by another resource, we should not update it.
374
                return fmt.Errorf("ConfigMap already owned by other resource. "+
×
375
                        "ConfigMap: %s, Stack: %s", rsc.GetName(), stack.Name)
×
376
        }
377

378
        objectMeta := updateObjMeta(&configMap.ObjectMeta)
1✔
379
        configMap.ObjectMeta = *objectMeta
1✔
380

1✔
381
        _, err = c.client.CoreV1().ConfigMaps(configMap.Namespace).
1✔
382
                Update(ctx, configMap, metav1.UpdateOptions{})
1✔
383
        if err != nil {
1✔
384
                return err
×
385
        }
×
386

387
        c.recorder.Eventf(
1✔
388
                stack,
1✔
389
                apiv1.EventTypeNormal,
1✔
390
                "UpdatedConfigMap",
1✔
391
                "Updated ConfigMap %s",
1✔
392
                configMap.Name,
1✔
393
        )
1✔
394

1✔
395
        return nil
1✔
396
}
397

398
// ReconcileStackSecretRefs will update the named user-provided Secrets to be
399
// attached to the Stack by ownerReferences, when a list of Configuration
400
// Resources are defined on the Stack template.
401
//
402
// The provided Secret name must be prefixed by the Stack name.
403
// eg: Stack: myapp-v1 Secret: myapp-v1-my-secret
404
//
405
// User update of running versioned Secrets is not encouraged but is allowed
406
// on consideration of emergency needs. Similarly, addition of Secrets to
407
// running resources is also allowed, so the method checks for changes on the
408
// ConfigurationResources to ensure all listed Secrets are properly linked
409
// to the Stack.
410
func (c *StackSetController) ReconcileStackSecretRefs(
411
        ctx context.Context,
412
        stack *zv1.Stack,
413
        updateObjMeta func(*metav1.ObjectMeta) *metav1.ObjectMeta,
414
) error {
1✔
415
        for _, rsc := range stack.Spec.ConfigurationResources {
2✔
416
                if !rsc.IsSecretRef() {
2✔
417
                        continue
1✔
418
                }
419

420
                if err := validateConfigurationResourceName(stack.Name, rsc.GetName()); err != nil {
1✔
421
                        return err
×
422
                }
×
423

424
                if err := c.ReconcileStackSecretRef(ctx, stack, rsc, updateObjMeta); err != nil {
1✔
425
                        return err
×
426
                }
×
427
        }
428

429
        return nil
1✔
430
}
431

432
func (c *StackSetController) ReconcileStackSecretRef(ctx context.Context,
433
        stack *zv1.Stack,
434
        rsc zv1.ConfigurationResourcesSpec,
435
        updateObjMeta func(*metav1.ObjectMeta) *metav1.ObjectMeta,
436
) error {
1✔
437
        secret, err := c.client.CoreV1().Secrets(stack.Namespace).
1✔
438
                Get(ctx, rsc.GetName(), metav1.GetOptions{})
1✔
439
        if err != nil {
1✔
440
                return err
×
441
        }
×
442

443
        // Check if the Secret is already owned by us or another resource.
444
        isOwned, owner := isOwned(secret.OwnerReferences)
1✔
445
        if isOwned {
2✔
446
                // If the Secret is already owned by us, we don't need to do anything.
1✔
447
                if owner == stack.UID {
2✔
448
                        return nil
1✔
449
                }
1✔
450

451
                // If the Secret is owned by another resource, we should not update it.
NEW
452
                return fmt.Errorf("secret already owned by other resource. "+
×
453
                        "Secret: %s, Stack: %s", rsc.GetName(), stack.Name)
×
454
        }
455

456
        objectMeta := updateObjMeta(&secret.ObjectMeta)
1✔
457
        secret.ObjectMeta = *objectMeta
1✔
458

1✔
459
        _, err = c.client.CoreV1().Secrets(secret.Namespace).
1✔
460
                Update(ctx, secret, metav1.UpdateOptions{})
1✔
461
        if err != nil {
1✔
462
                return err
×
463
        }
×
464

465
        c.recorder.Eventf(
1✔
466
                stack,
1✔
467
                apiv1.EventTypeNormal,
1✔
468
                "UpdatedSecret",
1✔
469
                "Updated Secret %s",
1✔
470
                secret.Name,
1✔
471
        )
1✔
472

1✔
473
        return nil
1✔
474
}
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