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

zalando-incubator / stackset-controller / 8649011694

11 Apr 2024 03:04PM UTC coverage: 49.355% (-1.0%) from 50.373%
8649011694

Pull #593

github

linki
some labels
Pull Request #593: [2/3] Add Inline Solution for ConfigMaps

0 of 114 new or added lines in 3 files covered. (0.0%)

2 existing lines in 1 file now uncovered.

2831 of 5736 relevant lines covered (49.35%)

0.55 hits per line

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

35.46
/controller/stackset.go
1
package controller
2

3
import (
4
        "context"
5
        "fmt"
6
        "net/http"
7
        "runtime/debug"
8
        "strings"
9
        "sync"
10
        "time"
11

12
        "github.com/google/go-cmp/cmp"
13
        "github.com/google/go-cmp/cmp/cmpopts"
14
        "github.com/heptiolabs/healthcheck"
15
        "github.com/prometheus/client_golang/prometheus"
16
        log "github.com/sirupsen/logrus"
17
        rgv1 "github.com/szuecs/routegroup-client/apis/zalando.org/v1"
18
        zv1 "github.com/zalando-incubator/stackset-controller/pkg/apis/zalando.org/v1"
19
        "github.com/zalando-incubator/stackset-controller/pkg/clientset"
20
        "github.com/zalando-incubator/stackset-controller/pkg/core"
21
        "github.com/zalando-incubator/stackset-controller/pkg/recorder"
22
        "golang.org/x/sync/errgroup"
23
        v1 "k8s.io/api/core/v1"
24
        networking "k8s.io/api/networking/v1"
25
        "k8s.io/apimachinery/pkg/api/equality"
26
        "k8s.io/apimachinery/pkg/api/errors"
27
        "k8s.io/apimachinery/pkg/api/resource"
28
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
        "k8s.io/apimachinery/pkg/fields"
30
        "k8s.io/apimachinery/pkg/runtime"
31
        "k8s.io/apimachinery/pkg/types"
32
        "k8s.io/client-go/tools/cache"
33
        kube_record "k8s.io/client-go/tools/record"
34
)
35

36
const (
37
        PrescaleStacksAnnotationKey               = "alpha.stackset-controller.zalando.org/prescale-stacks"
38
        ResetHPAMinReplicasDelayAnnotationKey     = "alpha.stackset-controller.zalando.org/reset-hpa-min-replicas-delay"
39
        StacksetControllerControllerAnnotationKey = "stackset-controller.zalando.org/controller"
40
        ControllerLastUpdatedAnnotationKey        = "stackset-controller.zalando.org/updated-timestamp"
41

42
        reasonFailedManageStackSet = "FailedManageStackSet"
43

44
        defaultResetMinReplicasDelay = 10 * time.Minute
45
)
46

47
var configurationResourceNameError = "ConfigurationResource name must be prefixed by Stack name. ConfigurationResource: %s, Stack: %s"
48

49
// StackSetController is the main controller. It watches for changes to
50
// stackset resources and starts and maintains other controllers per
51
// stackset resource.
52
type StackSetController struct {
53
        logger                      *log.Entry
54
        client                      clientset.Interface
55
        namespace                   string
56
        syncIngressAnnotations      []string
57
        controllerID                string
58
        backendWeightsAnnotationKey string
59
        clusterDomains              []string
60
        interval                    time.Duration
61
        stacksetEvents              chan stacksetEvent
62
        stacksetStore               map[types.UID]zv1.StackSet
63
        recorder                    kube_record.EventRecorder
64
        metricsReporter             *core.MetricsReporter
65
        HealthReporter              healthcheck.Handler
66
        routeGroupSupportEnabled    bool
67
        now                         func() string
68
        reconcileWorkers            int
69
        configMapSupportEnabled     bool
70
        secretSupportEnabled        bool
71
        sync.Mutex
72
}
73

74
type stacksetEvent struct {
75
        Deleted  bool
76
        StackSet *zv1.StackSet
77
}
78

79
// eventedError wraps an error that was already exposed as an event to the user
80
type eventedError struct {
81
        err error
82
}
83

84
func (ee *eventedError) Error() string {
×
85
        return ee.err.Error()
×
86
}
×
87

88
func now() string {
×
89
        return time.Now().Format(time.RFC3339)
×
90
}
×
91

92
// NewStackSetController initializes a new StackSetController.
93
func NewStackSetController(
94
        client clientset.Interface,
95
        namespace string,
96
        controllerID string,
97
        parallelWork int,
98
        backendWeightsAnnotationKey string,
99
        clusterDomains []string,
100
        registry prometheus.Registerer,
101
        interval time.Duration,
102
        routeGroupSupportEnabled bool,
103
        syncIngressAnnotations []string,
104
        configMapSupportEnabled bool,
105
        secretSupportEnabled bool,
106
) (*StackSetController, error) {
1✔
107
        metricsReporter, err := core.NewMetricsReporter(registry)
1✔
108
        if err != nil {
1✔
109
                return nil, err
×
110
        }
×
111

112
        return &StackSetController{
1✔
113
                logger:                      log.WithFields(log.Fields{"controller": "stackset"}),
1✔
114
                client:                      client,
1✔
115
                namespace:                   namespace,
1✔
116
                controllerID:                controllerID,
1✔
117
                backendWeightsAnnotationKey: backendWeightsAnnotationKey,
1✔
118
                clusterDomains:              clusterDomains,
1✔
119
                interval:                    interval,
1✔
120
                stacksetEvents:              make(chan stacksetEvent, 1),
1✔
121
                stacksetStore:               make(map[types.UID]zv1.StackSet),
1✔
122
                recorder:                    recorder.CreateEventRecorder(client),
1✔
123
                metricsReporter:             metricsReporter,
1✔
124
                HealthReporter:              healthcheck.NewHandler(),
1✔
125
                routeGroupSupportEnabled:    routeGroupSupportEnabled,
1✔
126
                syncIngressAnnotations:      syncIngressAnnotations,
1✔
127
                configMapSupportEnabled:     configMapSupportEnabled,
1✔
128
                secretSupportEnabled:        secretSupportEnabled,
1✔
129
                now:                         now,
1✔
130
                reconcileWorkers:            parallelWork,
1✔
131
        }, nil
1✔
132
}
133

134
func (c *StackSetController) stacksetLogger(ssc *core.StackSetContainer) *log.Entry {
×
135
        return c.logger.WithFields(map[string]interface{}{
×
136
                "namespace": ssc.StackSet.Namespace,
×
137
                "stackset":  ssc.StackSet.Name,
×
138
        })
×
139
}
×
140

