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

opendefensecloud / solution-arsenal / 25368188389

05 May 2026 09:21AM UTC coverage: 70.607%. Remained the same
25368188389

push

github

web-flow
fix(api): add missing listType markers to Conditions fields (#493)

## What
Add missing `+listType=map` / `+listMapKey=type` markers to `Conditions`
fields in `ReleaseStatus` and `RenderTaskStatus`.

## Why
`make codegen` generates `client-go/openapi/api_violations.report`. Two
`list_type_missing` violations were reported for our namespace — all
other status types in the package (`TargetStatus`, `ProfileStatus`,
`RegistryStatus`, etc.) already carry these markers; `ReleaseStatus` and
`RenderTaskStatus` were just missed.

## Testing
Run `make codegen` and verify no `list_type_missing` entries remain for
`go.opendefense.cloud/solar` in
`client-go/openapi/api_violations.report`.

## Checklist
- [x] ~~Tests added/updated~~ markers are validated by `make codegen`
:arrow_right: see diff in violations report
  - [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

* **Chores**
* Enhanced Kubernetes API resource schema metadata to improve list
merging and patching behavior in status definitions.
* Updated OpenAPI specifications to align with Kubernetes best practices
for list type handling.
  * Resolved API schema compliance violations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

2258 of 3198 relevant lines covered (70.61%)

19.47 hits per line

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

74.42
/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

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

21
        solarv1alpha1 "go.opendefense.cloud/solar/api/solar/v1alpha1"
22
)
23

24
// bindingTargetKey returns a stable namespace/name key for a ReleaseBinding's target reference.
25
func bindingTargetKey(rb *solarv1alpha1.ReleaseBinding) string {
14✔
26
        ns := rb.Spec.TargetNamespace
14✔
27
        if ns == "" {
26✔
28
                ns = rb.Namespace
12✔
29
        }
12✔
30

31
        return ns + "/" + rb.Spec.TargetRef.Name
14✔
32
}
33

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

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

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

62
        return false
×
63
}
64

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

71
// grantsTargetResource returns true if the ReferenceGrant includes Target in its To list.
72
func grantsTargetResource(grant *solarv1alpha1.ReferenceGrant) bool {
8✔
73
        for _, t := range grant.Spec.To {
16✔
74
                if t.Kind == "Target" && t.Group == solarGroup {
12✔
75
                        return true
4✔
76
                }
4✔
77
        }
78

79
        return false
4✔
80
}
81

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

88
// grantsComponentVersionResource returns true if the ReferenceGrant includes ComponentVersion in its To list.
89
func grantsComponentVersionResource(grant *solarv1alpha1.ReferenceGrant) bool {
16✔
90
        for _, t := range grant.Spec.To {
32✔
91
                if t.Kind == "ComponentVersion" && t.Group == solarGroup {
24✔
92
                        return true
8✔
93
                }
8✔
94
        }
95

96
        return false
8✔
97
}
98

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

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

120
// Reconcile evaluates the Profile's TargetSelector and ensures matching ReleaseBindings exist.
121
func (r *ProfileReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
32✔
122
        log := ctrl.LoggerFrom(ctx)
32✔
123

32✔
124
        log.V(1).Info("Profile is being reconciled", "req", req)
32✔
125

32✔
126
        if r.WatchNamespace != "" && req.Namespace != r.WatchNamespace {
45✔
127
                return ctrl.Result{}, nil
13✔
128
        }
13✔
129

130
        // Fetch Profile
131
        profile := &solarv1alpha1.Profile{}
19✔
132
        if err := r.Get(ctx, req.NamespacedName, profile); err != nil {
19✔
133
                if apierrors.IsNotFound(err) {
×
134
                        return ctrl.Result{}, nil
×
135
                }
×
136

137
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to get Profile")
×
138
        }
139

140
        // Evaluate TargetSelector against all Targets
141
        selector, err := metav1.LabelSelectorAsSelector(&profile.Spec.TargetSelector)
19✔
142
        if err != nil {
19✔
143
                log.Error(err, "invalid targetSelector in Profile")
×
144

×
145
                return ctrl.Result{}, nil
×
146
        }
×
147

148
        // Collect matching targets from the profile's own namespace
149
        sameNsTargets := &solarv1alpha1.TargetList{}
19✔
150
        if err := r.List(ctx, sameNsTargets,
19✔
151
                client.InNamespace(profile.Namespace),
19✔
152
                client.MatchingLabelsSelector{Selector: selector},
19✔
153
        ); err != nil {
19✔
154
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to list Targets")
×
155
        }
×
156

157
        allTargets := make([]solarv1alpha1.Target, 0, len(sameNsTargets.Items))
19✔
158
        allTargets = append(allTargets, sameNsTargets.Items...)
19✔
159

19✔
160
        // Collect cross-namespace targets via ReferenceGrants.
19✔
161
        // A ReferenceGrant in namespace B listing the profile's namespace in From and
19✔
162
        // "targets" in To allows this Profile to select Targets from namespace B.
19✔
163
        //
19✔
164
        // FIXME: listing all ReferenceGrants cluster-wide on every reconcile is a cache
19✔
165
        // scan only (no etcd round-trip, no host-cluster impact), but may become a
19✔
166
        // bottleneck at scale. Consider adding a field index on ReferenceGrants keyed by
19✔
167
        // the namespaces they grant access to so we can filter server-side.
19✔
168
        grantList := &solarv1alpha1.ReferenceGrantList{}
19✔
169
        if err := r.List(ctx, grantList); err != nil {
19✔
170
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to list ReferenceGrants")
×
171
        }
×
172

173
        for i := range grantList.Items {
21✔
174
                grant := &grantList.Items[i]
2✔
175
                if grant.Namespace == profile.Namespace {
2✔
176
                        // same-namespace targets already covered above
×
177
                        continue
×
178
                }
179
                if !grantPermitsTargetAccess(grant, profile.Namespace) {
2✔
180
                        continue
×
181
                }
182
                crossNsTargets := &solarv1alpha1.TargetList{}
2✔
183
                if err := r.List(ctx, crossNsTargets,
2✔
184
                        client.InNamespace(grant.Namespace),
2✔
185
                        client.MatchingLabelsSelector{Selector: selector},
2✔
186
                ); err != nil {
2✔
187
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to list cross-namespace Targets in "+grant.Namespace)
×
188
                }
×
189
                allTargets = append(allTargets, crossNsTargets.Items...)
2✔
190
        }
191

192
        // Build set of desired ReleaseBindings (one per matching target, keyed by namespace/name)
193
        desiredTargets := map[string]solarv1alpha1.Target{}
19✔
194
        for _, t := range allTargets {
38✔
195
                desiredTargets[targetKey(&t)] = t
19✔
196
        }
19✔
197

198
        // List existing ReleaseBindings owned by this Profile
199
        existingBindings := &solarv1alpha1.ReleaseBindingList{}
19✔
200
        if err := r.List(ctx, existingBindings,
19✔
201
                client.InNamespace(profile.Namespace),
19✔
202
                client.MatchingFields{"metadata.ownerReferences.name": profile.Name},
19✔
203
        ); err != nil {
38✔
204
                // Field index may not be available; fall back to listing all and filtering
19✔
205
                allBindings := &solarv1alpha1.ReleaseBindingList{}
19✔
206
                if err := r.List(ctx, allBindings, client.InNamespace(profile.Namespace)); err != nil {
19✔
207
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to list ReleaseBindings")
×
208
                }
×
209

210
                existingBindings = &solarv1alpha1.ReleaseBindingList{}
19✔
211

19✔
212
                for i := range allBindings.Items {
33✔
213
                        if metav1.IsControlledBy(&allBindings.Items[i], profile) {
28✔
214
                                existingBindings.Items = append(existingBindings.Items, allBindings.Items[i])
14✔
215
                        }
14✔
216
                }
217
        }
218

219
        // Delete ReleaseBindings for targets that no longer match
220
        existingByKey := map[string]*solarv1alpha1.ReleaseBinding{}
19✔
221
        for i := range existingBindings.Items {
33✔
222
                rb := &existingBindings.Items[i]
14✔
223
                key := bindingTargetKey(rb)
14✔
224
                existingByKey[key] = rb
14✔
225

14✔
226
                if _, desired := desiredTargets[key]; !desired {
17✔
227
                        log.V(1).Info("Deleting ReleaseBinding for unmatched target", "key", key)
3✔
228
                        if err := r.Delete(ctx, rb); err != nil && !apierrors.IsNotFound(err) {
3✔
229
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to delete ReleaseBinding")
×
230
                        }
×
231

232
                        r.Recorder.Eventf(profile, nil, corev1.EventTypeNormal, "Deleted", "Delete",
3✔
233
                                "Deleted ReleaseBinding for target %s", key)
3✔
234
                }
235
        }
236

237
        // Create ReleaseBindings for new matching targets
238
        for key, target := range desiredTargets {
38✔
239
                if _, exists := existingByKey[key]; exists {
30✔
240
                        continue
11✔
241
                }
242

243
                crossNs := ""
8✔
244
                if target.Namespace != profile.Namespace {
10✔
245
                        crossNs = target.Namespace
2✔
246
                }
2✔
247

248
                rb := &solarv1alpha1.ReleaseBinding{
8✔
249
                        ObjectMeta: metav1.ObjectMeta{
8✔
250
                                // We need to truncated the name: 57 (input) + 1 (-) + 5 (appended by generated) = 63 (max chars allowed)
8✔
251
                                GenerateName: truncateName(fmt.Sprintf("%s-%s", profile.Name, target.Name), 57) + "-",
8✔
252
                                Namespace:    profile.Namespace,
8✔
253
                        },
8✔
254
                        Spec: solarv1alpha1.ReleaseBindingSpec{
8✔
255
                                TargetRef:       corev1.LocalObjectReference{Name: target.Name},
8✔
256
                                TargetNamespace: crossNs,
8✔
257
                                ReleaseRef:      profile.Spec.ReleaseRef,
8✔
258
                        },
8✔
259
                }
8✔
260
                if err := ctrl.SetControllerReference(profile, rb, r.Scheme); err != nil {
8✔
261
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to set controller reference on ReleaseBinding")
×
262
                }
×
263

264
                if err := r.Create(ctx, rb); err != nil {
8✔
265
                        if apierrors.IsAlreadyExists(err) {
×
266
                                continue
×
267
                        }
268

269
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to create ReleaseBinding")
×
270
                }
271

272
                log.V(1).Info("Created ReleaseBinding for target", "key", key)
8✔
273
                r.Recorder.Eventf(profile, nil, corev1.EventTypeNormal, "Created", "Create",
8✔
274
                        "Created ReleaseBinding for target %s", key)
8✔
275
        }
276

277
        // Update status
278
        original := profile.DeepCopy()
19✔
279
        profile.Status.MatchedTargets = len(desiredTargets)
19✔
280
        if profile.Status.MatchedTargets != original.Status.MatchedTargets {
28✔
281
                if err := r.Status().Update(ctx, profile); err != nil {
9✔
282
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to update Profile status")
×
283
                }
×
284
        }
285

286
        return ctrl.Result{}, nil
19✔
287
}
288

