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

opendefensecloud / solution-arsenal / 30623048857

31 Jul 2026 10:17AM UTC coverage: 81.918% (-0.04%) from 81.954%
30623048857

push

github

web-flow
fix: restructured renderartifact and -binding to solve a known problem (#724)

## What
Changed the structure of RenderArtifacts and -bindings to reference
Registries instead of having a copy of the secret
Closes #616 

## Why
RenderArtifacts had possibly stale secrets with the old implementation,
and secrets would need to be repinned, dependent on remaining
ArtifatBindings etc., which was convoluted and not ideal for the simple
reason that we don't want the registry secrets or other sensitive data
copied in too many ressources.

## Testing
make test

## Notes for reviewers
RenderArtifact lost some Registry-Specific Fields, and RenderArtifact as
well as ArtifatBindings got a RegistryRef field.

## Checklist
- [x] Tests added/updated
- [x] No breaking changes (or upgrade path documented above)
- [x] Readable commit history (squashed and cleaned up as desired)
- [x] AI code review considered and comments resolved


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Render artifacts now reference a Registry for credentials and
transport settings.
* Render bindings preserve Registry references and support automatic
artifact re-pinning.
* Cross-namespace Registry access is supported through explicit
authorization grants.
  * Registry changes are propagated to existing artifacts and bindings.

* **Bug Fixes**
* Improved OCI cleanup authentication, host validation, and
insecure-transport handling.
* Unauthorized cleanup now reports a clear authentication failure
status.

* **Documentation**
* Updated API references, user guidance, and configuration examples for
Registry references and access grants.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

92 of 102 new or added lines in 2 files covered. (90.2%)

7 existing lines in 3 files now uncovered.

5246 of 6404 relevant lines covered (81.92%)

29.47 hits per line

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

73.05
/pkg/controller/profile_controller.go
1
// Copyright 2026 BWI GmbH and Solution Arsenal contributors
2
// SPDX-License-Identifier: Apache-2.0
3

4
package controller
5

6
import (
7
        "context"
8
        "fmt"
9
        "slices"
10

11
        corev1 "k8s.io/api/core/v1"
12
        apierrors "k8s.io/apimachinery/pkg/api/errors"
13
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14
        "k8s.io/apimachinery/pkg/labels"
15
        "k8s.io/apimachinery/pkg/runtime"
16
        "k8s.io/apimachinery/pkg/types"
17
        "k8s.io/client-go/tools/events"
18
        ctrl "sigs.k8s.io/controller-runtime"
19
        "sigs.k8s.io/controller-runtime/pkg/client"
20
        "sigs.k8s.io/controller-runtime/pkg/handler"
21
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
22

23
        solarv1alpha1 "go.opendefense.cloud/solar/api/solar/v1alpha1"
24
)
25

26
// bindingTargetKey returns a stable namespace/name key for a ReleaseBinding's target reference.
27
func bindingTargetKey(rb *solarv1alpha1.ReleaseBinding) string {
18✔
28
        ns := rb.Spec.TargetRef.Namespace
18✔
29
        if ns == "" {
34✔
30
                ns = rb.Namespace
16✔
31
        }
16✔
32

33
        return ns + "/" + rb.Spec.TargetRef.Name
18✔
34
}
35

36
// targetKey returns the namespace/name key for a Target.
37
func targetKey(t *solarv1alpha1.Target) string {
24✔
38
        return t.Namespace + "/" + t.Name
24✔
39
}
24✔
40

41
// solarGroup is the API group for all solar resources.
42
const solarGroup = "solar.opendefense.cloud"
43

44
// grantPermits returns true if the ReferenceGrant allows a resource identified by
45
// (fromGroup, fromKind, fromNamespace) to reference a resource of (toGroup, toKind)
46
// in the grant's own namespace.
47
func grantPermits(grant *solarv1alpha1.ReferenceGrant, fromGroup, fromKind, fromNamespace, toGroup, toKind string) bool {
18✔
48
        hasFrom := false
18✔
49
        for _, f := range grant.Spec.From {
36✔
50
                if f.Namespace == fromNamespace && f.Kind == fromKind && f.Group == fromGroup {
32✔
51
                        hasFrom = true
14✔
52
                        break
14✔
53
                }
54
        }
55
        if !hasFrom {
22✔
56
                return false
4✔
57
        }
4✔
58
        for _, t := range grant.Spec.To {
28✔
59
                if t.Kind == toKind && t.Group == toGroup {
27✔
60
                        return true
13✔
61
                }
13✔
62
        }
63

64
        return false
1✔
65
}
66

67
// grantPermitsTargetAccess returns true if the ReferenceGrant allows a Profile in
68
// fromNamespace to reference Target resources in the grant's namespace.
69
func grantPermitsTargetAccess(grant *solarv1alpha1.ReferenceGrant, fromNamespace string) bool {
2✔
70
        return grantPermits(grant, solarGroup, "Profile", fromNamespace, solarGroup, "Target")
2✔
71
}
2✔
72

73
// grantsTargetResource returns true if the ReferenceGrant includes Target in its To list.
74
func grantsTargetResource(grant *solarv1alpha1.ReferenceGrant) bool {
56✔
75
        for _, t := range grant.Spec.To {
112✔
76
                if t.Kind == "Target" && t.Group == solarGroup {
104✔
77
                        return true
48✔
78
                }
48✔
79
        }
80

81
        return false
8✔
82
}
83

84
// grantPermitsComponentVersionAccess returns true if the ReferenceGrant allows a Release
85
// in fromNamespace to reference ComponentVersion resources in the grant's namespace.
86
func grantPermitsComponentVersionAccess(grant *solarv1alpha1.ReferenceGrant, fromNamespace string) bool {
6✔
87
        return grantPermits(grant, solarGroup, "Release", fromNamespace, solarGroup, "ComponentVersion")
6✔
88
}
6✔
89

90
// grantsComponentVersionResource returns true if the ReferenceGrant includes ComponentVersion in its To list.
91
func grantsComponentVersionResource(grant *solarv1alpha1.ReferenceGrant) bool {
34✔
92
        for _, t := range grant.Spec.To {
68✔
93
                if t.Kind == "ComponentVersion" && t.Group == solarGroup {
43✔
94
                        return true
9✔
95
                }
9✔
96
        }
97

98
        return false
25✔
99
}
100

101
// ProfileReconciler reconciles a Profile object.
102
// It evaluates the Profile's TargetSelector against all Targets in the namespace
103
// and creates/deletes ReleaseBindings accordingly.
104
// Cross-namespace Targets are included when a ReferenceGrant in the target's namespace
105
// grants the Profile's namespace access to "targets".
106
type ProfileReconciler struct {
107
        client.Client
108
        Scheme   *runtime.Scheme
109
        Recorder events.EventRecorder
110
        // WatchNamespace restricts reconciliation to this namespace.
111
        WatchNamespace string
112
}
113

114
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=profiles,verbs=get;list;watch;update;patch
115
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=profiles/status,verbs=get;update;patch
116
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=profiles/finalizers,verbs=update
117
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=releasebindings,verbs=get;list;watch;create;update;patch;delete
118
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=releases,verbs=get;list;watch;update;patch
119
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=releases/finalizers,verbs=update
120
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=targets,verbs=get;list;watch
121
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=referencegrants,verbs=get;list;watch
122
//+kubebuilder:rbac:groups=events.k8s.io,resources=events,verbs=create;patch
123

124
// Reconcile evaluates the Profile's TargetSelector and ensures matching ReleaseBindings exist.
125
// It also manages a deletion-protection finalizer on the referenced Release.
126
func (r *ProfileReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
90✔
127
        log := ctrl.LoggerFrom(ctx)
90✔
128

90✔
129
        log.V(1).Info("Profile is being reconciled", "req", req)
90✔
130

90✔
131
        if r.WatchNamespace != "" && req.Namespace != r.WatchNamespace {
132✔
132
                return ctrl.Result{}, nil
42✔
133
        }
42✔
134

135
        // Fetch Profile
136
        profile := &solarv1alpha1.Profile{}
48✔
137
        if err := r.Get(ctx, req.NamespacedName, profile); err != nil {
52✔
138
                if apierrors.IsNotFound(err) {
8✔
139
                        return ctrl.Result{}, nil
4✔
140
                }
4✔
141

142
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to get Profile")
×
143
        }
144

145
        // Handle deletion.
146
        if !profile.DeletionTimestamp.IsZero() {
54✔
147
                // Block until all owned ReleaseBindings are fully gone from the API before unprotecting
10✔
148
                // the Release. This ensures the Release is never deletable while bindings still reference it.
10✔
149
                // The Owns() watch in SetupWithManager re-triggers this reconcile when each binding is removed.
10✔
150
                allBindings := &solarv1alpha1.ReleaseBindingList{}
10✔
151
                if err := r.List(ctx, allBindings, client.InNamespace(profile.Namespace)); err != nil {
10✔
152
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to list owned ReleaseBindings for deletion")
×
153
                }
×
154
                ownedBindings := &solarv1alpha1.ReleaseBindingList{}
10✔
155
                for i := range allBindings.Items {
16✔
156
                        if metav1.IsControlledBy(&allBindings.Items[i], profile) {
12✔
157
                                ownedBindings.Items = append(ownedBindings.Items, allBindings.Items[i])
6✔
158
                        }
6✔
159
                }
160

161
                var ownedExist bool
10✔
162
                for i := range ownedBindings.Items {
16✔
163
                        rb := &ownedBindings.Items[i]
6✔
164
                        ownedExist = true
6✔
165
                        if rb.DeletionTimestamp.IsZero() {
8✔
166
                                if err := r.Delete(ctx, rb); err != nil && !apierrors.IsNotFound(err) {
2✔
167
                                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to delete owned ReleaseBinding during Profile deletion")
×
168
                                }
×
169
                        }
170
                }
171
                if ownedExist {
16✔
172
                        // Re-triggered by Owns() watch when the last binding is fully removed.
6✔
173
                        return ctrl.Result{}, nil
6✔
174
                }
6✔
175

176
                if profile.Spec.ReleaseRef.Name != "" {
8✔
177
                        release := &solarv1alpha1.Release{}
4✔
178
                        if err := r.Get(ctx, types.NamespacedName{Name: profile.Spec.ReleaseRef.Name, Namespace: profile.Namespace}, release); err != nil {
4✔
179
                                if !apierrors.IsNotFound(err) {
×
180
                                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to get Release for finalizer cleanup")
×
181
                                }
×
182
                        } else if err := r.removeReleaseRefFinalizerIfUnreferenced(ctx, profile, release); err != nil {
4✔
183
                                return ctrl.Result{}, err
×
184
                        }
×
185
                }
186

187
                if slices.Contains(profile.Finalizers, profileFinalizer) {
8✔
188
                        latest := &solarv1alpha1.Profile{}
4✔
189
                        if err := r.Get(ctx, req.NamespacedName, latest); err != nil {
4✔
190
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to get latest Profile for finalizer removal")
×
191
                        }
×
192
                        original := latest.DeepCopy()
4✔
193
                        latest.Finalizers = slices.DeleteFunc(latest.Finalizers, func(s string) bool { return s == profileFinalizer })
8✔
194
                        if err := r.Patch(ctx, latest, client.MergeFrom(original)); err != nil {
4✔
195
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to remove finalizer from Profile")
×
196
                        }
×
197
                }
198

199
                return ctrl.Result{}, nil
4✔
200
        }
201

202
        // Ensure self-finalizer exists.
203
        if !slices.Contains(profile.Finalizers, profileFinalizer) {
46✔
204
                latest := &solarv1alpha1.Profile{}
12✔
205
                if err := r.Get(ctx, req.NamespacedName, latest); err != nil {
12✔
206
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to get latest Profile for finalizer addition")
×
207
                }
×
208
                if !slices.Contains(latest.Finalizers, profileFinalizer) {
24✔
209
                        original := latest.DeepCopy()
12✔
210
                        latest.Finalizers = append(latest.Finalizers, profileFinalizer)
12✔
211
                        if err := r.Patch(ctx, latest, client.MergeFrom(original)); err != nil {
12✔
212
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to add finalizer to Profile")
×
213
                        }
×
214
                }
215
        }
216

217
        // Protect the referenced Release from deletion.
218
        if profile.Spec.ReleaseRef.Name != "" {
68✔
219
                release := &solarv1alpha1.Release{}
34✔
220
                if err := r.Get(ctx, types.NamespacedName{Name: profile.Spec.ReleaseRef.Name, Namespace: profile.Namespace}, release); err != nil {
53✔
221
                        if !apierrors.IsNotFound(err) {
19✔
222
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to get Release for protection finalizer")
×
223
                        }
×
224
                } else if !slices.Contains(release.Finalizers, releaseRefFinalizer) {
22✔
225
                        latest := release.DeepCopy()
7✔
226
                        latest.Finalizers = append(latest.Finalizers, releaseRefFinalizer)
7✔
227
                        if err := r.Patch(ctx, latest, client.MergeFromWithOptions(release, client.MergeFromWithOptimisticLock{})); err != nil {
9✔
228
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to add protection finalizer to Release")
2✔
229
                        }
2✔
230
                }
231
        }
232

233
        // Evaluate TargetSelector against all Targets
234
        selector, err := metav1.LabelSelectorAsSelector(&profile.Spec.TargetSelector)
32✔
235
        if err != nil {
32✔
236
                log.Error(err, "invalid targetSelector in Profile")
×
237

×
238
                return ctrl.Result{}, nil
×
239
        }
×
240

241
        // Collect matching targets from the profile's own namespace
242
        sameNsTargets := &solarv1alpha1.TargetList{}
32✔
243
        if err := r.List(ctx, sameNsTargets,
32✔
244
                client.InNamespace(profile.Namespace),
32✔
245
                client.MatchingLabelsSelector{Selector: selector},
32✔
246
        ); err != nil {
32✔
247
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to list Targets")
×
248
        }
×
249

250
        allTargets := make([]solarv1alpha1.Target, 0, len(sameNsTargets.Items))
32✔
251
        allTargets = append(allTargets, sameNsTargets.Items...)
32✔
252

32✔
253
        // Collect cross-namespace targets via ReferenceGrants.
32✔
254
        // A ReferenceGrant in namespace B listing the profile's namespace in From and
32✔
255
        // "targets" in To allows this Profile to select Targets from namespace B.
32✔
256
        //
32✔
257
        // FIXME: listing all ReferenceGrants cluster-wide on every reconcile is a cache
32✔
258
        // scan only (no etcd round-trip, no host-cluster impact), but may become a
32✔
259
        // bottleneck at scale. Consider adding a field index on ReferenceGrants keyed by
32✔
260
        // the namespaces they grant access to so we can filter server-side.
32✔
261
        grantList := &solarv1alpha1.ReferenceGrantList{}
32✔
262
        if err := r.List(ctx, grantList); err != nil {
32✔
263
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to list ReferenceGrants")
×
264
        }
×
265

266
        for i := range grantList.Items {
34✔
267
                grant := &grantList.Items[i]
2✔
268
                if grant.Namespace == profile.Namespace {
2✔
269
                        // same-namespace targets already covered above
×
270
                        continue
×
271
                }
272
                if !grantPermitsTargetAccess(grant, profile.Namespace) {
2✔
UNCOV
273
                        continue
×
274
                }
275
                crossNsTargets := &solarv1alpha1.TargetList{}
2✔
276
                if err := r.List(ctx, crossNsTargets,
2✔
277
                        client.InNamespace(grant.Namespace),
2✔
278
                        client.MatchingLabelsSelector{Selector: selector},
2✔
279
                ); err != nil {
2✔
280
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to list cross-namespace Targets in "+grant.Namespace)
×
281
                }
×
282
                allTargets = append(allTargets, crossNsTargets.Items...)
2✔
283
        }
284

285
        // Build set of desired ReleaseBindings (one per matching target, keyed by namespace/name)
286
        desiredTargets := map[string]solarv1alpha1.Target{}
32✔
287
        for _, t := range allTargets {
56✔
288
                desiredTargets[targetKey(&t)] = t
24✔
289
        }
24✔
290

291
        // List existing ReleaseBindings owned by this Profile
292
        allBindings := &solarv1alpha1.ReleaseBindingList{}
32✔
293
        if err := r.List(ctx, allBindings, client.InNamespace(profile.Namespace)); err != nil {
32✔
294
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to list ReleaseBindings")
×
295
        }
×
296
        existingBindings := &solarv1alpha1.ReleaseBindingList{}
32✔
297
        for i := range allBindings.Items {
50✔
298
                if metav1.IsControlledBy(&allBindings.Items[i], profile) {
36✔
299
                        existingBindings.Items = append(existingBindings.Items, allBindings.Items[i])
18✔
300
                }
18✔
301
        }
302

303
        // Delete ReleaseBindings for targets that no longer match
304
        existingByKey := map[string]*solarv1alpha1.ReleaseBinding{}
32✔
305
        for i := range existingBindings.Items {
50✔
306
                rb := &existingBindings.Items[i]
18✔
307
                key := bindingTargetKey(rb)
18✔
308
                existingByKey[key] = rb
18✔
309

18✔
310
                if _, desired := desiredTargets[key]; !desired {
22✔
311
                        log.V(1).Info("Deleting ReleaseBinding for unmatched target", "key", key)
4✔
312
                        if err := r.Delete(ctx, rb); err != nil && !apierrors.IsNotFound(err) {
4✔
313
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to delete ReleaseBinding")
×
314
                        }
×
315

316
                        r.Recorder.Eventf(profile, nil, corev1.EventTypeNormal, "Deleted", "Delete",
4✔
317
                                "Deleted ReleaseBinding for target %s", key)
4✔
318
                }
319
        }
320

321
        // Create ReleaseBindings for new matching targets
322
        for key, target := range desiredTargets {
56✔
323
                if _, exists := existingByKey[key]; exists {
38✔
324
                        continue
14✔
325
                }
326

327
                crossNs := ""
10✔
328
                if target.Namespace != profile.Namespace {
12✔
329
                        crossNs = target.Namespace
2✔
330
                }
2✔
331

332
                rb := &solarv1alpha1.ReleaseBinding{
10✔
333
                        ObjectMeta: metav1.ObjectMeta{
10✔
334
                                // We need to truncated the name: 57 (input) + 1 (-) + 5 (appended by generated) = 63 (max chars allowed)
10✔
335
                                GenerateName: truncateName(fmt.Sprintf("%s-%s", profile.Name, target.Name), 57) + "-",
10✔
336
                                Namespace:    profile.Namespace,
10✔
337
                        },
10✔
338
                        Spec: solarv1alpha1.ReleaseBindingSpec{
10✔
339
                                TargetRef:  solarv1alpha1.ObjectReference{Name: target.Name, Namespace: crossNs},
10✔
340
                                ReleaseRef: profile.Spec.ReleaseRef,
10✔
341
                        },
10✔
342
                }
10✔
343
                if err := ctrl.SetControllerReference(profile, rb, r.Scheme); err != nil {
10✔
344
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to set controller reference on ReleaseBinding")
×
345
                }
×
346

347
                if err := r.Create(ctx, rb); err != nil {
10✔
348
                        if apierrors.IsAlreadyExists(err) {
×
349
                                continue
×
350
                        }
351

352
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to create ReleaseBinding")
×
353
                }
354

355
                log.V(1).Info("Created ReleaseBinding for target", "key", key)
10✔
356
                r.Recorder.Eventf(profile, nil, corev1.EventTypeNormal, "Created", "Create",
10✔
357
                        "Created ReleaseBinding for target %s", key)
10✔
358
        }
359

360
        // Update status
361
        original := profile.DeepCopy()
32✔
362
        profile.Status.MatchedTargets = len(desiredTargets)
32✔
363
        if profile.Status.MatchedTargets != original.Status.MatchedTargets {
47✔
364
                if err := r.Status().Update(ctx, profile); err != nil {
23✔
365
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to update Profile status")
8✔
366
                }
8✔
367
        }
368

369
        return ctrl.Result{}, nil
24✔
370
}
371

372
// SetupWithManager sets up the controller with the Manager.
373
func (r *ProfileReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
374
        return ctrl.NewControllerManagedBy(mgr).
1✔
375
                For(&solarv1alpha1.Profile{}).
1✔
376
                Owns(&solarv1alpha1.ReleaseBinding{}).
1✔
377
                Watches(
1✔
378
                        &solarv1alpha1.Target{},
1✔
379
                        handler.EnqueueRequestsFromMapFunc(r.mapTargetToProfiles),
1✔
380
                ).
1✔
381
                Watches(
1✔
382
                        &solarv1alpha1.ReferenceGrant{},
1✔
383
                        handler.EnqueueRequestsFromMapFunc(r.mapReferenceGrantToProfiles),
1✔
384
                ).
1✔
385
                Complete(r)
1✔
386
}
1✔
387

388
// mapTargetToProfiles maps a Target to all Profiles that might match it,
389
// including Profiles in namespaces that have been granted access via ReferenceGrant.
390
func (r *ProfileReconciler) mapTargetToProfiles(ctx context.Context, obj client.Object) []reconcile.Request {
447✔
391
        log := ctrl.LoggerFrom(ctx)
447✔
392

447✔
393
        target, ok := obj.(*solarv1alpha1.Target)
447✔
394
        if !ok {
447✔
395
                return nil
×
396
        }
×
397

398
        targetLabels := labels.Set(target.Labels)
447✔
399
        var requests []reconcile.Request
447✔
400

447✔
401
        // Enqueue profiles in the target's own namespace
447✔
402
        profileList := &solarv1alpha1.ProfileList{}
447✔
403
        if err := r.List(ctx, profileList, client.InNamespace(target.Namespace)); err != nil {
447✔
404
                log.Error(err, "failed to list Profiles for Target mapping")
×
405

×
406
                return nil
×
407
        }
×
408

409
        for _, profile := range profileList.Items {
502✔
410
                selector, err := metav1.LabelSelectorAsSelector(&profile.Spec.TargetSelector)
55✔
411
                if err != nil {
55✔
412
                        continue
×
413
                }
414

415
                if selector.Matches(targetLabels) {
99✔
416
                        requests = append(requests, reconcile.Request{
44✔
417
                                NamespacedName: client.ObjectKeyFromObject(&profile),
44✔
418
                        })
44✔
419
                }
44✔
420
        }
421

422
        // Enqueue profiles in namespaces that have been granted access to targets in
423
        // this target's namespace via a ReferenceGrant.
424
        grantList := &solarv1alpha1.ReferenceGrantList{}
447✔
425
        if err := r.List(ctx, grantList, client.InNamespace(target.Namespace)); err != nil {
447✔
426
                log.Error(err, "failed to list ReferenceGrants for cross-namespace Target mapping")
×
427

×
428
                return requests
×
429
        }
×
430

431
        for i := range grantList.Items {
487✔
432
                grant := &grantList.Items[i]
40✔
433
                if !grantsTargetResource(grant) {
40✔
434
                        continue
×
435
                }
436
                for _, from := range grant.Spec.From {
80✔
437
                        if from.Namespace == target.Namespace || from.Kind != "Profile" {
80✔
438
                                continue
40✔
439
                        }
440
                        fromProfiles := &solarv1alpha1.ProfileList{}
×
441
                        if err := r.List(ctx, fromProfiles, client.InNamespace(from.Namespace)); err != nil {
×
442
                                log.Error(err, "failed to list Profiles in granted namespace", "namespace", from.Namespace)
×
443
                                continue
×
444
                        }
445
                        for _, p := range fromProfiles.Items {
×
446
                                selector, err := metav1.LabelSelectorAsSelector(&p.Spec.TargetSelector)
×
447
                                if err != nil {
×
448
                                        continue
×
449
                                }
450
                                if selector.Matches(targetLabels) {
×
451
                                        requests = append(requests, reconcile.Request{
×
452
                                                NamespacedName: client.ObjectKeyFromObject(&p),
×
453
                                        })
×
454
                                }
×
455
                        }
456
                }
457
        }
458

459
        return requests
447✔
460
}
461

462
// mapReferenceGrantToProfiles enqueues all Profiles in the namespaces listed in
463
// a ReferenceGrant's From field, allowing them to re-evaluate cross-namespace matches.
464
func (r *ProfileReconciler) mapReferenceGrantToProfiles(ctx context.Context, obj client.Object) []reconcile.Request {
16✔
465
        log := ctrl.LoggerFrom(ctx)
16✔
466

16✔
467
        grant, ok := obj.(*solarv1alpha1.ReferenceGrant)
16✔
468
        if !ok {
16✔
469
                return nil
×
470
        }
×
471

472
        if !grantsTargetResource(grant) {
24✔
473
                return nil
8✔
474
        }
8✔
475

476
        var requests []reconcile.Request
8✔
477
        for _, from := range grant.Spec.From {
16✔
478
                if from.Kind != "Profile" || from.Group != solarGroup {
12✔
479
                        continue
4✔
480
                }
481
                profiles := &solarv1alpha1.ProfileList{}
4✔
482
                if err := r.List(ctx, profiles, client.InNamespace(from.Namespace)); err != nil {
4✔
483
                        log.Error(err, "failed to list Profiles for ReferenceGrant mapping", "namespace", from.Namespace)
×
484
                        continue
×
485
                }
486
                for _, p := range profiles.Items {
5✔
487
                        requests = append(requests, reconcile.Request{
1✔
488
                                NamespacedName: client.ObjectKeyFromObject(&p),
1✔
489
                        })
1✔
490
                }
1✔
491
        }
492

493
        return requests
8✔
494
}
495

496
// removeReleaseRefFinalizerIfUnreferenced removes releaseRefFinalizer from release when no active
497
// Profile or ReleaseBinding (excluding the deleting Profile and its owned ReleaseBindings) still
498
// references it.
499
func (r *ProfileReconciler) removeReleaseRefFinalizerIfUnreferenced(ctx context.Context, deletingProfile *solarv1alpha1.Profile, release *solarv1alpha1.Release) error {
4✔
500
        if !slices.Contains(release.Finalizers, releaseRefFinalizer) {
4✔
501
                return nil
×
502
        }
×
503

504
        // Count Profiles (excluding self) referencing this Release.
505
        profileList := &solarv1alpha1.ProfileList{}
4✔
506
        if err := r.List(ctx, profileList,
4✔
507
                client.InNamespace(release.Namespace),
4✔
508
                client.MatchingFields{indexProfileByReleaseName: release.Name},
4✔
509
        ); err != nil {
4✔
510
                return errLogAndWrap(ctrl.LoggerFrom(ctx), err, "failed to list Profiles for Release finalizer check")
×
511
        }
×
512

513
        for _, p := range profileList.Items {
8✔
514
                if p.Name == deletingProfile.Name {
8✔
515
                        continue
4✔
516
                }
517
                if !p.DeletionTimestamp.IsZero() {
×
518
                        continue
×
519
                }
520

521
                return nil
×
522
        }
523

524
        // Count ReleaseBindings (excluding those owned by the deleting Profile) referencing this Release.
525
        bindingList := &solarv1alpha1.ReleaseBindingList{}
4✔
526
        if err := r.List(ctx, bindingList,
4✔
527
                client.InNamespace(release.Namespace),
4✔
528
                client.MatchingFields{indexReleaseBindingReleaseName: release.Name},
4✔
529
        ); err != nil {
4✔
530
                return errLogAndWrap(ctrl.LoggerFrom(ctx), err, "failed to list ReleaseBindings for Release finalizer check")
×
531
        }
×
532

533
        for _, rb := range bindingList.Items {
4✔
534
                if metav1.IsControlledBy(&rb, deletingProfile) {
×
535
                        continue // owned by the Profile being deleted; K8s GC will remove these
×
536
                }
537
                if !rb.DeletionTimestamp.IsZero() {
×
538
                        continue
×
539
                }
540

541
                // Check if this binding's owner Profile is also being deleted (concurrent deletion).
542
                ownerRef := metav1.GetControllerOf(&rb)
×
543
                if ownerRef != nil && ownerRef.Kind == "Profile" && ownerRef.APIVersion == solarv1alpha1.SchemeGroupVersion.String() {
×
544
                        ownerProfile := &solarv1alpha1.Profile{}
×
545
                        err := r.Get(ctx, types.NamespacedName{Name: ownerRef.Name, Namespace: rb.Namespace}, ownerProfile)
×
546
                        if apierrors.IsNotFound(err) || (err == nil && !ownerProfile.DeletionTimestamp.IsZero()) {
×
547
                                continue // owner Profile is gone or being deleted, this binding will be GC'd
×
548
                        }
549
                        if err != nil {
×
550
                                return errLogAndWrap(ctrl.LoggerFrom(ctx), err, "failed to check owner Profile for concurrent deletion")
×
551
                        }
×
552
                }
553

554
                return nil
×
555
        }
556

557
        freshRelease := &solarv1alpha1.Release{}
4✔
558
        if err := r.Get(ctx, client.ObjectKeyFromObject(release), freshRelease); err != nil {
4✔
559
                if apierrors.IsNotFound(err) {
×
560
                        return nil
×
561
                }
×
562

563
                return errLogAndWrap(ctrl.LoggerFrom(ctx), err, "failed to get latest Release for finalizer removal")
×
564
        }
565
        original := freshRelease.DeepCopy()
4✔
566
        freshRelease.Finalizers = slices.DeleteFunc(freshRelease.Finalizers, func(s string) bool { return s == releaseRefFinalizer })
11✔
567
        if err := r.Patch(ctx, freshRelease, client.MergeFromWithOptions(original, client.MergeFromWithOptimisticLock{})); err != nil {
4✔
568
                return errLogAndWrap(ctrl.LoggerFrom(ctx), err, "failed to remove protection finalizer from Release")
×
569
        }
×
570

571
        ctrl.LoggerFrom(ctx).V(1).Info("Removed protection finalizer from Release", "release", release.Name)
4✔
572

4✔
573
        return nil
4✔
574
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc