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

opendefensecloud / artifact-conduit / 19698835285

26 Nov 2025 09:26AM UTC coverage: 64.242% (+0.4%) from 63.83%
19698835285

push

github

jastBytes
refactor: standardize TTL field naming and update related logic across workflows

1 of 2 new or added lines in 1 file covered. (50.0%)

58 existing lines in 2 files now uncovered.

530 of 825 relevant lines covered (64.24%)

888.66 hits per line

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

78.45
/pkg/controller/order_controller.go
1
// Copyright 2025 BWI GmbH and Artifact Conduit contributors
2
// SPDX-License-Identifier: Apache-2.0
3

4
package controller
5

6
import (
7
        "context"
8
        "crypto/sha256"
9
        "encoding/hex"
10
        "encoding/json"
11
        "fmt"
12
        "slices"
13
        "time"
14

15
        "github.com/go-logr/logr"
16
        arcv1alpha1 "go.opendefense.cloud/arc/api/arc/v1alpha1"
17
        corev1 "k8s.io/api/core/v1"
18
        apierrors "k8s.io/apimachinery/pkg/api/errors"
19
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20
        "k8s.io/apimachinery/pkg/runtime"
21
        "k8s.io/client-go/tools/record"
22
        ctrl "sigs.k8s.io/controller-runtime"
23
        "sigs.k8s.io/controller-runtime/pkg/client"
24
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
25
)
26

27
const (
28
        orderFinalizer = "arc.bwi.de/order-finalizer"
29
)
30

31
// OrderReconciler reconciles a Order object
32
type OrderReconciler struct {
33
        client.Client
34
        Scheme   *runtime.Scheme
35
        Recorder record.EventRecorder
36
}
37

38
type desiredAW struct {
39
        index       int
40
        objectMeta  metav1.ObjectMeta
41
        artifact    *arcv1alpha1.OrderArtifact
42
        srcEndpoint *arcv1alpha1.Endpoint
43
        dstEndpoint *arcv1alpha1.Endpoint
44
        srcSecret   *corev1.Secret
45
        dstSecret   *corev1.Secret
46
        sha         string
47
}
48

49
//+kubebuilder:rbac:groups=arc.bwi.de,resources=endpoints,verbs=get;list;watch
50
//+kubebuilder:rbac:groups=arc.bwi.de,resources=artifacttypes,verbs=get;list;watch
51
//+kubebuilder:rbac:groups=arc.bwi.de,resources=clusterartifacttypes,verbs=get;list;watch
52
//+kubebuilder:rbac:groups=arc.bwi.de,resources=artifactworkflows,verbs=get;list;watch;create;update;patch;delete
53
//+kubebuilder:rbac:groups=arc.bwi.de,resources=orders,verbs=get;list;watch;create;update;patch;delete
54
//+kubebuilder:rbac:groups=arc.bwi.de,resources=orders/status,verbs=get;update;patch
55
//+kubebuilder:rbac:groups=arc.bwi.de,resources=orders/finalizers,verbs=update
56
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
57
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
58