141
func (c *StackSetController) stackLogger(ssc *core.StackSetContainer, sc *core.StackContainer) *log.Entry {
×
142
        return c.logger.WithFields(map[string]interface{}{
×
143
                "namespace": ssc.StackSet.Namespace,
×
144
                "stackset":  ssc.StackSet.Name,
×
145
                "stack":     sc.Name(),
×
146
        })
×
147
}
×
148

149
// Run runs the main loop of the StackSetController. Before the loops it
150
// sets up a watcher to watch StackSet resources. The watch will send
151
// changes over a channel which is polled from the main loop.
152
func (c *StackSetController) Run(ctx context.Context) error {
×
153
        var nextCheck time.Time
×
154

×
155
        // We're not alive if nextCheck is too far in the past
×
156
        c.HealthReporter.AddLivenessCheck("nextCheck", func() error {
×
157
                if time.Since(nextCheck) > 5*c.interval {
×
158
                        return fmt.Errorf("nextCheck too old")
×
159
                }
×
160
                return nil
×
161
        })
162

163
        err := c.startWatch(ctx)
×
164
        if err != nil {
×
165
                return err
×
166
        }
×
167

168
        http.HandleFunc("/healthz", c.HealthReporter.LiveEndpoint)
×
169

×
170
        nextCheck = time.Now().Add(-c.interval)
×
171

×
172
        for {
×
173
                select {
×
174
                case <-time.After(time.Until(nextCheck)):
×
175

×
176
                        nextCheck = time.Now().Add(c.interval)
×
177

×
178
                        stackSetContainers, err := c.collectResources(ctx)
×
179
                        if err != nil {
×
180
                                c.logger.Errorf("Failed to collect resources: %v", err)
×
181
                                continue
×
182
                        }
183

184
                        var reconcileGroup errgroup.Group
×
185
                        reconcileGroup.SetLimit(c.reconcileWorkers)
×
186
                        for stackset, container := range stackSetContainers {
×
187
                                container := container
×
188
                                stackset := stackset
×
189

×
190
                                reconcileGroup.Go(func() error {
×
191
                                        if _, ok := c.stacksetStore[stackset]; ok {
×
192
                                                err := c.ReconcileStackSet(ctx, container)
×
193
                                                if err != nil {
×
194
                                                        c.stacksetLogger(container).Errorf("unable to reconcile a stackset: %v", err)
×
195
                                                        return c.errorEventf(container.StackSet, reasonFailedManageStackSet, err)
×
196
                                                }
×
197
                                        }
198
                                        return nil
×
199
                                })
200
                        }
201

202
                        err = reconcileGroup.Wait()
×
203
                        if err != nil {
×
204
                                c.logger.Errorf("Failed waiting for reconcilers: %v", err)
×
205
                        }
×
206
                        err = c.metricsReporter.Report(stackSetContainers)
×
207
                        if err != nil {
×
208
                                c.logger.Errorf("Failed reporting metrics: %v", err)
×
209
                        }
×
210
                case e := <-c.stacksetEvents:
×
211
                        stackset := *e.StackSet
×
212
                        fixupStackSetTypeMeta(&stackset)
×
213

×
214
                        // update/delete existing entry
×
215
                        if _, ok := c.stacksetStore[stackset.UID]; ok {
×
216
                                if e.Deleted || !c.hasOwnership(&stackset) {
×
217
                                        delete(c.stacksetStore, stackset.UID)
×
218
                                        continue
×
219
                                }
220

221
                                // update stackset entry
222
                                c.stacksetStore[stackset.UID] = stackset
×
223
                                continue
×
224
                        }
225

226
                        // check if stackset should be managed by the controller
227
                        if !c.hasOwnership(&stackset) {
×
228
                                continue
×
229
                        }
230

231
                        c.logger.Infof("Adding entry for StackSet %s/%s", stackset.Namespace, stackset.Name)
×
232
                        c.stacksetStore[stackset.UID] = stackset
×
233
                case <-ctx.Done():
×
234
                        c.logger.Info("Terminating main controller loop.")
×
235
                        return nil
×
236
                }
237
        }
238
}
239

240
// collectResources collects resources for all stacksets at once and stores them per StackSet/Stack so that we don't
241
// overload the API requests with unnecessary requests
242
func (c *StackSetController) collectResources(ctx context.Context) (map[types.UID]*core.StackSetContainer, error) {
1✔
243
        stacksets := make(map[types.UID]*core.StackSetContainer, len(c.stacksetStore))
1✔
244
        for uid, stackset := range c.stacksetStore {
2✔
245
                stackset := stackset
1✔
246

1✔
247
                reconciler := core.TrafficReconciler(&core.SimpleTrafficReconciler{})
1✔
248

1✔
249
                // use prescaling logic if enabled with an annotation
1✔
250
                if _, ok := stackset.Annotations[PrescaleStacksAnnotationKey]; ok {
2✔
251
                        resetDelay := defaultResetMinReplicasDelay
1✔
252
                        if resetDelayValue, ok := getResetMinReplicasDelay(stackset.Annotations); ok {
2✔
253
                                resetDelay = resetDelayValue
1✔
254
                        }
1✔
255
                        reconciler = &core.PrescalingTrafficReconciler{
1✔
256
                                ResetHPAMinReplicasTimeout: resetDelay,
1✔
257
                        }
1✔
258
                }
259

260
                stacksetContainer := core.NewContainer(
1✔
261
                        &stackset,
1✔
262
                        reconciler,
1✔
263
                        c.backendWeightsAnnotationKey,
1✔
264
                        c.clusterDomains,
1✔
265
                        c.syncIngressAnnotations,
1✔
266
                )
1✔
267
                stacksets[uid] = stacksetContainer
1✔
268
        }
269

270
        err := c.collectStacks(ctx, stacksets)
1✔
271
        if err != nil {
1✔
272
                return nil, err
×
273
        }
×
274

275
        err = c.collectIngresses(ctx, stacksets)
1✔
276
        if err != nil {
1✔
277
                return nil, err
×
278
        }
×
279

280
        if c.routeGroupSupportEnabled {
2✔
281
                err = c.collectRouteGroups(ctx, stacksets)
1✔
282
                if err != nil {
1✔
283
                        return nil, err
×
284
                }
×
285
        }
286

287
        err = c.collectDeployments(ctx, stacksets)
1✔
288
        if err != nil {
1✔
289
                return nil, err
×
290
        }
×
291

292
        err = c.collectServices(ctx, stacksets)
1✔
293
        if err != nil {
1✔
294
                return nil, err
×
295
        }
×
296

297
        err = c.collectHPAs(ctx, stacksets)
1✔
298
        if err != nil {
1✔
299
                return nil, err
×
300
        }
×
301

302
        if c.configMapSupportEnabled {
2✔
303
                err = c.collectConfigMaps(ctx, stacksets)
1✔
304
                if err != nil {
1✔
305
                        return nil, err
×
306
                }
×
307
        }
308

309
        if c.secretSupportEnabled {
2✔
310
                err = c.collectSecrets(ctx, stacksets)
1✔
311
                if err != nil {
1✔
312
                        return nil, err
×
313
                }
×
314
        }
315

316
        return stacksets, nil
1✔
317
}
318

