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

opendefensecloud / artifact-conduit / 20994364778

14 Jan 2026 12:38PM UTC coverage: 83.655% (-1.2%) from 84.904%
20994364778

push

github

web-flow
make ginkgo: autodetect version (#174)

737 of 881 relevant lines covered (83.65%)

1162.83 hits per line

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

80.73
/pkg/controller/workflow_handler.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
        "fmt"
9

10
        wfv1alpha1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
11
        "github.com/go-logr/logr"
12
        "github.com/jastBytes/sprint"
13
        arcv1alpha1 "go.opendefense.cloud/arc/api/arc/v1alpha1"
14
        corev1 "k8s.io/api/core/v1"
15
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16
        "sigs.k8s.io/controller-runtime/pkg/client"
17
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
18
)
19

20
type WorkflowHandler interface {
21
        DeleteArgoResources(ctx context.Context) error
22
        CreateArgoResources(ctx context.Context) error
23
        CheckArgoResources(ctx context.Context) error
24
}
25

26
var _ WorkflowHandler = &SingleWorkflowHandler{}
27

28
type SingleWorkflowHandler struct {
29
        *ArtifactWorkflowReconciler
30
        log logr.Logger
31
        aw  *arcv1alpha1.ArtifactWorkflow
32
}
33

34
func NewSingleWorkflowHandler(r *ArtifactWorkflowReconciler, log logr.Logger, aw *arcv1alpha1.ArtifactWorkflow) *SingleWorkflowHandler {
2,308✔
35
        return &SingleWorkflowHandler{r, log, aw}
2,308✔
36
}
2,308✔
37

38
func (h *SingleWorkflowHandler) DeleteArgoResources(ctx context.Context) error {
6✔
39
        wf := wfv1alpha1.Workflow{
6✔
40
                ObjectMeta: metav1.ObjectMeta{
6✔
41
                        Namespace: h.aw.Namespace,
6✔
42
                        Name:      h.aw.Name,
6✔
43
                },
6✔
44
        }
6✔
45
        if err := h.Delete(ctx, &wf); client.IgnoreNotFound(err) != nil {
6✔
46
                h.Recorder.Event(h.aw, corev1.EventTypeWarning, "DeletionFailed", fmt.Sprintf("Failed to delete associated workflow '%s': %v", h.aw.Name, err))
×
47
                return errLogAndWrap(h.log, err, "workflow deletion failed")
×
48
        }
×
49
        h.Recorder.Event(h.aw, corev1.EventTypeNormal, "Deleted", fmt.Sprintf("Deleted workflow '%s'", h.aw.Name))
6✔
50
        return nil
6✔
51
}
52

53
func (h *SingleWorkflowHandler) CreateArgoResources(ctx context.Context) error {
1,236✔
54
        srcSecret, dstSecret, err := h.retrieveSecrets(ctx, h.aw)
1,236✔
55
        if err != nil {
1,236✔
56
                return errLogAndWrap(h.log, err, "failed to fetch secrets for artifact workflow")
×
57
        }
×
58

59
        wf := hydrateArgoWorkflow(h.aw, srcSecret, dstSecret)
1,236✔
60

1,236✔
61
        if err := controllerutil.SetControllerReference(h.aw, wf, h.Scheme); err != nil {
1,236✔
62
                return errLogAndWrap(h.log, err, "failed to set controller reference")
×
63
        }
×
64

65
        if err := h.Create(ctx, wf); client.IgnoreAlreadyExists(err) != nil {
1,437✔
66
                h.Recorder.Event(h.aw, corev1.EventTypeWarning, "CreationFailed", fmt.Sprintf("Failed to create workflow '%s': %v", wf.GetName(), err))
201✔
67
                return errLogAndWrap(h.log, err, "failed to create argo workflow")
201✔
68
        }
201✔
69
        h.Recorder.Event(h.aw, corev1.EventTypeNormal, "Created", fmt.Sprintf("Created workflow '%s'", wf.GetName()))
1,035✔
70

1,035✔
71
        h.aw.Status.Phase = arcv1alpha1.WorkflowPending
1,035✔
72
        if err := h.Status().Update(ctx, h.aw); err != nil {
1,038✔
73
                return errLogAndWrap(h.log, err, "failed to update status")
3✔
74
        }
3✔
75
        return nil
1,032✔
76
}
77

78
func (h *SingleWorkflowHandler) CheckArgoResources(ctx context.Context) error {
1,034✔
79
        wf := wfv1alpha1.Workflow{}
1,034✔
80
        if err := h.Get(ctx, namespacedName(h.aw.Namespace, h.aw.Name), &wf); err != nil {
1,034✔
81
                return errLogAndWrap(h.log, err, "failed to get workflow")
×
82
        }
×
83

84
        if updated := h.setStatusFromWorkflow(ctx, h.log, h.aw, &wf); !updated {
1,037✔
85
                return nil // nothing updated
3✔
86
        }
3✔
87

88
        if err := h.Status().Update(ctx, h.aw); err != nil {
1,031✔
89
                return errLogAndWrap(h.log, err, "failed to update status")
×
90
        }
×
91

92
        return nil
1,031✔
93
}
94

95
var _ WorkflowHandler = &CronWorkflowHandler{}
96

97
type CronWorkflowHandler struct {
98
        *ArtifactWorkflowReconciler
99
        log logr.Logger
100
        aw  *arcv1alpha1.ArtifactWorkflow
101
}
102

103
func NewCronWorkflowHandler(r *ArtifactWorkflowReconciler, log logr.Logger, aw *arcv1alpha1.ArtifactWorkflow) *CronWorkflowHandler {
15✔
104
        return &CronWorkflowHandler{r, log, aw}
15✔
105
}
15✔
106

107
func (h *CronWorkflowHandler) DeleteArgoResources(ctx context.Context) error {
×
108
        cwf := wfv1alpha1.CronWorkflow{
×
109
                ObjectMeta: metav1.ObjectMeta{
×
110
                        Namespace: h.aw.Namespace,
×
111
                        Name:      h.aw.Name,
×
112
                },
×
113
        }
×
114
        if err := h.Delete(ctx, &cwf); client.IgnoreNotFound(err) != nil {
×
115
                h.Recorder.Event(h.aw, corev1.EventTypeWarning, "DeletionFailed", fmt.Sprintf("Failed to delete associated cron workflow '%s': %v", h.aw.Name, err))
×
116
                return errLogAndWrap(h.log, err, "cron workflow deletion failed")
×
117
        }
×
118
        h.Recorder.Event(h.aw, corev1.EventTypeNormal, "Deleted", fmt.Sprintf("Deleted cron workflow '%s'", h.aw.Name))
×
119
        return nil
×
120
}
121

122
func (h *CronWorkflowHandler) CreateArgoResources(ctx context.Context) error {
1✔
123
        srcSecret, dstSecret, err := h.retrieveSecrets(ctx, h.aw)
1✔
124
        if err != nil {
1✔
125
                return errLogAndWrap(h.log, err, "failed to fetch secrets for artifact workflow")
×
126
        }
×
127

128
        cwf := hydrateArgoCronWorkflow(h.aw, srcSecret, dstSecret)
1✔
129

1✔
130
        if err := controllerutil.SetControllerReference(h.aw, cwf, h.Scheme); err != nil {
1✔
131
                return errLogAndWrap(h.log, err, "failed to set controller reference")
×
132
        }
×
133

134
        if err := h.Create(ctx, cwf); err != nil {
1✔
135
                if client.IgnoreAlreadyExists(err) != nil {
×
136
                        h.Recorder.Event(h.aw, corev1.EventTypeWarning, "CreationFailed", fmt.Sprintf("Failed to create cron workflow '%s': %v", cwf.GetName(), err))
×
137
                        return errLogAndWrap(h.log, err, "failed to create argo cron workflow")
×
138
                }
×
139
        } else {
1✔
140
                h.Recorder.Event(h.aw, corev1.EventTypeNormal, "Created", fmt.Sprintf("Created cron workflow '%s'", cwf.GetName()))
1✔
141
        }
1✔
142

143
        h.aw.Status.Phase = arcv1alpha1.WorkflowPending
1✔
144
        if err := h.Status().Update(ctx, h.aw); err != nil {
1✔
145
                return errLogAndWrap(h.log, err, "failed to update status")
×
146
        }
×
147
        return nil
1✔
148
}
149