59
// Reconcile moves the current state of the cluster closer to the desired state
60
func (r *OrderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
2,086✔
61
        log := ctrl.LoggerFrom(ctx)
2,086✔
62

2,086✔
63
        // Fetch the Order instance
2,086✔
64
        order := &arcv1alpha1.Order{}
2,086✔
65
        if err := r.Get(ctx, req.NamespacedName, order); err != nil {
2,089✔
66
                if apierrors.IsNotFound(err) {
6✔
67
                        // Object not found, return. Created objects are automatically garbage collected.
3✔
68
                        return ctrl.Result{}, nil
3✔
69
                }
3✔
UNCOV
70
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to get object")
×
71
        }
72

73
        // Handle deletion: cleanup artifact workflows, then remove finalizer
74
        if !order.DeletionTimestamp.IsZero() {
2,085✔
75
                log.V(1).Info("Order is being deleted")
2✔
76
                r.Recorder.Event(order, corev1.EventTypeWarning, "Deleting", "Order is being deleted, cleaning up artifact workflows")
2✔
77

2✔
78
                // Cleanup all artifact workflows
2✔
79
                if len(order.Status.ArtifactWorkflows) > 0 {
3✔
80
                        for sha := range order.Status.ArtifactWorkflows {
3✔
81
                                // Remove Secret and ArtifactWorkflow
2✔
82
                                aw := &arcv1alpha1.ArtifactWorkflow{
2✔
83
                                        ObjectMeta: awObjectMeta(order, sha),
2✔
84
                                }
2✔
85
                                _ = r.Delete(ctx, aw) // Ignore errors
2✔
86
                                delete(order.Status.ArtifactWorkflows, sha)
2✔
87
                        }
2✔
88
                        if err := r.Status().Update(ctx, order); err != nil {
1✔
89
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to update order status")
×
UNCOV
90
                        }
×
91
                        log.V(1).Info("Order artifact workflows cleaned up")
1✔
92

1✔
93
                        // Requeue until all artifact workflows are gone
1✔
94
                        return ctrl.Result{}, nil
1✔
95
                }
96
                // All artifact workflows are gone, remove finalizer
97
                if slices.Contains(order.Finalizers, orderFinalizer) {
2✔
98
                        log.V(1).Info("No artifact workflows, removing finalizer from Order")
1✔
99
                        order.Finalizers = slices.DeleteFunc(order.Finalizers, func(f string) bool {
2✔
100
                                return f == orderFinalizer
1✔
101
                        })
1✔
102
                        if err := r.Update(ctx, order); err != nil {
1✔
103
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to remove finalizer")
×
UNCOV
104
                        }
×
105
                }
106
                return ctrl.Result{}, nil
1✔
107
        }
108

109
        // Add finalizer if not present and not deleting
110
        if order.DeletionTimestamp.IsZero() {
4,162✔
111
                if !slices.Contains(order.Finalizers, orderFinalizer) {
2,092✔
112
                        log.V(1).Info("Adding finalizer to Order")
11✔
113
                        order.Finalizers = append(order.Finalizers, orderFinalizer)
11✔
114
                        if err := r.Update(ctx, order); err != nil {
11✔
115
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to add finalizer")
×
UNCOV
116
                        }
×
117
                        // Return without requeue; the Update event will trigger reconciliation again
118
                        return ctrl.Result{}, nil
11✔
119
                }
120
        }
121

122
        // Make sure status is initialized
123
        if order.Status.ArtifactWorkflows == nil {
2,084✔
124
                order.Status.ArtifactWorkflows = map[string]arcv1alpha1.OrderArtifactWorkflowStatus{}
14✔
125
        }
14✔
126

127
        // Before we compare to our status, let's fetch all necessary information
128
        // to compute desired state:
129
        desiredAWs := map[string]desiredAW{}
2,070✔
130
        for i, artifact := range order.Spec.Artifacts {
5,613✔
131
                daw, err := r.computeDesiredAW(ctx, log, order, &artifact, i)
3,543✔
132
                if err != nil {
3,577✔
133
                        r.Recorder.Event(order, corev1.EventTypeWarning, "ComputationFailed", fmt.Sprintf("Failed to compute desired artifact workflow for artifact index %d: %v", i, err))
34✔
134
                        order.Status.Message = fmt.Sprintf("Failed to compute desired artifact workflow for artifact index %d: %v", i, err)
34✔
135
                        if err := r.Status().Update(ctx, order); err != nil {
34✔
136
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to update status")
×
UNCOV
137
                        }
×
138
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to compute desired artifact workflow")
34✔
139
                }
140
                desiredAWs[daw.sha] = *daw
3,509✔
141
        }
142

143
        // List missing artifact workflows
144
        createAWs := []string{}
2,036✔
145
        for sha := range desiredAWs {
5,540✔
146
                _, exists := order.Status.ArtifactWorkflows[sha]
3,504✔
147
                if exists {
6,990✔
148
                        continue
3,486✔
149
                }
150
                createAWs = append(createAWs, sha)
18✔
151
        }
152

153
        // Find obsolete artifact workflows
154
        deleteAWs := []string{}
2,036✔
155
        for sha := range order.Status.ArtifactWorkflows {
5,523✔
156
                _, exists := desiredAWs[sha]
3,487✔
157
                if exists {
6,973✔
158
                        continue
3,486✔
159
                }
160
                deleteAWs = append(deleteAWs, sha)
1✔
161
        }
162

163
        // Find finished artifact workflows to clean up
164
        finishedAWs := []string{}
2,036✔
165
        for sha := range order.Status.ArtifactWorkflows {
5,523✔
166
                awStatus := order.Status.ArtifactWorkflows[sha]
3,487✔
167

3,487✔
168
                // Only consider succeeded workflows for TTL cleanup
3,487✔
169
                if awStatus.Phase != arcv1alpha1.WorkflowSucceeded {
6,862✔
170
                        continue
3,375✔
171
                }
172

173
                // If TTL is set, check if it has expired
174
                if order.Spec.TTLSecondsAfterCompletion != nil && *order.Spec.TTLSecondsAfterCompletion > 0 {
112✔
NEW
175
                        if time.Since(awStatus.CompletionTime.Time) > time.Duration(*order.Spec.TTLSecondsAfterCompletion)*time.Second {
×
UNCOV
176
                                finishedAWs = append(finishedAWs, sha)
×
UNCOV
177
                        }
×
UNCOV
178
                        continue
×
179
                }
180

181
                // No TTL set, cleanup immediately
182
                finishedAWs = append(finishedAWs, sha)
112✔
183
        }
184

185
        // Create missing artifact workflows
186
        for _, sha := range createAWs {
2,054✔
187
                daw := desiredAWs[sha]
18✔
188
                aw, err := r.hydrateArtifactWorkflow(&daw)
18✔
189
                if err != nil {
18✔
UNCOV
190
                        r.Recorder.Event(order, corev1.EventTypeWarning, "HydrationFailed", fmt.Sprintf("Failed to hydrate artifact workflow for artifact index %d: %v", daw.index, err))
×
UNCOV
191
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to hydrate artifact workflow")
×
UNCOV
192
                }
×
193

194
                // Set owner references
195
                if err := controllerutil.SetControllerReference(order, aw, r.Scheme); err != nil {
18✔
UNCOV
196
                        r.Recorder.Event(order, corev1.EventTypeWarning, "HydrationFailed", fmt.Sprintf("Failed to set controller reference for artifact workflow: %v", err))
×
UNCOV
197
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to set controller reference")
×
UNCOV
198
                }
×
199

200
                // Create artifact workflow
201
                if err := r.Create(ctx, aw); err != nil {
18✔
UNCOV
202
                        if apierrors.IsAlreadyExists(err) {
×
203
                                // Already created by a previous reconcile — that's fine
×
204
                                continue
×
205
                        }
UNCOV
206
                        r.Recorder.Event(order, corev1.EventTypeWarning, "CreationFailed", fmt.Sprintf("Failed to create artifact workflow for artifact index %d: %v", daw.index, err))
×
UNCOV
207
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to create artifact workflow")
×
208
                }
209

210
                // Update status
211
                order.Status.ArtifactWorkflows[sha] = arcv1alpha1.OrderArtifactWorkflowStatus{
18✔
212
                        ArtifactIndex: daw.index,
18✔
213
                        Phase:         arcv1alpha1.WorkflowUnknown,
18✔
214
                }
18✔
215

18✔
216
                r.Recorder.Event(order, corev1.EventTypeNormal, "ArtifactWorkflowCreated", fmt.Sprintf("Created artifact workflow '%s' for artifact index %d", aw.Name, daw.index))
18✔
217
                log.V(1).Info("Created artifact workflow", "artifactWorkflow", aw.Name)
18✔
218
        }
219

220
        // Delete obsolete artifact workflows
221
        for _, sha := range deleteAWs {
2,037✔
222
                // Does not exist anymore, let's clean up!
1✔
223
                if err := r.Delete(ctx, &arcv1alpha1.ArtifactWorkflow{
1✔
224
                        ObjectMeta: awObjectMeta(order, sha),
1✔
225
                }); client.IgnoreNotFound(err) != nil {
1✔
UNCOV
226
                        r.Recorder.Event(order, corev1.EventTypeWarning, "DeletionFailed", fmt.Sprintf("Failed to delete obsolete artifact workflow '%s': %v", sha, err))
×
UNCOV
227
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to delete artifact workflow")
×
UNCOV
228
                }
×
229

230
                // Update status
231
                delete(order.Status.ArtifactWorkflows, sha)
1✔
232
                log.V(1).Info("Deleted obsolete artifact workflow", "artifactWorkflow", sha)
1✔
233
                r.Recorder.Event(order, corev1.EventTypeNormal, "ArtifactWorkflowDeleted", fmt.Sprintf("Deleted obsolete artifact workflow '%s'", sha))
1✔
234
        }
235

236
        // Delete finished artifact workflows
237
        for _, sha := range finishedAWs {
2,148✔
238
                // Finished, let's clean up!
112✔
239
                if err := r.Delete(ctx, &arcv1alpha1.ArtifactWorkflow{
112✔
240
                        ObjectMeta: awObjectMeta(order, sha),
112✔
241
                }); client.IgnoreNotFound(err) != nil {
112✔
UNCOV
242
                        r.Recorder.Event(order, corev1.EventTypeWarning, "DeletionFailed", fmt.Sprintf("Failed to delete finished artifact workflow '%s': %v", sha, err))
×
UNCOV
243
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to delete artifact workflow")
×
UNCOV
244
                }
×
245

246
                log.V(1).Info("Deleted finished artifact workflow", "artifactWorkflow", sha)
112✔
247
                r.Recorder.Event(order, corev1.EventTypeNormal, "ArtifactWorkflowDeleted", fmt.Sprintf("Deleted finished artifact workflow '%s'", sha))
112✔
248
        }
249

250
        anyPhaseChanged := false
2,036✔
251
        for sha, daw := range desiredAWs {
5,444✔
252
                if slices.Contains(createAWs, sha) {
3,426✔
253
                        // If it was just created we skip the update
18✔
254
                        continue
18✔
255
                }
256
                aw := arcv1alpha1.ArtifactWorkflow{}
3,390✔
257
                if err := r.Get(ctx, namespacedName(daw.objectMeta.Namespace, daw.objectMeta.Name), &aw); err != nil {
3,500✔
258
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to get artifact workflow")
110✔
259
                }
110✔
260
                if order.Status.ArtifactWorkflows[sha].Phase != aw.Status.Phase {
4,580✔
261
                        awStatus := order.Status.ArtifactWorkflows[sha]
1,300✔
262
                        awStatus.Phase = aw.Status.Phase
1,300✔
263
                        awStatus.CompletionTime = aw.Status.CompletionTime
1,300✔
264
                        order.Status.ArtifactWorkflows[sha] = awStatus
1,300✔
265
                        anyPhaseChanged = true
1,300✔
266
                }
1,300✔
267
        }
268

269
        // Update status
270
        if len(createAWs) > 0 || len(deleteAWs) > 0 || anyPhaseChanged {
3,181✔
271
                log.V(1).Info("Updating order status")
1,255✔
272
                // Make sure ArtifactIndex is up to date
1,255✔
273
                for sha, daw := range desiredAWs {
3,427✔
274
                        aws := order.Status.ArtifactWorkflows[sha]
2,172✔
275
                        aws.ArtifactIndex = daw.index
2,172✔
276
                        order.Status.ArtifactWorkflows[sha] = aws
2,172✔
277
                }
2,172✔
278
                if err := r.Status().Update(ctx, order); err != nil {
1,301✔
279
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to update status")
46✔
280
                }
46✔
281
        }
282

283
        return ctrl.Result{}, nil
1,880✔
284
}
285

