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

opendefensecloud / solution-arsenal / 25150910053

30 Apr 2026 06:28AM UTC coverage: 72.877% (+0.9%) from 71.93%
25150910053

push

github

web-flow
fix: check errors being not found errors (#472)

## What
<!-- One sentence summary -->
Closes #374 

## Testing
This PR improves existing tests

## 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

* **Tests**
* Improved test assertions to more accurately verify resource deletion
by specifically checking for deletion conditions rather than treating
general retrieval errors as confirmation of removal. Enhanced validation
consistency across multiple test scenarios.

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

2077 of 2850 relevant lines covered (72.88%)

22.28 hits per line

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

77.78
/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
// ProfileReconciler reconciles a Profile object.
25
// It evaluates the Profile's TargetSelector against all Targets in the namespace
26
// and creates/deletes ReleaseBindings accordingly.
27
type ProfileReconciler struct {
28
        client.Client
29
        Scheme   *runtime.Scheme
30
        Recorder events.EventRecorder
31
        // WatchNamespace restricts reconciliation to this namespace.
32
        WatchNamespace string
33
}
34

35
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=profiles,verbs=get;list;watch
36
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=profiles/status,verbs=get;update;patch
37
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=releasebindings,verbs=get;list;watch;create;update;patch;delete
38
//+kubebuilder:rbac:groups=solar.opendefense.cloud,resources=targets,verbs=get;list;watch
39
//+kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
40
//+kubebuilder:rbac:groups=events.k8s.io,resources=events,verbs=create;patch
41

42
// Reconcile evaluates the Profile's TargetSelector and ensures matching ReleaseBindings exist.
43
func (r *ProfileReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
22✔
44
        log := ctrl.LoggerFrom(ctx)
22✔
45

22✔
46
        log.V(1).Info("Profile is being reconciled", "req", req)
22✔
47

22✔
48
        if r.WatchNamespace != "" && req.Namespace != r.WatchNamespace {
33✔
49
                return ctrl.Result{}, nil
11✔
50
        }
11✔
51

52
        // Fetch Profile
53
        profile := &solarv1alpha1.Profile{}
11✔
54
        if err := r.Get(ctx, req.NamespacedName, profile); err != nil {
11✔
55
                if apierrors.IsNotFound(err) {
×
56
                        return ctrl.Result{}, nil
×
57
                }
×
58

59
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to get Profile")
×
60
        }
61

62
        // Evaluate TargetSelector against all Targets
63
        selector, err := metav1.LabelSelectorAsSelector(&profile.Spec.TargetSelector)
11✔
64
        if err != nil {
11✔
65
                log.Error(err, "invalid targetSelector in Profile")
×
66

×
67
                return ctrl.Result{}, nil
×
68
        }
×
69

70
        targetList := &solarv1alpha1.TargetList{}
11✔
71
        if err := r.List(ctx, targetList,
11✔
72
                client.InNamespace(profile.Namespace),
11✔
73
                client.MatchingLabelsSelector{Selector: selector},
11✔
74
        ); err != nil {
11✔
75
                return ctrl.Result{}, errLogAndWrap(log, err, "failed to list Targets")
×
76
        }
×
77

78
        // Build set of desired ReleaseBindings (one per matching target)
79
        desiredTargets := map[string]bool{}
11✔
80
        for _, target := range targetList.Items {
22✔
81
                desiredTargets[target.Name] = true
11✔
82
        }
11✔
83

84
        // List existing ReleaseBindings owned by this Profile
85
        existingBindings := &solarv1alpha1.ReleaseBindingList{}
11✔
86
        if err := r.List(ctx, existingBindings,
11✔
87
                client.InNamespace(profile.Namespace),
11✔
88
                client.MatchingFields{"metadata.ownerReferences.name": profile.Name},
11✔
89
        ); err != nil {
22✔
90
                // Field index may not be available; fall back to listing all and filtering
11✔
91
                allBindings := &solarv1alpha1.ReleaseBindingList{}
11✔
92
                if err := r.List(ctx, allBindings, client.InNamespace(profile.Namespace)); err != nil {
11✔
93
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to list ReleaseBindings")
×
94
                }
×
95

96
                existingBindings = &solarv1alpha1.ReleaseBindingList{}
11✔
97

11✔
98
                for i := range allBindings.Items {
17✔
99
                        if metav1.IsControlledBy(&allBindings.Items[i], profile) {
12✔
100
                                existingBindings.Items = append(existingBindings.Items, allBindings.Items[i])
6✔
101
                        }
6✔
102
                }
103
        }
104

105
        // Delete ReleaseBindings for targets that no longer match
106
        existingTargets := map[string]*solarv1alpha1.ReleaseBinding{}
11✔
107
        for i := range existingBindings.Items {
17✔
108
                rb := &existingBindings.Items[i]
6✔
109
                existingTargets[rb.Spec.TargetRef.Name] = rb
6✔
110

6✔
111
                if !desiredTargets[rb.Spec.TargetRef.Name] {
7✔
112
                        log.V(1).Info("Deleting ReleaseBinding for unmatched target", "target", rb.Spec.TargetRef.Name)
1✔
113
                        if err := r.Delete(ctx, rb); err != nil && !apierrors.IsNotFound(err) {
1✔
114
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to delete ReleaseBinding")
×
115
                        }
×
116

117
                        r.Recorder.Eventf(profile, nil, corev1.EventTypeNormal, "Deleted", "Delete",
1✔
118
                                "Deleted ReleaseBinding for target %s", rb.Spec.TargetRef.Name)
1✔
119
                }
120
        }
121

122
        // Create ReleaseBindings for new matching targets
123
        for _, target := range targetList.Items {
22✔
124
                if _, exists := existingTargets[target.Name]; exists {
16✔
125
                        continue
5✔
126
                }
127

128
                rb := &solarv1alpha1.ReleaseBinding{
6✔
129
                        ObjectMeta: metav1.ObjectMeta{
6✔
130
                                // We need to truncated the name: 57 (input) + 1 (-) + 5 (appended by generated) = 63 (max chars allowed)
6✔
131
                                GenerateName: truncateName(fmt.Sprintf("%s-%s", profile.Name, target.Name), 57) + "-",
6✔
132
                                Namespace:    profile.Namespace,
6✔
133
                        },
6✔
134
                        Spec: solarv1alpha1.ReleaseBindingSpec{
6✔
135
                                TargetRef:  corev1.LocalObjectReference{Name: target.Name},
6✔
136
                                ReleaseRef: profile.Spec.ReleaseRef,
6✔
137
                        },
6✔
138
                }
6✔
139
                if err := ctrl.SetControllerReference(profile, rb, r.Scheme); err != nil {
6✔
140
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to set controller reference on ReleaseBinding")
×
141
                }
×
142

143
                if err := r.Create(ctx, rb); err != nil {
6✔
144
                        if apierrors.IsAlreadyExists(err) {
×
145
                                continue
×
146
                        }
147

148
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to create ReleaseBinding")
×
149
                }
150

151
                log.V(1).Info("Created ReleaseBinding for target", "target", target.Name)
6✔
152
                r.Recorder.Eventf(profile, nil, corev1.EventTypeNormal, "Created", "Create",
6✔
153
                        "Created ReleaseBinding for target %s", target.Name)
6✔
154
        }
155

156
        // Update status
157
        original := profile.DeepCopy()
11✔
158
        profile.Status.MatchedTargets = len(targetList.Items)
11✔
159
        if profile.Status.MatchedTargets != original.Status.MatchedTargets {
17✔
160
                if err := r.Status().Update(ctx, profile); err != nil {
6✔
161
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to update Profile status")
×
162
                }
×
163
        }
164

165
        return ctrl.Result{}, nil
11✔
166
}
167

168
// SetupWithManager sets up the controller with the Manager.
169
func (r *ProfileReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
170
        return ctrl.NewControllerManagedBy(mgr).
1✔
171
                For(&solarv1alpha1.Profile{}).
1✔
172
                Owns(&solarv1alpha1.ReleaseBinding{}).
1✔
173
                Watches(
1✔
174
                        &solarv1alpha1.Target{},
1✔
175
                        handler.EnqueueRequestsFromMapFunc(r.mapTargetToProfiles),
1✔
176
                ).
1✔
177
                Complete(r)
1✔
178
}
1✔
179

180
// mapTargetToProfiles maps a Target to all Profiles in the same namespace that might match it.
181
func (r *ProfileReconciler) mapTargetToProfiles(ctx context.Context, obj client.Object) []reconcile.Request {
136✔
182
        log := ctrl.LoggerFrom(ctx)
136✔
183

136✔
184
        target, ok := obj.(*solarv1alpha1.Target)
136✔
185
        if !ok {
136✔
186
                return nil
×
187
        }
×
188

189
        profileList := &solarv1alpha1.ProfileList{}
136✔
190
        if err := r.List(ctx, profileList, client.InNamespace(target.Namespace)); err != nil {
136✔
191
                log.Error(err, "failed to list Profiles for Target mapping")
×
192

×
193
                return nil
×
194
        }
×
195

196
        targetLabels := labels.Set(target.Labels)
136✔
197
        var requests []reconcile.Request
136✔
198

136✔
199
        for _, profile := range profileList.Items {
179✔
200
                selector, err := metav1.LabelSelectorAsSelector(&profile.Spec.TargetSelector)
43✔
201
                if err != nil {
43✔
202
                        continue
×
203
                }
204

205
                if selector.Matches(targetLabels) {
75✔
206
                        requests = append(requests, reconcile.Request{
32✔
207
                                NamespacedName: client.ObjectKeyFromObject(&profile),
32✔
208
                        })
32✔
209
                }
32✔
210
        }
211

212
        return requests
136✔
213
}
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