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

opendefensecloud / artifact-conduit / 19268551168

11 Nov 2025 02:21PM UTC coverage: 41.116% (-1.0%) from 42.149%
19268551168

push

github

jastBytes
adjust

199 of 484 relevant lines covered (41.12%)

4.09 hits per line

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

84.29
/pkg/controller/order_controller.go
1
// Copyright 2025 BWI GmbH and Artefact 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

14
        arcv1alpha1 "github.com/opendefensecloud/artifact-conduit/api/arc/v1alpha1"
15
        corev1 "k8s.io/api/core/v1"
16
        apierrors "k8s.io/apimachinery/pkg/api/errors"
17
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
        "k8s.io/apimachinery/pkg/runtime"
19
        ctrl "sigs.k8s.io/controller-runtime"
20
        "sigs.k8s.io/controller-runtime/pkg/client"
21
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
22
)
23

24
const finalizer = "arc.bwi.de/order-finalizer"
25

26
// OrderReconciler reconciles a Order object
27
type OrderReconciler struct {
28
        client.Client
29
        Scheme *runtime.Scheme
30
}
31

32
//+kubebuilder:rbac:groups=arc.bwi.de,resources=fragments,verbs=get;list;watch;create;update;patch;delete
33
//+kubebuilder:rbac:groups=arc.bwi.de,resources=orders,verbs=get;list;watch;create;update;patch;delete
34
//+kubebuilder:rbac:groups=arc.bwi.de,resources=orders/status,verbs=get;update;patch
35
//+kubebuilder:rbac:groups=arc.bwi.de,resources=orders/finalizers,verbs=update
36