286
func (r *OrderReconciler) hydrateArtifactWorkflow(daw *desiredAW) (*arcv1alpha1.ArtifactWorkflow, error) {
18✔
287
        params, err := dawToParameters(daw)
18✔
288
        if err != nil {
18✔
289
                return nil, err
×
UNCOV
290
        }
×
291

292
        // Next we create the ArtifactWorkflow instance
293
        aw := &arcv1alpha1.ArtifactWorkflow{
18✔
294
                ObjectMeta: daw.objectMeta,
18✔
295
                Spec: arcv1alpha1.ArtifactWorkflowSpec{
18✔
296
                        Type:         daw.artifact.Type,
18✔
297
                        Parameters:   params,
18✔
298
                        SrcSecretRef: daw.srcEndpoint.Spec.SecretRef,
18✔
299
                        DstSecretRef: daw.dstEndpoint.Spec.SecretRef,
18✔
300
                },
18✔
301
        }
18✔
302

18✔
303
        return aw, nil
18✔
304
}
305

306
func (r *OrderReconciler) computeDesiredAW(ctx context.Context, log logr.Logger, order *arcv1alpha1.Order, artifact *arcv1alpha1.OrderArtifact, i int) (*desiredAW, error) {
3,543✔
307
        log = log.WithValues("artifactIndex", i)
3,543✔
308

3,543✔
309
        // We need the referenced src- and dst-endpoints for the artifact
3,543✔
310
        srcRefName := artifact.SrcRef.Name
3,543✔
311
        if srcRefName == "" {
4,058✔
312
                srcRefName = order.Spec.Defaults.SrcRef.Name
515✔
313
        }
515✔
314
        dstRefName := artifact.DstRef.Name
3,543✔
315
        if dstRefName == "" {
4,234✔
316
                dstRefName = order.Spec.Defaults.DstRef.Name
691✔
317
        }
691✔
318
        srcEndpoint := &arcv1alpha1.Endpoint{}
3,543✔
319
        if err := r.Get(ctx, namespacedName(order.Namespace, srcRefName), srcEndpoint); err != nil {
3,543✔
UNCOV
320
                r.Recorder.Event(order, corev1.EventTypeWarning, "InvalidEndpoint", fmt.Sprintf("Failed to fetch source endpoint '%s': %v", srcRefName, err))
×
UNCOV
321
                return nil, errLogAndWrap(log, err, "failed to fetch endpoint for source")
×
UNCOV
322
        }
×
323
        dstEndpoint := &arcv1alpha1.Endpoint{}
3,543✔
324
        if err := r.Get(ctx, namespacedName(order.Namespace, dstRefName), dstEndpoint); err != nil {
3,543✔
UNCOV
325
                r.Recorder.Event(order, corev1.EventTypeWarning, "InvalidEndpoint", fmt.Sprintf("Failed to fetch destination endpoint '%s': %v", dstRefName, err))
×
UNCOV
326
                return nil, errLogAndWrap(log, err, "failed to fetch endpoint for destination")
×
UNCOV
327
        }
×
328

329
        // Validate that the endpoint usage is correct
330
        if srcEndpoint.Spec.Usage != arcv1alpha1.EndpointUsagePullOnly && srcEndpoint.Spec.Usage != arcv1alpha1.EndpointUsageAll {
3,543✔
331
                err := fmt.Errorf("endpoint '%s' usage '%s' is not compatible with source usage", srcEndpoint.Name, srcEndpoint.Spec.Usage)
×
332
                r.Recorder.Event(order, corev1.EventTypeWarning, "InvalidEndpoint", fmt.Sprintf("Source endpoint '%s' has incompatible usage '%s'", srcEndpoint.Name, srcEndpoint.Spec.Usage))
×
333
                return nil, errLogAndWrap(log, err, "artifact validation failed")
×
334
        }
×
335
        if dstEndpoint.Spec.Usage != arcv1alpha1.EndpointUsagePushOnly && dstEndpoint.Spec.Usage != arcv1alpha1.EndpointUsageAll {
3,543✔
UNCOV
336
                err := fmt.Errorf("endpoint '%s' usage '%s' is not compatible with destination usage", dstEndpoint.Name, dstEndpoint.Spec.Usage)
×
UNCOV
337
                r.Recorder.Event(order, corev1.EventTypeWarning, "InvalidEndpoint", fmt.Sprintf("Destination endpoint '%s' has incompatible usage '%s'", dstEndpoint.Name, dstEndpoint.Spec.Usage))
×
UNCOV
338
                return nil, errLogAndWrap(log, err, "artifact validation failed")
×
UNCOV
339
        }
×
340

341
        // Validate against ArtifactType rules
342
        artifactType := &arcv1alpha1.ArtifactType{}
3,543✔
343
        if err := r.Get(ctx, namespacedName(order.Namespace, artifact.Type), artifactType); client.IgnoreNotFound(err) != nil {
3,543✔
UNCOV
344
                r.Recorder.Event(order, corev1.EventTypeWarning, "InvalidArtifactType", fmt.Sprintf("Failed to fetch ArtifactType '%s': %v", artifact.Type, err))
×
UNCOV
345
                return nil, errLogAndWrap(log, err, "failed to fetch referenced ArtifactType")
×
UNCOV
346
        }
×
347
        var (
3,543✔
348
                artifactTypeGen  int64
3,543✔
349
                artifactTypeSpec *arcv1alpha1.ArtifactTypeSpec
3,543✔
350
        )
3,543✔
351
        if artifactType.Name == "" { // was not found, let's check ClusterArtifactType
6,897✔
352
                clusterArtifactType := &arcv1alpha1.ClusterArtifactType{}
3,354✔
353
                if err := r.Get(ctx, namespacedName("", artifact.Type), clusterArtifactType); err != nil {
3,386✔
354
                        return nil, errLogAndWrap(log, err, "failed to fetch ArtifactType or ClusterArtifactType")
32✔
355
                }
32✔
356
                artifactTypeSpec = &clusterArtifactType.Spec
3,322✔
357
                artifactTypeGen = clusterArtifactType.Generation
3,322✔
358
        } else {
189✔
359
                artifactTypeSpec = &artifactType.Spec
189✔
360
                artifactTypeGen = artifactType.Generation
189✔
361
        }
189✔
362

363
        if len(artifactTypeSpec.Rules.SrcTypes) > 0 && !slices.Contains(artifactTypeSpec.Rules.SrcTypes, srcEndpoint.Spec.Type) {
3,513✔
364
                err := fmt.Errorf("source endpoint type '%s' is not allowed by ArtifactType rules", srcEndpoint.Spec.Type)
2✔
365
                r.Recorder.Event(order, corev1.EventTypeWarning, "InvalidArtifactType", fmt.Sprintf("Source endpoint type '%s' is not allowed by ArtifactType '%s' rules", srcEndpoint.Spec.Type, artifact.Type))
2✔
366
                return nil, errLogAndWrap(log, err, "artifact validation failed")
2✔
367
        }
2✔
368
        if len(artifactTypeSpec.Rules.DstTypes) > 0 && !slices.Contains(artifactTypeSpec.Rules.DstTypes, dstEndpoint.Spec.Type) {
3,509✔
UNCOV
369
                err := fmt.Errorf("destination endpoint type '%s' is not allowed by ArtifactType rules", dstEndpoint.Spec.Type)
×
UNCOV
370
                r.Recorder.Event(order, corev1.EventTypeWarning, "InvalidArtifactType", fmt.Sprintf("Destination endpoint type '%s' is not allowed by ArtifactType '%s' rules", dstEndpoint.Spec.Type, artifact.Type))
×
UNCOV
371
                return nil, errLogAndWrap(log, err, "artifact validation failed")
×
UNCOV
372
        }
×
373

374
        // Next, we need the secret contents
375
        srcSecret := &corev1.Secret{}
3,509✔
376
        if srcEndpoint.Spec.SecretRef.Name != "" {
7,016✔
377
                if err := r.Get(ctx, namespacedName(order.Namespace, srcEndpoint.Spec.SecretRef.Name), srcSecret); err != nil {
3,507✔
UNCOV
378
                        r.Recorder.Event(order, corev1.EventTypeWarning, "InvalidSecret", fmt.Sprintf("Failed to fetch source secret '%s': %v", srcEndpoint.Spec.SecretRef.Name, err))
×
UNCOV
379
                        return nil, errLogAndWrap(log, err, "failed to fetch secret for source")
×
UNCOV
380
                }
×
381
        }
382

383
        dstSecret := &corev1.Secret{}
3,509✔
384
        if dstEndpoint.Spec.SecretRef.Name != "" {
7,016✔
385
                if err := r.Get(ctx, namespacedName(order.Namespace, dstEndpoint.Spec.SecretRef.Name), dstSecret); err != nil {
3,507✔
UNCOV
386
                        r.Recorder.Event(order, corev1.EventTypeWarning, "InvalidSecret", fmt.Sprintf("Failed to fetch destination secret '%s': %v", dstEndpoint.Spec.SecretRef.Name, err))
×
UNCOV
387
                        return nil, errLogAndWrap(log, err, "failed to fetch secret for destination")
×
UNCOV
388
                }
×
389
        }
390

391
        // Create a hash based on all related data for idempotency and compute the workflow name
392
        h := sha256.New()
3,509✔
393
        data := []any{
3,509✔
394
                order.Namespace,
3,509✔
395
                artifact.Type, artifact.Spec.Raw, artifactTypeGen,
3,509✔
396
                srcEndpoint.Name,
3,509✔
397
                dstEndpoint.Name,
3,509✔
398
        }
3,509✔
399
        jsonData, err := json.Marshal(data)
3,509✔
400
        if err != nil {
3,509✔
UNCOV
401
                return nil, errLogAndWrap(log, err, "failed to marshal artifact workflow data")
×
UNCOV
402
        }
×
403
        h.Write(jsonData)
3,509✔
404
        sha := hex.EncodeToString(h.Sum(nil))[:16]
3,509✔
405

3,509✔
406
        // We gave all the information to further process this artifact workflow.
3,509✔
407
        // Let's store it to compare it to the current status!
3,509✔
408
        return &desiredAW{
3,509✔
409
                index:       i,
3,509✔
410
                objectMeta:  awObjectMeta(order, sha),
3,509✔
411
                artifact:    artifact,
3,509✔
412
                srcEndpoint: srcEndpoint,
3,509✔
413
                dstEndpoint: dstEndpoint,
3,509✔
414
                srcSecret:   srcSecret,
3,509✔
415
                dstSecret:   dstSecret,
3,509✔
416
                sha:         sha,
3,509✔
417
        }, nil
3,509✔
418
}
419

420
// SetupWithManager sets up the controller with the Manager.
421
func (r *OrderReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
422
        return ctrl.NewControllerManagedBy(mgr).
1✔
423
                For(&arcv1alpha1.Order{}).
1✔
424
                Owns(&arcv1alpha1.ArtifactWorkflow{}).
1✔
425
                Complete(r)
1✔
426
}
1✔
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