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

opendefensecloud / solution-arsenal / 24239656491

10 Apr 2026 10:58AM UTC coverage: 73.887%. First build
24239656491

Pull #395

github

web-flow
Merge 3babd17a2 into ef59b87e2
Pull Request #395: feat: split of additional resources from Target and refactor rendering

476 of 581 new or added lines in 7 files covered. (81.93%)

1975 of 2673 relevant lines covered (73.89%)

26.53 hits per line

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

77.6
/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) {
25✔
44
        log := ctrl.LoggerFrom(ctx)
25✔
45

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

25✔
48
        if r.WatchNamespace != "" && req.Namespace != r.WatchNamespace {
35✔
49
                return ctrl.Result{}, nil
10✔
50
        }
10✔
51

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

NEW
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)
15✔
64
        if err != nil {
15✔
NEW
65
                log.Error(err, "invalid targetSelector in Profile")
×
NEW
66

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

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

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

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

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

15✔
98
                for i := range allBindings.Items {
25✔
99
                        if metav1.IsControlledBy(&allBindings.Items[i], profile) {
20✔
100
                                existingBindings.Items = append(existingBindings.Items, allBindings.Items[i])
10✔
101
                        }
10✔
102
                }
103
        }
104

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

10✔
111
                if !desiredTargets[rb.Spec.TargetRef.Name] {
11✔
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✔
NEW
114
                                return ctrl.Result{}, errLogAndWrap(log, err, "failed to delete ReleaseBinding")
×
NEW
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 {
30✔
124
                if _, exists := existingTargets[target.Name]; exists {
24✔
125
                        continue
9✔
126
                }
127

128
                rb := &solarv1alpha1.ReleaseBinding{
6✔
129
                        ObjectMeta: metav1.ObjectMeta{
6✔
130
                                GenerateName: fmt.Sprintf("%s-%s-", profile.Name, target.Name),
6✔
131
                                Namespace:    profile.Namespace,
6✔
132
                        },
6✔
133
                        Spec: solarv1alpha1.ReleaseBindingSpec{
6✔
134
                                TargetRef:  corev1.LocalObjectReference{Name: target.Name},
6✔
135
                                ReleaseRef: profile.Spec.ReleaseRef,
6✔
136
                        },
6✔
137
                }
6✔
138
                if err := ctrl.SetControllerReference(profile, rb, r.Scheme); err != nil {
6✔
NEW
139
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to set controller reference on ReleaseBinding")
×
NEW
140
                }
×
141

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

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

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

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

164
        return ctrl.Result{}, nil
15✔
165
}
166

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

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

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

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

×
NEW
192
                return nil
×
NEW
193
        }
×
194

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

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

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

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