289
// SetupWithManager sets up the controller with the Manager.
290
func (r *ProfileReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
291
        return ctrl.NewControllerManagedBy(mgr).
1✔
292
                For(&solarv1alpha1.Profile{}).
1✔
293
                Owns(&solarv1alpha1.ReleaseBinding{}).
1✔
294
                Watches(
1✔
295
                        &solarv1alpha1.Target{},
1✔
296
                        handler.EnqueueRequestsFromMapFunc(r.mapTargetToProfiles),
1✔
297
                ).
1✔
298
                Watches(
1✔
299
                        &solarv1alpha1.ReferenceGrant{},
1✔
300
                        handler.EnqueueRequestsFromMapFunc(r.mapReferenceGrantToProfiles),
1✔
301
                ).
1✔
302
                Complete(r)
1✔
303
}
1✔
304

305
// mapTargetToProfiles maps a Target to all Profiles that might match it,
306
// including Profiles in namespaces that have been granted access via ReferenceGrant.
307
func (r *ProfileReconciler) mapTargetToProfiles(ctx context.Context, obj client.Object) []reconcile.Request {
140✔
308
        log := ctrl.LoggerFrom(ctx)
140✔
309

140✔
310
        target, ok := obj.(*solarv1alpha1.Target)
140✔
311
        if !ok {
140✔
312
                return nil
×
313
        }
×
314

315
        targetLabels := labels.Set(target.Labels)
140✔
316
        var requests []reconcile.Request
140✔
317

140✔
318
        // Enqueue profiles in the target's own namespace
140✔
319
        profileList := &solarv1alpha1.ProfileList{}
140✔
320
        if err := r.List(ctx, profileList, client.InNamespace(target.Namespace)); err != nil {
140✔
321
                log.Error(err, "failed to list Profiles for Target mapping")
×
322

×
323
                return nil
×
324
        }
×
325

326
        for _, profile := range profileList.Items {
191✔
327
                selector, err := metav1.LabelSelectorAsSelector(&profile.Spec.TargetSelector)
51✔
328
                if err != nil {
51✔
329
                        continue
×
330
                }
331

332
                if selector.Matches(targetLabels) {
90✔
333
                        requests = append(requests, reconcile.Request{
39✔
334
                                NamespacedName: client.ObjectKeyFromObject(&profile),
39✔
335
                        })
39✔
336
                }
39✔
337
        }
338

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

×
345
                return requests
×
346
        }