150
func (h *CronWorkflowHandler) CheckArgoResources(ctx context.Context) error {
13✔
151
        cwf := wfv1alpha1.CronWorkflow{}
13✔
152
        if err := h.Get(ctx, namespacedName(h.aw.Namespace, h.aw.Name), &cwf); err != nil {
13✔
153
                return errLogAndWrap(h.log, err, "failed to get cron workflow")
×
154
        }
×
155

156
        updated := false
13✔
157

13✔
158
        if !h.aw.Status.LastScheduled.Equal(cwf.Status.LastScheduledTime) {
14✔
159
                h.aw.Status.LastScheduled = cwf.Status.LastScheduledTime
1✔
160
                updated = true
1✔
161
        }
1✔
162
        if h.aw.Status.Failed != cwf.Status.Failed {
14✔
163
                h.aw.Status.Failed = cwf.Status.Failed
1✔
164
                updated = true
1✔
165
        }
1✔
166
        if h.aw.Status.Succeeded != cwf.Status.Succeeded {
14✔
167
                h.aw.Status.Succeeded = cwf.Status.Succeeded
1✔
168
                updated = true
1✔
169
        }
1✔
170

171
        // If the active workflow is not the same as the current one, update the reference
172
        if len(cwf.Status.Active) > 0 {
24✔
173
                // Should only contain a single element at most (expected to be in the same namespace!)
11✔
174
                ref := cwf.Status.Active[len(cwf.Status.Active)-1]
11✔
175

11✔
176
                if h.aw.Status.ActiveWorkflowRef.Name != ref.Name {
19✔
177
                        h.log.V(1).Info("Updating reference for cron workflow", "cronWorkflow", cwf.Name, "activeWorkflow", ref.Name)
8✔
178

8✔
179
                        // Get the active workflow
8✔
180
                        wf := wfv1alpha1.Workflow{}
8✔
181
                        if err := h.Get(ctx, namespacedName(h.aw.Namespace, ref.Name), &wf); err != nil {
8✔
182
                                return errLogAndWrap(h.log, err, "failed to fetch active workflow")
×
183
                        }
×
184

185
                        h.aw.Status.ActiveWorkflowRef = corev1.LocalObjectReference{
8✔
186
                                Name: wf.Name,
8✔
187
                        }
8✔
188
                        h.aw.Status.Message = ""
8✔
189
                        h.aw.Status.Phase = arcv1alpha1.WorkflowActive
8✔
190

8✔
191
                        updated = updated || h.setStatusFromWorkflow(ctx, h.log, h.aw, &wf)
8✔
192
                }
193
        }
194

195
        // If there is an active workflow, check its status
196
        if h.aw.Status.ActiveWorkflowRef.Name != "" {
24✔
197
                wf := wfv1alpha1.Workflow{}
11✔
198
                if err := h.Get(ctx, namespacedName(h.aw.Namespace, h.aw.Status.ActiveWorkflowRef.Name), &wf); err != nil {
11✔
199
                        return errLogAndWrap(h.log, err, "failed to fetch active workflow")
×
200
                }
×
201

202
                updated = updated || h.setStatusFromWorkflow(ctx, h.log, h.aw, &wf)
11✔
203

11✔
204
                if wf.Status.Phase.Completed() {
19✔
205
                        h.aw.Status.ActiveWorkflowRef.Name = ""
8✔
206
                        updated = true
8✔
207
                }
8✔
208
        }
209

210
        if !updated {
16✔
211
                return nil
3✔
212
        }
3✔
213

214
        h.log.V(1).Info("Updating status from active workflow", "cronWorkflow", cwf.Name)
10✔
215

10✔
216
        if err := h.Status().Update(ctx, h.aw); err != nil {
10✔
217
                return errLogAndWrap(h.log, err, "failed to update status")
×
218
        }
×
219

220
        return nil
10✔
221
}
222