319
func (c *StackSetController) collectIngresses(ctx context.Context, stacksets map[types.UID]*core.StackSetContainer) error {
1✔
320
        ingresses, err := c.client.NetworkingV1().Ingresses(c.namespace).List(ctx, metav1.ListOptions{})
1✔
321

1✔
322
        if err != nil {
1✔
323
                return fmt.Errorf("failed to list Ingresses: %v", err)
×
324
        }
×
325

326
        for _, i := range ingresses.Items {
2✔
327
                ingress := i
1✔
328
                if uid, ok := getOwnerUID(ingress.ObjectMeta); ok {
2✔
329
                        // stackset ingress
1✔
330
                        if s, ok := stacksets[uid]; ok {
2✔
331
                                s.Ingress = &ingress
1✔
332
                                continue
1✔
333
                        }
334

335
                        // stack ingress
336
                        for _, stackset := range stacksets {
2✔
337
                                if s, ok := stackset.StackContainers[uid]; ok {
2✔
338
                                        if strings.HasSuffix(
1✔
339
                                                ingress.ObjectMeta.Name,
1✔
340
                                                core.SegmentSuffix,
1✔
341
                                        ) {
2✔
342
                                                // Traffic Segment
1✔
343
                                                s.Resources.IngressSegment = &ingress
1✔
344
                                        } else {
2✔
345
                                                s.Resources.Ingress = &ingress
1✔
346
                                        }
1✔
347
                                        break
1✔
348
                                }
349
                        }
350
                }
351
        }
352
        return nil
1✔
353
}
354

355
func (c *StackSetController) collectRouteGroups(ctx context.Context, stacksets map[types.UID]*core.StackSetContainer) error {
1✔
356
        rgs, err := c.client.RouteGroupV1().RouteGroups(c.namespace).List(
1✔
357
                ctx,
1✔
358
                metav1.ListOptions{},
1✔
359
        )
1✔
360
        if err != nil {
1✔
361
                return fmt.Errorf("failed to list RouteGroups: %v", err)
×
362
        }
×
363

364
        for _, rg := range rgs.Items {
2✔
365
                routegroup := rg
1✔
366
                if uid, ok := getOwnerUID(routegroup.ObjectMeta); ok {
2✔
367
                        // stackset routegroups
1✔
368
                        if s, ok := stacksets[uid]; ok {
2✔
369
                                s.RouteGroup = &routegroup
1✔
370
                                continue
1✔
371
                        }
372

373
                        // stack routegroups
374
                        for _, stackset := range stacksets {
2✔
375
                                if s, ok := stackset.StackContainers[uid]; ok {
2✔
376
                                        if strings.HasSuffix(
1✔
377
                                                routegroup.ObjectMeta.Name,
1✔
378
                                                core.SegmentSuffix,
1✔
379
                                        ) {
2✔
380
                                                // Traffic Segment
1✔
381
                                                s.Resources.RouteGroupSegment = &routegroup
1✔
382
                                        } else {
2✔
383
                                                s.Resources.RouteGroup = &routegroup
1✔
384
                                        }
1✔
385
                                        break
1✔
386
                                }
387
                        }
388
                }
389
        }
390
        return nil
1✔
391
}
392

393
func (c *StackSetController) collectStacks(ctx context.Context, stacksets map[types.UID]*core.StackSetContainer) error {
1✔
394
        stacks, err := c.client.ZalandoV1().Stacks(c.namespace).List(ctx, metav1.ListOptions{})
1✔
395
        if err != nil {
1✔
396
                return fmt.Errorf("failed to list Stacks: %v", err)
×
397
        }
×
398

399
        for _, stack := range stacks.Items {
2✔
400
                if uid, ok := getOwnerUID(stack.ObjectMeta); ok {
2✔
401
                        if s, ok := stacksets[uid]; ok {
2✔
402
                                stack := stack
1✔
403
                                fixupStackTypeMeta(&stack)
1✔
404

1✔
405
                                s.StackContainers[stack.UID] = &core.StackContainer{
1✔
406
                                        Stack: &stack,
1✔
407
                                }
1✔
408
                                continue
1✔
409
                        }
410
                }
411
        }
412
        return nil
1✔
413
}
414

415
func (c *StackSetController) collectDeployments(ctx context.Context, stacksets map[types.UID]*core.StackSetContainer) error {
1✔
416
        deployments, err := c.client.AppsV1().Deployments(c.namespace).List(ctx, metav1.ListOptions{})
1✔
417
        if err != nil {
1✔
418
                return fmt.Errorf("failed to list Deployments: %v", err)
×
419
        }
×
420

421
        for _, d := range deployments.Items {
2✔
422
                deployment := d
1✔
423
                if uid, ok := getOwnerUID(deployment.ObjectMeta); ok {
2✔
424
                        for _, stackset := range stacksets {
2✔
425
                                if s, ok := stackset.StackContainers[uid]; ok {
2✔
426
                                        s.Resources.Deployment = &deployment
1✔
427
                                        break
1✔
428
                                }
429
                        }
430
                }
431
        }
432
        return nil
1✔
433
}
434

435
func (c *StackSetController) collectServices(ctx context.Context, stacksets map[types.UID]*core.StackSetContainer) error {
1✔
436
        services, err := c.client.CoreV1().Services(c.namespace).List(ctx, metav1.ListOptions{})
1✔
437
        if err != nil {
1✔
438
                return fmt.Errorf("failed to list Services: %v", err)
×
439
        }
×
440

441
Items:
1✔
442
        for _, s := range services.Items {
2✔
443
                service := s
1✔
444
                if uid, ok := getOwnerUID(service.ObjectMeta); ok {
2✔
445
                        for _, stackset := range stacksets {
2✔
446
                                if s, ok := stackset.StackContainers[uid]; ok {
2✔
447
                                        s.Resources.Service = &service
1✔
448
                                        continue Items
1✔
449
                                }
450

451
                                // service/HPA used to be owned by the deployment for some reason
452
                                for _, stack := range stackset.StackContainers {
2✔
453
                                        if stack.Resources.Deployment != nil && stack.Resources.Deployment.UID == uid {
2✔
454
                                                stack.Resources.Service = &service
1✔
455
                                                continue Items
1✔
456
                                        }
457
                                }
458
                        }
459
                }
460
        }
461
        return nil
1✔
462
}
463

464
func (c *StackSetController) collectHPAs(ctx context.Context, stacksets map[types.UID]*core.StackSetContainer) error {
1✔
465
        hpas, err := c.client.AutoscalingV2().HorizontalPodAutoscalers(c.namespace).List(ctx, metav1.ListOptions{})
1✔
466
        if err != nil {
1✔
467
                return fmt.Errorf("failed to list HPAs: %v", err)
×
468
        }
×
469

470
Items:
1✔
471
        for _, h := range hpas.Items {
2✔
472
                hpa := h
1✔
473
                if uid, ok := getOwnerUID(hpa.ObjectMeta); ok {
2✔
474
                        for _, stackset := range stacksets {
2✔
475
                                if s, ok := stackset.StackContainers[uid]; ok {
2✔
476
                                        s.Resources.HPA = &hpa
1✔
477
                                        continue Items
1✔
478
                                }
479

480
                                // service/HPA used to be owned by the deployment for some reason
481
                                for _, stack := range stackset.StackContainers {
2✔
482
                                        if stack.Resources.Deployment != nil && stack.Resources.Deployment.UID == uid {
2✔
483
                                                stack.Resources.HPA = &hpa
1✔
484
                                                continue Items
1✔
485
                                        }
486
                                }
487
                        }
488
                }
489
        }
490
        return nil
1✔
491
}
492

493
func (c *StackSetController) collectConfigMaps(ctx context.Context, stacksets map[types.UID]*core.StackSetContainer) error {
1✔
494
        configMaps, err := c.client.CoreV1().ConfigMaps(c.namespace).List(ctx, metav1.ListOptions{})
1✔
495
        if err != nil {
1✔
496
                return fmt.Errorf("failed to list ConfigMaps: %v", err)
×
497
        }
×
498

499
        for _, cm := range configMaps.Items {
2✔
500
                configMap := cm
1✔
501
                if uid, ok := getOwnerUID(configMap.ObjectMeta); ok {
2✔
502
                        for _, stackset := range stacksets {
2✔
503
                                if s, ok := stackset.StackContainers[uid]; ok {
2✔
504
                                        s.Resources.ConfigMaps = append(s.Resources.ConfigMaps, &configMap)
1✔
505
                                        break
1✔
506
                                }
507
                        }
508
                }
509
        }
510
        return nil
1✔
511
}
512

513
func (c *StackSetController) collectSecrets(ctx context.Context, stacksets map[types.UID]*core.StackSetContainer) error {
1✔
514
        secrets, err := c.client.CoreV1().Secrets(c.namespace).List(ctx, metav1.ListOptions{})
1✔
515
        if err != nil {
1✔
516
                return fmt.Errorf("failed to list Secrets: %v", err)
×
517
        }
×
518

519
        for _, sct := range secrets.Items {
2✔
520
                secret := sct
1✔
521
                if uid, ok := getOwnerUID(secret.ObjectMeta); ok {
2✔
522
                        for _, stackset := range stacksets {
2✔
523
                                if s, ok := stackset.StackContainers[uid]; ok {
2✔
524
                                        s.Resources.Secrets = append(s.Resources.Secrets, &secret)
1✔
525
                                        break
1✔
526
                                }
527
                        }
528
                }
529
        }
530
        return nil
1✔
531
}
532

533
func getOwnerUID(objectMeta metav1.ObjectMeta) (types.UID, bool) {
1✔
534
        if len(objectMeta.OwnerReferences) == 1 {
2✔
535
                return objectMeta.OwnerReferences[0].UID, true
1✔
536
        }
1✔
537
        return "", false
1✔
538
}
539

540
func (c *StackSetController) errorEventf(object runtime.Object, reason string, err error) error {
×
541
        switch err.(type) {
×
542
        case *eventedError:
×
543
                // already notified
×
544
                return err
×
545
        default:
×
546
                c.recorder.Eventf(
×
547
                        object,
×
548
                        v1.EventTypeWarning,
×
549
                        reason,
×
550
                        err.Error())
×
551
                return &eventedError{err: err}
×
552
        }
553
}
554

555
// hasOwnership returns true if the controller is the "owner" of the stackset.
556
// Whether it's owner is determined by the value of the
557
// 'stackset-controller.zalando.org/controller' annotation. If the value
558
// matches the controllerID then it owns it, or if the controllerID is
559
// "" and there's no annotation set.
560
func (c *StackSetController) hasOwnership(stackset *zv1.StackSet) bool {
×
561
        if stackset.Annotations != nil {
×
562
                if owner, ok := stackset.Annotations[StacksetControllerControllerAnnotationKey]; ok {
×
563
                        return owner == c.controllerID
×
564
                }
×
565
        }
566
        return c.controllerID == ""
×
567
}
568

569
func (c *StackSetController) startWatch(ctx context.Context) error {
×
570
        informer := cache.NewSharedIndexInformer(
×
571
                cache.NewListWatchFromClient(c.client.ZalandoV1().RESTClient(), "stacksets", c.namespace, fields.Everything()),
×
572
                &zv1.StackSet{},
×
573
                0, // skip resync
×
574
                cache.Indexers{},
×
575
        )
×
576

×
577
        _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
×
578
                AddFunc:    c.add,
×
579
                UpdateFunc: c.update,
×
580
                DeleteFunc: c.del,
×
581
        })
×
582
        if err != nil {
×
583
                return fmt.Errorf("failed to add event handler: %w", err)
×
584
        }
×
585

586
        go informer.Run(ctx.Done())
×
587
        if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced) {
×
588
                return fmt.Errorf("timed out waiting for caches to sync")
×
589
        }
×
590
        c.logger.Info("Synced StackSet watcher")
×
591

×
592
        return nil
×
593
}
594

595
func (c *StackSetController) add(obj interface{}) {
×
596
        stackset, ok := obj.(*zv1.StackSet)
×
597
        if !ok {
×
598
                return
×
599
        }
×
600

601
        c.logger.Infof("New StackSet added %s/%s", stackset.Namespace, stackset.Name)
×
602
        c.stacksetEvents <- stacksetEvent{
×
603
                StackSet: stackset.DeepCopy(),
×
604
        }
×
605
}
606

607
func (c *StackSetController) update(oldObj, newObj interface{}) {
×
608
        newStackset, ok := newObj.(*zv1.StackSet)
×
609
        if !ok {
×
610
                return
×
611
        }
×
612

613
        oldStackset, ok := oldObj.(*zv1.StackSet)
×
614
        if !ok {
×
615
                return
×
616
        }
×
617

618
        c.logger.Debugf("StackSet %s/%s changed: %s",
×
619
                newStackset.Namespace,
×
620
                newStackset.Name,
×
621
                cmp.Diff(oldStackset, newStackset, cmpopts.IgnoreUnexported(resource.Quantity{})),
×
622
        )
×
623

×
624
        c.logger.Infof("StackSet updated %s/%s", newStackset.Namespace, newStackset.Name)
×
625
        c.stacksetEvents <- stacksetEvent{
×
626
                StackSet: newStackset.DeepCopy(),
×
627
        }
×
628
}
629