37
// Reconcile moves the current state of the cluster closer to the desired state
38
func (r *OrderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
26✔
39
        log := ctrl.LoggerFrom(ctx)
26✔
40

26✔
41
        // Fetch the Order instance
26✔
42
        order := &arcv1alpha1.Order{}
26✔
43
        if err := r.Get(ctx, req.NamespacedName, order); err != nil {
28✔
44
                if apierrors.IsNotFound(err) {
4✔
45
                        // Object not found, return. Created objects are automatically garbage collected.
2✔
46
                        return ctrl.Result{}, nil
2✔
47
                }
2✔
48
                return ctrl.Result{}, err
×
49
        }
50

51
        // Handle deletion: cleanup fragments, then remove finalizer
52
        if !order.DeletionTimestamp.IsZero() {
26✔
53
                log.V(1).Info("Order is being deleted")
2✔
54
                if len(order.Status.Fragments) > 0 {
3✔
55
                        for sha, ref := range order.Status.Fragments {
3✔
56
                                frag := &arcv1alpha1.Fragment{
2✔
57
                                        ObjectMeta: metav1.ObjectMeta{
2✔
58
                                                Namespace: order.Namespace,
2✔
59
                                                Name:      ref.Name,
2✔
60
                                        },
2✔
61
                                }
2✔
62
                                _ = r.Delete(ctx, frag) // ignore not found errors
2✔
63
                                delete(order.Status.Fragments, sha)
2✔
64
                        }
2✔
65
                        if err := r.Status().Update(ctx, order); err != nil {
1✔
66
                                log.Error(err, "Failed to update fragments in Order.Status")
×
67
                                return ctrl.Result{}, err
×
68
                        }
×
69
                        log.V(1).Info("Order fragments cleaned up")
1✔
70
                        // Requeue until all fragments are gone
1✔
71
                        return ctrl.Result{Requeue: true}, nil
1✔
72
                }
73
                // All fragments are gone, remove finalizer
74
                if slices.Contains(order.Finalizers, finalizer) {
2✔
75
                        log.V(1).Info("No fragments, removing finalizer from Order")
1✔
76
                        order.Finalizers = slices.DeleteFunc(order.Finalizers, func(f string) bool {
2✔
77
                                return f == finalizer
1✔
78
                        })
1✔
79
                        if err := r.Update(ctx, order); err != nil {
1✔
80
                                log.Error(err, "Failed to remove finalizer from Order")
×
81
                                return ctrl.Result{}, err
×
82
                        }
×
83
                }
84
                return ctrl.Result{}, nil
1✔
85
        }
86

87
        // Add finalizer if not present and not deleting
88
        if order.DeletionTimestamp.IsZero() {
44✔
89
                if !slices.Contains(order.Finalizers, finalizer) {
28✔
90
                        log.V(1).Info("Adding finalizer to Order")
6✔
91
                        order.Finalizers = append(order.Finalizers, finalizer)
6✔
92
                        if err := r.Update(ctx, order); err != nil {
6✔
93
                                log.Error(err, "Failed to add finalizer to Order")
×
94
                                return ctrl.Result{}, err
×
95
                        }
×
96
                        // Return without requeue; the Update event will trigger reconciliation again
97
                        return ctrl.Result{}, nil
6✔
98
                }
99
        }
100

101
        desiredFrags := map[string]*arcv1alpha1.Fragment{}
16✔
102
        for _, artifact := range order.Spec.Artifacts {
46✔
103
                spec := runtime.RawExtension(artifact.Spec)
30✔
104

30✔
105
                // Let's collect the necessary data for the fragment from the artifact and order
30✔
106
                frag := &arcv1alpha1.Fragment{
30✔
107
                        ObjectMeta: metav1.ObjectMeta{
30✔
108
                                Namespace: order.Namespace,
30✔
109
                                Name:      "",
30✔
110
                        },
30✔
111
                        Spec: arcv1alpha1.FragmentSpec{
30✔
112
                                Type:   artifact.Type,
30✔
113
                                SrcRef: artifact.SrcRef,
30✔
114
                                DstRef: artifact.DstRef,
30✔
115
                                Spec:   spec,
30✔
116
                        },
30✔
117
                }
30✔
118
                if frag.Spec.SrcRef.Name == "" {
36✔
119
                        frag.Spec.SrcRef = order.Spec.Defaults.SrcRef
6✔
120
                }
6✔
121
                if frag.Spec.DstRef.Name == "" {
38✔
122
                        frag.Spec.DstRef = order.Spec.Defaults.DstRef
8✔
123
                }
8✔
124

125
                // Create a hash based on fragment fields for idempotency and compute the fragment name
126
                h := sha256.New()
30✔
127
                data := map[string]any{
30✔
128
                        "type": frag.Spec.Type,
30✔
129
                        "src":  frag.Spec.SrcRef.Name,
30✔
130
                        "dst":  frag.Spec.DstRef.Name,
30✔
131
                        "spec": frag.Spec.Spec.Raw,
30✔
132
                }
30✔
133
                jsonData, err := json.Marshal(data)
30✔
134
                if err != nil {
30✔
135
                        return ctrl.Result{}, fmt.Errorf("failed to marshal fragment data: %w", err)
×
136
                }
×
137
                h.Write(jsonData)
30✔
138
                sha := hex.EncodeToString(h.Sum(nil))[:16]
30✔
139
                frag.Name = fmt.Sprintf("%s-%s", order.Name, sha)
30✔
140

30✔
141
                desiredFrags[sha] = frag
30✔
142
        }
143

144
        // List missing fragments
145
        fragsToCreate := []string{}
16✔
146
        for sha := range desiredFrags {
46✔
147
                _, exists := order.Status.Fragments[sha]
30✔
148
                if exists {
47✔
149
                        continue
17✔
150
                }
151
                fragsToCreate = append(fragsToCreate, sha)
13✔
152
        }
153

154
        // Make sure status is initialized
155
        if order.Status.Fragments == nil {
22✔
156
                order.Status.Fragments = map[string]corev1.LocalObjectReference{}
6✔
157
        }
6✔
158

159
        // Find obsolete fragments
160
        fragsToDelete := []string{}
16✔
161
        for sha := range order.Status.Fragments {
34✔
162
                _, exists := desiredFrags[sha]
18✔
163
                if exists {
35✔
164
                        continue
17✔
165
                }
166
                fragsToDelete = append(fragsToDelete, sha)
1✔
167
        }
168

169
        // Create missing fragments
170
        for _, sha := range fragsToCreate {
29✔
171
                frag := desiredFrags[sha]
13✔
172

13✔
173
                // Set owner reference so Fragment is garbage-collected with the Order
13✔
174
                if err := controllerutil.SetControllerReference(order, frag, r.Scheme); err != nil {
13✔
175
                        return ctrl.Result{}, err
×
176
                }
×
177

178
                if err := r.Create(ctx, frag); err != nil {
13✔
179
                        if apierrors.IsAlreadyExists(err) {
×
180
                                // Already created by a previous reconcile — that's fine
×
181
                                continue
×
182
                        }
183
                        return ctrl.Result{}, err
×
184
                }
185

186
                // Update status
187
                order.Status.Fragments[sha] = corev1.LocalObjectReference{Name: frag.Name}
13✔
188
        }
189

190
        // Delete obsolete fragments
191
        for _, sha := range fragsToDelete {
17✔
192
                // Does not exist anymore, let's clean up!
1✔
193
                if err := r.Delete(ctx, &arcv1alpha1.Fragment{
1✔
194
                        ObjectMeta: metav1.ObjectMeta{
1✔
195
                                Namespace: order.Namespace,
1✔
196
                                Name:      order.Status.Fragments[sha].Name,
1✔
197
                        },
1✔
198
                }); err != nil {
1✔
199
                        return ctrl.Result{}, err
×
200
                }
×
201

202
                // Update status
203
                delete(order.Status.Fragments, sha)
1✔
204
        }
205

206
        // Update status
207
        if len(fragsToCreate) > 0 || len(fragsToDelete) > 0 {
24✔
208
                log.V(1).Info("Updating Order.Status")
8✔
209
                if err := r.Status().Update(ctx, order); err != nil {
8✔
210
                        return ctrl.Result{}, err
×
211
                }
×
212
        }
213

214
        return ctrl.Result{}, nil
16✔
215
}
216

217
// SetupWithManager sets up the controller with the Manager.
218
func (r *OrderReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
219
        return ctrl.NewControllerManagedBy(mgr).
1✔
220
                For(&arcv1alpha1.Order{}).
1✔
221
                Owns(&arcv1alpha1.Fragment{}).
1✔
222
                Complete(r)
1✔
223
}
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