223
func hydrateArgoWorkflowSpec(aw *arcv1alpha1.ArtifactWorkflow, srcSecret *corev1.Secret, dstSecret *corev1.Secret) wfv1alpha1.WorkflowSpec {
1,237✔
224
        srcVolume := corev1.Volume{
1,237✔
225
                Name: "src-secret-vol",
1,237✔
226
                VolumeSource: corev1.VolumeSource{
1,237✔
227
                        EmptyDir: &corev1.EmptyDirVolumeSource{},
1,237✔
228
                },
1,237✔
229
        }
1,237✔
230
        if srcSecret.Name != "" {
2,161✔
231
                srcVolume.VolumeSource = corev1.VolumeSource{
924✔
232
                        Secret: &corev1.SecretVolumeSource{
924✔
233
                                SecretName: srcSecret.Name,
924✔
234
                        },
924✔
235
                }
924✔
236
        }
924✔
237

238
        dstVolume := corev1.Volume{
1,237✔
239
                Name: "dst-secret-vol",
1,237✔
240
                VolumeSource: corev1.VolumeSource{
1,237✔
241
                        EmptyDir: &corev1.EmptyDirVolumeSource{},
1,237✔
242
                },
1,237✔
243
        }
1,237✔
244
        if dstSecret.Name != "" {
2,161✔
245
                dstVolume.VolumeSource = corev1.VolumeSource{
924✔
246
                        Secret: &corev1.SecretVolumeSource{
924✔
247
                                SecretName: dstSecret.Name,
924✔
248
                        },
924✔
249
                }
924✔
250
        }
924✔
251

252
        parameters := []wfv1alpha1.Parameter{}
1,237✔
253
        for _, p := range aw.Spec.Parameters {
9,071✔
254
                parameters = append(parameters, wfv1alpha1.Parameter{
7,834✔
255
                        Name:  p.Name,
7,834✔
256
                        Value: (*wfv1alpha1.AnyString)(&p.Value),
7,834✔
257
                })
7,834✔
258
        }
7,834✔
259

260
        return wfv1alpha1.WorkflowSpec{
1,237✔
261
                WorkflowTemplateRef: &wfv1alpha1.WorkflowTemplateRef{
1,237✔
262
                        Name:         aw.Spec.WorkflowTemplateRef.Name,
1,237✔
263
                        ClusterScope: aw.Spec.WorkflowTemplateRef.ClusterScope,
1,237✔
264
                },
1,237✔
265
                Volumes: []corev1.Volume{
1,237✔
266
                        srcVolume,
1,237✔
267
                        dstVolume,
1,237✔
268
                },
1,237✔
269
                Arguments: wfv1alpha1.Arguments{
1,237✔
270
                        Parameters: parameters,
1,237✔
271
                },
1,237✔
272
        }
1,237✔
273
}
274

275
func hydrateArgoWorkflow(aw *arcv1alpha1.ArtifactWorkflow, srcSecret *corev1.Secret, dstSecret *corev1.Secret) *wfv1alpha1.Workflow {
1,236✔
276
        return &wfv1alpha1.Workflow{
1,236✔
277
                ObjectMeta: workflowObjectMeta(aw),
1,236✔
278
                Spec:       hydrateArgoWorkflowSpec(aw, srcSecret, dstSecret),
1,236✔
279
        }
1,236✔
280
}
1,236✔
281

282
func hydrateArgoCronWorkflow(aw *arcv1alpha1.ArtifactWorkflow, srcSecret *corev1.Secret, dstSecret *corev1.Secret) *wfv1alpha1.CronWorkflow {
1✔
283
        om := workflowObjectMeta(aw)
1✔
284
        wf := &wfv1alpha1.CronWorkflow{
1✔
285
                ObjectMeta: om,
1✔
286
                Spec: wfv1alpha1.CronWorkflowSpec{
1✔
287
                        WorkflowSpec:               hydrateArgoWorkflowSpec(aw, srcSecret, dstSecret),
1✔
288
                        Schedules:                  aw.Spec.Cron.Schedules,
1✔
289
                        ConcurrencyPolicy:          wfv1alpha1.ReplaceConcurrent,
1✔
290
                        StartingDeadlineSeconds:    aw.Spec.Cron.StartingDeadlineSeconds,
1✔
291
                        Timezone:                   aw.Spec.Cron.Timezone,
1✔
292
                        When:                       aw.Spec.Cron.When,
1✔
293
                        SuccessfulJobsHistoryLimit: sprint.ToPointer(int32(1)),
1✔
294
                        FailedJobsHistoryLimit:     sprint.ToPointer(int32(1)),
1✔
295
                        WorkflowMetadata:           &om,
1✔
296
                },
1✔
297
        }
1✔
298

1✔
299
        return wf
1✔
300
}
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