×
347

348
        for i := range grantList.Items {
140✔
349
                grant := &grantList.Items[i]
×
350
                if !grantsTargetResource(grant) {
×
351
                        continue
×
352
                }
353
                for _, from := range grant.Spec.From {
×
354
                        if from.Namespace == target.Namespace || from.Kind != "Profile" {
×
355
                                continue
×
356
                        }
357
                        fromProfiles := &solarv1alpha1.ProfileList{}
×
358
                        if err := r.List(ctx, fromProfiles, client.InNamespace(from.Namespace)); err != nil {
×
359
                                log.Error(err, "failed to list Profiles in granted namespace", "namespace", from.Namespace)
×
360
                                continue
×
361
                        }
362
                        for _, p := range fromProfiles.Items {
×
363
                                selector, err := metav1.LabelSelectorAsSelector(&p.Spec.TargetSelector)
×
364
                                if err != nil {
×
365
                                        continue
×
366
                                }
367
                                if selector.Matches(targetLabels) {
×
368
                                        requests = append(requests, reconcile.Request{
×
369
                                                NamespacedName: client.ObjectKeyFromObject(&p),
×
370
                                        })
×
371
                                }
×
372
                        }
373
                }
374
        }
375

376
        return requests
140✔
377
}
378

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

8✔
384
        grant, ok := obj.(*solarv1alpha1.ReferenceGrant)
8✔
385
        if !ok {
8✔
386
                return nil
×
387
        }
×
388

389
        if !grantsTargetResource(grant) {
12✔
390
                return nil
4✔
391
        }
4✔
392

393
        var requests []reconcile.Request
4✔
394
        for _, from := range grant.Spec.From {
8✔
395
                if from.Kind != "Profile" || from.Group != solarGroup {
4✔
396
                        continue
×
397
                }
398
                profiles := &solarv1alpha1.ProfileList{}
4✔
399
                if err := r.List(ctx, profiles, client.InNamespace(from.Namespace)); err != nil {
4✔
400
                        log.Error(err, "failed to list Profiles for ReferenceGrant mapping", "namespace", from.Namespace)
×
401
                        continue
×
402
                }
403
                for _, p := range profiles.Items {
6✔
404
                        requests = append(requests, reconcile.Request{
2✔
405
                                NamespacedName: client.ObjectKeyFromObject(&p),
2✔
406
                        })
2✔
407
                }
2✔
408
        }
409

410
        return requests
4✔
411
}
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