630
func (c *StackSetController) del(obj interface{}) {
×
631
        stackset, ok := obj.(*zv1.StackSet)
×
632
        if !ok {
×
633
                return
×
634
        }
×
635

636
        c.logger.Infof("StackSet deleted %s/%s", stackset.Namespace, stackset.Name)
×
637
        c.stacksetEvents <- stacksetEvent{
×
638
                StackSet: stackset.DeepCopy(),
×
639
                Deleted:  true,
×
640
        }
×
641
}
642

643
func retryUpdate(updateFn func(retry bool) error) error {
×
644
        retry := false
×
645
        for {
×
646
                err := updateFn(retry)
×
647
                if err != nil {
×
648
                        if errors.IsConflict(err) {
×
649
                                retry = true
×
650
                                continue
×
651
                        }
652
                        return err
×
653
                }
654
                return nil
×
655
        }
656
}
657

658
// ReconcileStatuses reconciles the statuses of StackSets and Stacks.
659
func (c *StackSetController) ReconcileStatuses(ctx context.Context, ssc *core.StackSetContainer) error {
×
660
        for _, sc := range ssc.StackContainers {
×
661
                stack := sc.Stack.DeepCopy()
×
662
                status := *sc.GenerateStackStatus()
×
663
                err := retryUpdate(func(retry bool) error {
×
664
                        if retry {
×
665
                                updated, err := c.client.ZalandoV1().Stacks(sc.Namespace()).Get(ctx, stack.Name, metav1.GetOptions{})
×
666
                                if err != nil {
×
667
                                        return err
×
668
                                }
×
669
                                stack = updated
×
670
                        }
671
                        if !equality.Semantic.DeepEqual(status, stack.Status) {
×
672
                                stack.Status = status
×
673
                                _, err := c.client.ZalandoV1().Stacks(sc.Namespace()).UpdateStatus(ctx, stack, metav1.UpdateOptions{})
×
674
                                return err
×
675
                        }
×
676
                        return nil
×
677
                })
678
                if err != nil {
×
679
                        return c.errorEventf(sc.Stack, "FailedUpdateStackStatus", err)
×
680
                }
×
681
        }
682

683
        stackset := ssc.StackSet.DeepCopy()
×
684
        status := *ssc.GenerateStackSetStatus()
×
685
        err := retryUpdate(func(retry bool) error {
×
686
                if retry {
×
687
                        updated, err := c.client.ZalandoV1().StackSets(ssc.StackSet.Namespace).Get(ctx, ssc.StackSet.Name, metav1.GetOptions{})
×
688
                        if err != nil {
×
689
                                return err
×
690
                        }
×
691
                        stackset = updated
×
692
                }
693
                if !equality.Semantic.DeepEqual(status, stackset.Status) {
×
694
                        stackset.Status = status
×
695
                        _, err := c.client.ZalandoV1().StackSets(ssc.StackSet.Namespace).UpdateStatus(ctx, stackset, metav1.UpdateOptions{})
×
696
                        return err
×
697
                }
×
698
                return nil
×
699
        })
700
        if err != nil {
×
701
                return c.errorEventf(ssc.StackSet, "FailedUpdateStackSetStatus", err)
×
702
        }
×
703
        return nil
×
704
}
705

706
// ReconcileTrafficSegments updates the traffic segments according to the actual
707
// traffic weight of each stack.
708
//
709
// Returns the ordered list of Trafic Segments that need to be updated.
710
func (c *StackSetController) ReconcileTrafficSegments(
711
        ctx context.Context,
712
        ssc *core.StackSetContainer,
713
) ([]types.UID, error) {
×
714
        // Compute segments
×
715
        toUpdate, err := ssc.ComputeTrafficSegments()
×
716
        if err != nil {
×
717
                return nil, c.errorEventf(ssc.StackSet, "FailedManageSegments", err)
×
718
        }
×
719

720
        return toUpdate, nil
×
721
}
722

723
// CreateCurrentStack creates a new Stack object for the current stack, if needed
724
func (c *StackSetController) CreateCurrentStack(ctx context.Context, ssc *core.StackSetContainer) error {
1✔
725
        newStack, newStackVersion := ssc.NewStack()
1✔
726
        if newStack == nil {
2✔
727
                return nil
1✔
728
        }
1✔
729

730
        if c.configMapSupportEnabled || c.secretSupportEnabled {
2✔
731
                // ensure that ConfigurationResources are prefixed by Stack name.
1✔
732
                if err := validateAllConfigurationResourcesNames(newStack.Stack); err != nil {
1✔
733
                        return err
×
734
                }
×
735
        }
736

737
        created, err := c.client.ZalandoV1().Stacks(newStack.Namespace()).Create(ctx, newStack.Stack, metav1.CreateOptions{})
1✔
738
        if err != nil {
1✔
739
                return err
×
740
        }
×
741
        fixupStackTypeMeta(created)
1✔
742

1✔
743
        c.recorder.Eventf(
1✔
744
                ssc.StackSet,
1✔
745
                v1.EventTypeNormal,
1✔
746
                "CreatedStack",
1✔
747
                "Created stack %s",
1✔
748
                newStack.Name(),
1✔
749
        )
1✔
750

1✔
751
        // Persist ObservedStackVersion in the status
1✔
752
        updated := ssc.StackSet.DeepCopy()
1✔
753
        updated.Status.ObservedStackVersion = newStackVersion
1✔
754

1✔
755
        result, err := c.client.ZalandoV1().StackSets(ssc.StackSet.Namespace).UpdateStatus(ctx, updated, metav1.UpdateOptions{})
1✔
756
        if err != nil {
1✔
757
                return err
×
758
        }
×
759
        fixupStackSetTypeMeta(result)
1✔
760
        ssc.StackSet = result
1✔
761

1✔
762
        ssc.StackContainers[created.UID] = &core.StackContainer{
1✔
763
                Stack:          created,
1✔
764
                PendingRemoval: false,
1✔
765
                Resources:      core.StackResources{},
1✔
766
        }
1✔
767
        return nil
1✔
768
}
769

