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

opendefensecloud / artifact-conduit / 29222863544

13 Jul 2026 03:59AM UTC coverage: 84.428% (-0.3%) from 84.746%
29222863544

Pull #436

github

web-flow
Merge 8efbe857f into f96a1a379
Pull Request #436: fix(deps): update k8s.io/kube-openapi digest to cdb1db5

797 of 944 relevant lines covered (84.43%)

1254.78 hits per line

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

83.26
/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/v4/pkg/apis/workflow/v1alpha1"
11
        "github.com/go-logr/logr"
12
        corev1 "k8s.io/api/core/v1"
13
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14
        "sigs.k8s.io/controller-runtime/pkg/client"
15
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
16

17
        arcv1alpha1 "go.opendefense.cloud/arc/api/arc/v1alpha1"
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,941✔
35
        return &SingleWorkflowHandler{r, log, aw}
2,941✔
36
}
2,941✔
37

38
func (h *SingleWorkflowHandler) DeleteArgoResources(ctx context.Context) error {
8✔
39
        wf := wfv1alpha1.Workflow{
8✔
40
                ObjectMeta: metav1.ObjectMeta{
8✔
41
                        Namespace: h.aw.Namespace,
8✔
42
                        Name:      h.aw.Name,
8✔
43
                },
8✔
44
        }
8✔
45
        if err := h.Delete(ctx, &wf); client.IgnoreNotFound(err) != nil {
8✔
46
                h.Recorder.Eventf(h.aw, nil, corev1.EventTypeWarning, "DeletionFailed", "Delete", 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.Eventf(h.aw, nil, corev1.EventTypeNormal, "Deleted", "Delete", fmt.Sprintf("Deleted workflow '%s'", h.aw.Name))
8✔
50

8✔
51
        return nil
8✔
52
}
53

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

60
        wf := hydrateArgoWorkflow(h.aw, srcSecret, dstSecret)
1,554✔
61

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

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

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

77
        return nil
1,340✔
78
}
79

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

86
        updated := h.setStatusFromWorkflow(ctx, h.log, h.aw, &wf)
1,344✔
87

1,344✔
88
        if wf.Status.Phase == wfv1alpha1.WorkflowSucceeded && h.aw.Status.Succeeded != 1 {
1,347✔
89
                h.aw.Status.Succeeded = 1
3✔
90
                updated = true
3✔
91
        }
3✔
92

93
        failed := wf.Status.Phase == wfv1alpha1.WorkflowError ||
1,344✔
94
                wf.Status.Phase == wfv1alpha1.WorkflowFailed
1,344✔
95

1,344✔
96
        if failed && h.aw.Status.Failed != 1 {
1,347✔
97
                h.aw.Status.Failed = 1
3✔
98
                updated = true
3✔
99
        }
3✔
100

101
        if !updated {
1,347✔
102
                return nil
3✔
103
        }
3✔
104

105
        if err := h.Status().Update(ctx, h.aw); err != nil {
1,342✔
106
                return errLogAndWrap(h.log, err, "failed to update status")
1✔
107
        }
1✔
108

109
        return nil
1,340✔
110
}
111

112
var _ WorkflowHandler = &CronWorkflowHandler{}
113

114
type CronWorkflowHandler struct {
115
        *ArtifactWorkflowReconciler
116
        log logr.Logger
117
        aw  *arcv1alpha1.ArtifactWorkflow
118
}
119

120
func NewCronWorkflowHandler(r *ArtifactWorkflowReconciler, log logr.Logger, aw *arcv1alpha1.ArtifactWorkflow) *CronWorkflowHandler {
24✔
121
        return &CronWorkflowHandler{r, log, aw}
24✔
122
}
24✔
123

124
func (h *CronWorkflowHandler) DeleteArgoResources(ctx context.Context) error {
×
125
        cwf := wfv1alpha1.CronWorkflow{
×
126
                ObjectMeta: metav1.ObjectMeta{
×
127
                        Namespace: h.aw.Namespace,
×
128
                        Name:      h.aw.Name,
×
129
                },
×
130
        }
×
131
        if err := h.Delete(ctx, &cwf); client.IgnoreNotFound(err) != nil {
×
132
                h.Recorder.Eventf(h.aw, nil, corev1.EventTypeWarning, "DeletionFailed", "Delete", fmt.Sprintf("Failed to delete associated cron workflow '%s': %v", h.aw.Name, err))
×
133
                return errLogAndWrap(h.log, err, "cron workflow deletion failed")
×
134
        }
×
135
        h.Recorder.Eventf(h.aw, nil, corev1.EventTypeNormal, "Deleted", "Delete", fmt.Sprintf("Deleted cron workflow '%s'", h.aw.Name))
×
136

×
137
        return nil
×
138
}
139