770
// CleanupOldStacks deletes stacks that are no longer needed.
771
func (c *StackSetController) CleanupOldStacks(ctx context.Context, ssc *core.StackSetContainer) error {
1✔
772
        for _, sc := range ssc.StackContainers {
2✔
773
                if !sc.PendingRemoval {
2✔
774
                        continue
1✔
775
                }
776

777
                stack := sc.Stack
1✔
778
                err := c.client.ZalandoV1().Stacks(stack.Namespace).Delete(ctx, stack.Name, metav1.DeleteOptions{})
1✔
779
                if err != nil {
1✔
780
                        return c.errorEventf(ssc.StackSet, "FailedDeleteStack", err)
×
781
                }
×
782
                c.recorder.Eventf(
1✔
783
                        ssc.StackSet,
1✔
784
                        v1.EventTypeNormal,
1✔
785
                        "DeletedExcessStack",
1✔
786
                        "Deleted excess stack %s",
1✔
787
                        stack.Name)
1✔
788
        }
789

790
        return nil
1✔
791
}
792

793
// AddUpdateStackSetIngress reconciles the Ingress but never deletes it, it returns the existing/new Ingress
794
func (c *StackSetController) AddUpdateStackSetIngress(ctx context.Context, stackset *zv1.StackSet, existing *networking.Ingress, routegroup *rgv1.RouteGroup, ingress *networking.Ingress) (*networking.Ingress, error) {
×
795
        // Ingress removed, handled outside
×
796
        if ingress == nil {
×
797
                return existing, nil
×
798
        }
×
799

800
        if existing == nil {
×
801
                if ingress.Annotations == nil {
×
802
                        ingress.Annotations = make(map[string]string)
×
803
                }
×
804
                ingress.Annotations[ControllerLastUpdatedAnnotationKey] = c.now()
×
805

×
806
                createdIng, err := c.client.NetworkingV1().Ingresses(ingress.Namespace).Create(ctx, ingress, metav1.CreateOptions{})
×
807
                if err != nil {
×
808
                        return nil, err
×
809
                }
×
810
                c.recorder.Eventf(
×
811
                        stackset,
×
812
                        v1.EventTypeNormal,
×
813
                        "CreatedIngress",
×
814
                        "Created Ingress %s",
×
815
                        ingress.Name)
×
816
                return createdIng, nil
×
817
        }
818

819
        lastUpdateValue, existingHaveUpdateTimeStamp := existing.Annotations[ControllerLastUpdatedAnnotationKey]
×
820
        if existingHaveUpdateTimeStamp {
×
821
                delete(existing.Annotations, ControllerLastUpdatedAnnotationKey)
×
822
        }
×
823

824
        // Check if we need to update the Ingress
825
        if existingHaveUpdateTimeStamp && equality.Semantic.DeepDerivative(ingress.Spec, existing.Spec) &&
×
826
                equality.Semantic.DeepEqual(ingress.Annotations, existing.Annotations) &&
×
827
                equality.Semantic.DeepEqual(ingress.Labels, existing.Labels) {
×
828
                // add the annotation back after comparing
×
829
                existing.Annotations[ControllerLastUpdatedAnnotationKey] = lastUpdateValue
×
830
                return existing, nil
×
831
        }
×
832

833
        updated := existing.DeepCopy()
×
834
        updated.Spec = ingress.Spec
×
835
        if ingress.Annotations != nil {
×
836
                updated.Annotations = ingress.Annotations
×
837
        } else {
×
838
                updated.Annotations = make(map[string]string)
×
839
        }
×
840
        updated.Annotations[ControllerLastUpdatedAnnotationKey] = c.now()
×
841

×
842
        updated.Labels = ingress.Labels
×
843

×
844
        createdIngress, err := c.client.NetworkingV1().Ingresses(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
×
845
        if err != nil {
×
846
                return nil, err
×
847
        }
×
848
        c.recorder.Eventf(
×
849
                stackset,
×
850
                v1.EventTypeNormal,
×
851
                "UpdatedIngress",
×
852
                "Updated Ingress %s",
×
853
                ingress.Name)
×
854
        return createdIngress, nil
×
855
}
856

857
// AddUpdateStackSetRouteGroup reconciles the RouteGroup but never deletes it, it returns the existing/new RouteGroup
858
func (c *StackSetController) AddUpdateStackSetRouteGroup(ctx context.Context, stackset *zv1.StackSet, existing *rgv1.RouteGroup, ingress *networking.Ingress, rg *rgv1.RouteGroup) (*rgv1.RouteGroup, error) {
×
859
        // RouteGroup removed, handled outside
×
860
        if rg == nil {
×
861
                return existing, nil
×
862
        }
×
863

864
        // Create new RouteGroup
865
        if existing == nil {
×
866
                if rg.Annotations == nil {
×
867
                        rg.Annotations = make(map[string]string)
×
868
                }
×
869
                rg.Annotations[ControllerLastUpdatedAnnotationKey] = c.now()
×
870

×
871
                createdRg, err := c.client.RouteGroupV1().RouteGroups(rg.Namespace).Create(ctx, rg, metav1.CreateOptions{})
×
872
                if err != nil {
×
873
                        return nil, err
×
874
                }
×
875
                c.recorder.Eventf(
×
876
                        stackset,
×
877
                        v1.EventTypeNormal,
×
878
                        "CreatedRouteGroup",
×
879
                        "Created RouteGroup %s",
×
880
                        rg.Name)
×
881
                return createdRg, nil
×
882
        }
883

884
        lastUpdateValue, existingHaveUpdateTimeStamp := existing.Annotations[ControllerLastUpdatedAnnotationKey]
×
885
        if existingHaveUpdateTimeStamp {
×
886
                delete(existing.Annotations, ControllerLastUpdatedAnnotationKey)
×
887
        }
×
888

889
        // Check if we need to update the RouteGroup
890
        if existingHaveUpdateTimeStamp && equality.Semantic.DeepDerivative(rg.Spec, existing.Spec) &&
×
891
                equality.Semantic.DeepEqual(rg.Annotations, existing.Annotations) &&
×
892
                equality.Semantic.DeepEqual(rg.Labels, existing.Labels) {
×
893
                // add the annotation back after comparing
×
894
                existing.Annotations[ControllerLastUpdatedAnnotationKey] = lastUpdateValue
×
895
                return existing, nil
×
896
        }
×
897

898
        updated := existing.DeepCopy()
×
899
        updated.Spec = rg.Spec
×
900
        if rg.Annotations != nil {
×
901
                updated.Annotations = rg.Annotations
×
902
        } else {
×
903
                updated.Annotations = make(map[string]string)
×
904
        }
×
905
        updated.Annotations[ControllerLastUpdatedAnnotationKey] = c.now()
×
906

×
907
        updated.Labels = rg.Labels
×
908

×
909
        createdRg, err := c.client.RouteGroupV1().RouteGroups(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
×
910
        if err != nil {
×
911
                return nil, err
×
912
        }
×
913
        c.recorder.Eventf(
×
914
                stackset,
×
915
                v1.EventTypeNormal,
×
916
                "UpdatedRouteGroup",
×
917
                "Updated RouteGroup %s",
×
918
                rg.Name)
×
919
        return createdRg, nil
×
920
}
921

922
// RecordTrafficSwitch records an event detailing when switches in traffic to
923
// Stacks, only when there are changes to record.
924
func (c *StackSetController) RecordTrafficSwitch(ctx context.Context, ssc *core.StackSetContainer) error {
×
925
        trafficChanges := ssc.TrafficChanges()
×
926
        if len(trafficChanges) != 0 {
×
927
                var changeMessages []string
×
928
                for _, change := range trafficChanges {
×
929
                        changeMessages = append(changeMessages, change.String())
×
930
                }
×
931

932
                c.recorder.Eventf(
×
933
                        ssc.StackSet,
×
934
                        v1.EventTypeNormal,
×
935
                        "TrafficSwitched",
×
936
                        "Switched traffic: %s",
×
937
                        strings.Join(changeMessages, ", "))
×
938
        }
939

940
        return nil
×
941
}
942

943
func (c *StackSetController) ReconcileStackSetDesiredTraffic(ctx context.Context, existing *zv1.StackSet, generateUpdated func() []*zv1.DesiredTraffic) error {
1✔
944
        updatedTraffic := generateUpdated()
1✔
945

1✔
946
        if equality.Semantic.DeepEqual(existing.Spec.Traffic, updatedTraffic) {
1✔
947
                return nil
×
948
        }
×
949

950
        updated := existing.DeepCopy()
1✔
951
        updated.Spec.Traffic = updatedTraffic
1✔
952

1✔
953
        _, err := c.client.ZalandoV1().StackSets(updated.Namespace).Update(ctx, updated, metav1.UpdateOptions{})
1✔
954
        if err != nil {
1✔
955
                return err
×
956
        }
×
957
        c.recorder.Eventf(
1✔
958
                updated,
1✔
959
                v1.EventTypeNormal,
1✔
960
                "UpdatedStackSet",
1✔
961
                "Updated StackSet %s",
1✔
962
                updated.Name)
1✔
963
        return nil
1✔
964
}
965

966
func (c *StackSetController) ReconcileStackResources(ctx context.Context, ssc *core.StackSetContainer, sc *core.StackContainer) error {
×
967
        err := c.ReconcileStackIngress(ctx, sc.Stack, sc.Resources.Ingress, sc.GenerateIngress)
×
968
        if err != nil {
×
969
                return c.errorEventf(sc.Stack, "FailedManageIngress", err)
×
970
        }
×
971

972
        err = c.ReconcileStackIngress(
×
973
                ctx,
×
974
                sc.Stack,
×
975
                sc.Resources.IngressSegment,
×
976
                sc.GenerateIngressSegment,
×
977
        )
×
978
        if err != nil {
×
979
                return c.errorEventf(sc.Stack, "FailedManageIngressSegment", err)
×
980
        }
×
981

982
        if c.routeGroupSupportEnabled {
×
983
                err = c.ReconcileStackRouteGroup(ctx, sc.Stack, sc.Resources.RouteGroup, sc.GenerateRouteGroup)
×
984
                if err != nil {
×
985
                        return c.errorEventf(sc.Stack, "FailedManageRouteGroup", err)
×
986
                }
×
987

988
                err = c.ReconcileStackRouteGroup(
×
989
                        ctx,
×
990
                        sc.Stack,
×
991
                        sc.Resources.RouteGroupSegment,
×
992
                        sc.GenerateRouteGroupSegment,
×
993
                )
×
994
                if err != nil {
×
995
                        return c.errorEventf(
×
996
                                sc.Stack,
×
997
                                "FailedManageRouteGroupSegment",
×
998
                                err,
×
999
                        )
×
1000
                }
×
1001
        }
1002

1003
        if c.configMapSupportEnabled {
×
NEW
1004
                err = c.ReconcileStackConfigMaps(ctx, sc.Stack, sc.Resources.ConfigMaps, sc.GenerateConfigMaps)
×
1005
                if err != nil {
×
NEW
1006
                        return c.errorEventf(sc.Stack, "FailedManageConfigMaps", err)
×
1007
                }
×
1008

1009
                // err = c.ReconcileStackConfigMapRefs(ctx, sc.Stack, sc.UpdateObjectMeta)
1010
                // if err != nil {
1011
                //         return c.errorEventf(sc.Stack, "FailedManageConfigMapRefs", err)
1012
                // }
1013
        }
1014

1015
        if c.secretSupportEnabled {
×
1016
                err := c.ReconcileStackSecretRefs(ctx, sc.Stack, sc.UpdateObjectMeta)
×
1017
                if err != nil {
×
1018
                        return c.errorEventf(sc.Stack, "FailedManageSecretRefs", err)
×
1019
                }
×
1020
        }
1021

1022
        err = c.ReconcileStackDeployment(ctx, sc.Stack, sc.Resources.Deployment, sc.GenerateDeployment)
×
1023
        if err != nil {
×
1024
                return c.errorEventf(sc.Stack, "FailedManageDeployment", err)
×
1025
        }
×
1026

1027
        hpaGenerator := sc.GenerateHPA
×
1028
        err = c.ReconcileStackHPA(ctx, sc.Stack, sc.Resources.HPA, hpaGenerator)
×
1029
        if err != nil {
×
1030
                return c.errorEventf(sc.Stack, "FailedManageHPA", err)
×
1031
        }
×
1032

1033
        err = c.ReconcileStackService(ctx, sc.Stack, sc.Resources.Service, sc.GenerateService)
×
1034
        if err != nil {
×
1035
                return c.errorEventf(sc.Stack, "FailedManageService", err)
×
1036
        }
×
1037

1038
        return nil
×
1039
}
1040

1041
// ReconcileStackSet reconciles all the things from a stackset
1042
func (c *StackSetController) ReconcileStackSet(ctx context.Context, container *core.StackSetContainer) (err error) {
×
1043
        defer func() {
×
1044
                if r := recover(); r != nil {
×
1045
                        c.metricsReporter.ReportPanic()
×
1046
                        c.stacksetLogger(container).Errorf("Encountered a panic while processing a stackset: %v\n%s", r, debug.Stack())
×
1047
                        err = fmt.Errorf("panic: %v", r)
×
1048
                }
×
1049
        }()
1050

1051
        // Create current stack, if needed. Proceed on errors.
1052
        err = c.CreateCurrentStack(ctx, container)
×
1053
        if err != nil {
×
1054
                err = c.errorEventf(container.StackSet, "FailedCreateStack", err)
×
1055
                c.stacksetLogger(container).Errorf("Unable to create stack: %v", err)
×
1056
        }
×
1057

1058
        // Update statuses from external resources (ingresses, deployments, etc). Abort on errors.
1059
        err = container.UpdateFromResources()
×
1060
        if err != nil {
×
1061
                return err
×
1062
        }
×
1063

1064
        // Update the stacks with the currently selected traffic reconciler. Proceed on errors.
1065
        err = container.ManageTraffic(time.Now())
×
1066
        if err != nil {
×
1067
                c.stacksetLogger(container).Errorf("Traffic reconciliation failed: %v", err)
×
1068
                c.recorder.Eventf(
×
1069
                        container.StackSet,
×
1070
                        v1.EventTypeWarning,
×
1071
                        "TrafficNotSwitched",
×
1072
                        "Failed to switch traffic: "+err.Error())
×
1073
        }
×
1074

1075
        // Mark stacks that should be removed
1076
        container.MarkExpiredStacks()
×
1077

×
1078
        // Update traffic segments. Proceed on errors.
×
1079
        segsInOrder, err := c.ReconcileTrafficSegments(ctx, container)
×
1080
        if err != nil {
×
1081
                err = c.errorEventf(
×
1082
                        container.StackSet,
×
1083
                        reasonFailedManageStackSet,
×
1084
                        err,
×
1085
                )
×
1086
                c.stacksetLogger(container).Errorf(
×
1087
                        "Unable to reconcile traffic segments: %v",
×
1088
                        err,
×
1089
                )
×
1090
        }
×
1091

1092
        // Reconcile stack resources. Proceed on errors.
1093
        reconciledStacks := map[types.UID]bool{}
×
1094
        for _, id := range segsInOrder {
×
1095
                reconciledStacks[id] = true
×
1096
                sc := container.StackContainers[id]
×
1097
                err = c.ReconcileStackResources(ctx, container, sc)
×
1098
                if err != nil {
×
1099
                        err = c.errorEventf(sc.Stack, "FailedManageStack", err)
×
1100
                        c.stackLogger(container, sc).Errorf(
×
1101
                                "Unable to reconcile stack resources: %v",
×
1102
                                err,
×
1103
                        )
×
1104
                }
×
1105
        }
1106

1107
        for k, sc := range container.StackContainers {
×
1108
                if reconciledStacks[k] {
×
1109
                        continue
×
1110
                }
1111

1112
                err = c.ReconcileStackResources(ctx, container, sc)
×
1113
                if err != nil {
×
1114
                        err = c.errorEventf(sc.Stack, "FailedManageStack", err)
×
1115
                        c.stackLogger(container, sc).Errorf("Unable to reconcile stack resources: %v", err)
×
1116
                }
×
1117
        }
1118

1119
        // Reconcile stackset resources (update ingress and/or routegroups). Proceed on errors.
1120
        err = c.RecordTrafficSwitch(ctx, container)
×
1121
        if err != nil {
×
1122
                err = c.errorEventf(container.StackSet, reasonFailedManageStackSet, err)
×
1123
                c.stacksetLogger(container).Errorf("Unable to reconcile stackset resources: %v", err)
×
1124
        }
×
1125

1126
        // Reconcile desired traffic in the stackset. Proceed on errors.
1127
        err = c.ReconcileStackSetDesiredTraffic(ctx, container.StackSet, container.GenerateStackSetTraffic)
×
1128
        if err != nil {
×
1129
                err = c.errorEventf(container.StackSet, reasonFailedManageStackSet, err)
×
1130
                c.stacksetLogger(container).Errorf("Unable to reconcile stackset traffic: %v", err)
×
1131
        }
×
1132

1133
        // Delete old stacks. Proceed on errors.
1134
        err = c.CleanupOldStacks(ctx, container)
×
1135
        if err != nil {
×
1136
                err = c.errorEventf(container.StackSet, reasonFailedManageStackSet, err)
×
1137
                c.stacksetLogger(container).Errorf("Unable to delete old stacks: %v", err)
×
1138
        }
×
1139

1140
        // Update statuses.
1141
        err = c.ReconcileStatuses(ctx, container)
×
1142
        if err != nil {
×
1143
                return err
×
1144
        }
×
1145

1146
        return nil
×
1147
}
1148

1149
// getResetMinReplicasDelay parses and returns the reset delay if set in the
1150
// stackset annotation.
1151
func getResetMinReplicasDelay(annotations map[string]string) (time.Duration, bool) {
1✔
1152
        resetDelayStr, ok := annotations[ResetHPAMinReplicasDelayAnnotationKey]
1✔
1153
        if !ok {
2✔
1154
                return 0, false
1✔
1155
        }
1✔
1156
        resetDelay, err := time.ParseDuration(resetDelayStr)
1✔
1157
        if err != nil {
1✔
1158
                return 0, false
×
1159
        }
×
1160
        return resetDelay, true
1✔
1161
}
1162

1163
func fixupStackSetTypeMeta(stackset *zv1.StackSet) {
1✔
1164
        // set TypeMeta manually because of this bug:
1✔
1165
        // https://github.com/kubernetes/client-go/issues/308
1✔
1166
        stackset.APIVersion = core.APIVersion
1✔
1167
        stackset.Kind = core.KindStackSet
1✔
1168
}
1✔
1169

1170
func fixupStackTypeMeta(stack *zv1.Stack) {
1✔
1171
        // set TypeMeta manually because of this bug:
1✔
1172
        // https://github.com/kubernetes/client-go/issues/308
1✔
1173
        stack.APIVersion = core.APIVersion
1✔
1174
        stack.Kind = core.KindStack
1✔
1175
}
1✔
1176

1177
// validateConfigurationResourcesNames returns an error if any ConfigurationResource
1178
// name is not prefixed by Stack name.
1179
func validateAllConfigurationResourcesNames(stack *zv1.Stack) error {
1✔
1180
        for _, rsc := range stack.Spec.ConfigurationResources {
1✔
NEW
1181
                // TODO: no need to enforce naming scheme for inline definitions
×
NEW
1182
                if rsc.IsConfigMap() {
×
NEW
1183
                        continue
×
1184
                }
1185

1186
                if err := validateConfigurationResourceName(stack.Name, rsc.GetName()); err != nil {
×
1187
                        return err
×
1188
                }
×
1189
        }
1190
        return nil
1✔
1191
}
1192

1193
// validateConfigurationResourceName returns an error if specific resource
1194
// name is not prefixed by Stack name.
1195
func validateConfigurationResourceName(stack string, rsc string) error {
1✔
1196
        if !strings.HasPrefix(rsc, stack) {
2✔
1197
                return fmt.Errorf(configurationResourceNameError, rsc, stack)
1✔
1198
        }
1✔
1199
        return nil
1✔
1200
}
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