140
func (h *CronWorkflowHandler) CreateArgoResources(ctx context.Context) error {
2✔
141
        srcSecret, dstSecret, err := h.retrieveSecrets(ctx, h.aw)
2✔
142
        if err != nil {
2✔
143
                return errLogAndWrap(h.log, err, "failed to fetch secrets for artifact workflow")
×
144
        }
×
145

146
        cwf := hydrateArgoCronWorkflow(h.aw, srcSecret, dstSecret)
2✔
147

2✔
148
        if err := controllerutil.SetControllerReference(h.aw, cwf, h.Scheme); err != nil {
2✔
149
                return errLogAndWrap(h.log, err, "failed to set controller reference")
×
150
        }
×
151

152
        if err := h.Create(ctx, cwf); err != nil {
2✔
153
                if client.IgnoreAlreadyExists(err) != nil {
×
154
                        h.Recorder.Eventf(h.aw, nil, corev1.EventTypeWarning, "CreationFailed", "Create", fmt.Sprintf("Failed to create cron workflow '%s': %v", cwf.GetName(), err))
×
155
                        return errLogAndWrap(h.log, err, "failed to create argo cron workflow")
×
156
                }
×
157
        } else {
2✔
158
                h.Recorder.Eventf(h.aw, nil, corev1.EventTypeNormal, "Created", "Create", fmt.Sprintf("Created cron workflow '%s'", cwf.GetName()))
2✔
159
        }
2✔
160

161
        h.aw.Status.Phase = arcv1alpha1.WorkflowPending
2✔
162
        if err := h.Status().Update(ctx, h.aw); err != nil {
2✔
163
                return errLogAndWrap(h.log, err, "failed to update status")
×
164
        }
×
165

166
        return nil
2✔
167
}
168

169
func (h *CronWorkflowHandler) CheckArgoResources(ctx context.Context) error {
20✔
170
        cwf := wfv1alpha1.CronWorkflow{}
20✔
171
        if err := h.Get(ctx, namespacedName(h.aw.Namespace, h.aw.Name), &cwf); err != nil {
20✔
172
                return errLogAndWrap(h.log, err, "failed to get cron workflow")
×
173
        }
×
174

175
        updated := false
20✔
176

20✔
177
        if !h.aw.Status.LastScheduled.Equal(cwf.Status.LastScheduledTime) {
22✔
178
                h.aw.Status.LastScheduled = cwf.Status.LastScheduledTime
2✔
179
                updated = true
2✔
180
        }
2✔
181
        if h.aw.Status.Failed != cwf.Status.Failed {
21✔
182
                h.aw.Status.Failed = cwf.Status.Failed
1✔
183
                updated = true
1✔
184
        }
1✔
185
        if h.aw.Status.Succeeded != cwf.Status.Succeeded {
21✔
186
                h.aw.Status.Succeeded = cwf.Status.Succeeded
1✔
187
                updated = true
1✔
188
        }
1✔
189

190
        // If the active workflow is not the same as the current one, update the reference
191
        if len(cwf.Status.Active) > 0 {
36✔
192
                // Should only contain a single element at most (expected to be in the same namespace!)
16✔
193
                ref := cwf.Status.Active[len(cwf.Status.Active)-1]
16✔
194

16✔
195
                if h.aw.Status.ActiveWorkflowRef.Name != ref.Name {
26✔
196
                        h.log.V(1).Info("Updating reference for cron workflow", "cronWorkflow", cwf.Name, "activeWorkflow", ref.Name)
10✔
197

10✔
198
                        // Get the active workflow
10✔
199
                        wf := wfv1alpha1.Workflow{}
10✔
200
                        if err := h.Get(ctx, namespacedName(h.aw.Namespace, ref.Name), &wf); err != nil {
11✔
201
                                return errLogAndWrap(h.log, err, "failed to fetch active workflow")
1✔
202
                        }
1✔
203

204
                        h.aw.Status.ActiveWorkflowRef = corev1.LocalObjectReference{
9✔
205
                                Name: wf.Name,
9✔
206
                        }
9✔
207
                        h.aw.Status.Message = ""
9✔
208
                        h.aw.Status.Phase = arcv1alpha1.WorkflowActive
9✔
209

9✔
210
                        updated = updated || h.setStatusFromWorkflow(ctx, h.log, h.aw, &wf)
9✔
211
                }
212
        }
213

214
        // If there is an active workflow, check its status
215
        if h.aw.Status.ActiveWorkflowRef.Name != "" {
34✔
216
                wf := wfv1alpha1.Workflow{}
15✔
217
                if err := h.Get(ctx, namespacedName(h.aw.Namespace, h.aw.Status.ActiveWorkflowRef.Name), &wf); err != nil {
15✔
218
                        return errLogAndWrap(h.log, err, "failed to fetch active workflow")
×
219
                }
×
220

221
                updated = updated || h.setStatusFromWorkflow(ctx, h.log, h.aw, &wf)
15✔
222

15✔
223
                if wf.Status.Phase.Completed() {
24✔
224
                        h.aw.Status.ActiveWorkflowRef.Name = ""
9✔
225
                        updated = true
9✔
226
                }
9✔
227
        }
228

229
        if !updated {
25✔
230
                return nil
6✔
231
        }
6✔
232

233
        h.log.V(1).Info("Updating status from active workflow", "cronWorkflow", cwf.Name)
13✔
234

13✔
235
        if err := h.Status().Update(ctx, h.aw); err != nil {
13✔
236
                return errLogAndWrap(h.log, err, "failed to update status")
×
237
        }
×
238

239
        return nil
13✔
240
}
241

242
func hydrateArgoWorkflowSpec(aw *arcv1alpha1.ArtifactWorkflow, srcSecret *corev1.Secret, dstSecret *corev1.Secret) wfv1alpha1.WorkflowSpec {
1,556✔
243
        srcVolume := corev1.Volume{
1,556✔
244
                Name: "src-secret-vol",
1,556✔
245
                VolumeSource: corev1.VolumeSource{
1,556✔
246
                        EmptyDir: &corev1.EmptyDirVolumeSource{},
1,556✔
247
                },
1,556✔
248
        }
1,556✔
249
        if srcSecret.Name != "" {
2,666✔
250
                srcVolume.VolumeSource = corev1.VolumeSource{
1,110✔
251
                        Secret: &corev1.SecretVolumeSource{
1,110✔
252
                                SecretName: srcSecret.Name,
1,110✔
253
                        },
1,110✔
254
                }
1,110✔
255
        }
1,110✔
256

257
        dstVolume := corev1.Volume{
1,556✔
258
                Name: "dst-secret-vol",
1,556✔
259
                VolumeSource: corev1.VolumeSource{
1,556✔
260
                        EmptyDir: &corev1.EmptyDirVolumeSource{},
1,556✔
261
                },
1,556✔
262
        }
1,556✔
263
        if dstSecret.Name != "" {
2,666✔
264
                dstVolume.VolumeSource = corev1.VolumeSource{
1,110✔
265
                        Secret: &corev1.SecretVolumeSource{
1,110✔
266
                                SecretName: dstSecret.Name,
1,110✔
267
                        },
1,110✔
268
                }
1,110✔
269
        }
1,110✔
270

271
        parameters := []wfv1alpha1.Parameter{}
1,556✔
272
        for _, p := range aw.Spec.Parameters {
11,037✔
273
                parameters = append(parameters, wfv1alpha1.Parameter{
9,481✔
274
                        Name:  p.Name,
9,481✔
275
                        Value: (*wfv1alpha1.AnyString)(&p.Value),
9,481✔
276
                })
9,481✔
277
        }
9,481✔
278

279
        return wfv1alpha1.WorkflowSpec{
1,556✔
280
                WorkflowTemplateRef: &wfv1alpha1.WorkflowTemplateRef{
1,556✔
281
                        Name:         aw.Spec.WorkflowTemplateRef.Name,
1,556✔
282
                        ClusterScope: aw.Spec.WorkflowTemplateRef.ClusterScope,
1,556✔
283
                },
1,556✔
284
                Volumes: []corev1.Volume{
1,556✔
285
                        srcVolume,
1,556✔
286
                        dstVolume,
1,556✔
287
                },
1,556✔
288
                Arguments: wfv1alpha1.Arguments{
1,556✔
289
                        Parameters: parameters,
1,556✔
290
                },
1,556✔
291
        }
1,556✔
292
}
293

294
func hydrateArgoWorkflow(aw *arcv1alpha1.ArtifactWorkflow, srcSecret *corev1.Secret, dstSecret *corev1.Secret) *wfv1alpha1.Workflow {
1,554✔
295
        return &wfv1alpha1.Workflow{
1,554✔
296
                ObjectMeta: workflowObjectMeta(aw),
1,554✔
297
                Spec:       hydrateArgoWorkflowSpec(aw, srcSecret, dstSecret),
1,554✔
298
        }
1,554✔
299
}
1,554✔
300

301
func hydrateArgoCronWorkflow(aw *arcv1alpha1.ArtifactWorkflow, srcSecret *corev1.Secret, dstSecret *corev1.Secret) *wfv1alpha1.CronWorkflow {
2✔
302
        om := workflowObjectMeta(aw)
2✔
303
        wf := &wfv1alpha1.CronWorkflow{
2✔
304
                ObjectMeta: om,
2✔
305
                Spec: wfv1alpha1.CronWorkflowSpec{
2✔
306
                        WorkflowSpec:               hydrateArgoWorkflowSpec(aw, srcSecret, dstSecret),
2✔
307
                        Schedules:                  aw.Spec.Cron.Schedules,
2✔
308
                        ConcurrencyPolicy:          wfv1alpha1.ReplaceConcurrent,
2✔
309
                        StartingDeadlineSeconds:    aw.Spec.Cron.StartingDeadlineSeconds,
2✔
310
                        Timezone:                   aw.Spec.Cron.Timezone,
2✔
311
                        When:                       aw.Spec.Cron.When,
2✔
312
                        SuccessfulJobsHistoryLimit: new(int32(1)),
2✔
313
                        FailedJobsHistoryLimit:     new(int32(1)),
2✔
314
                        WorkflowMetadata:           &om,
2✔
315
                },
2✔
316
        }
2✔
317

2✔
318
        return wf
2✔
319
}
2✔
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