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

opendefensecloud / solution-arsenal / 24347467865

13 Apr 2026 01:59PM UTC coverage: 72.925% (-1.4%) from 74.334%
24347467865

Pull #395

github

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

548 of 712 new or added lines in 8 files covered. (76.97%)

11 existing lines in 2 files now uncovered.

2047 of 2807 relevant lines covered (72.92%)

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

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

24✔
48
        if r.WatchNamespace != "" && req.Namespace != r.WatchNamespace {
37✔
49
                return ctrl.Result{}, nil
13✔
50
        }
13✔
51

52
        // Fetch Profile
53
        profile := &solarv1alpha1.Profile{}
11✔
54
        if err := r.Get(ctx, req.NamespacedName, profile); err != nil {
11✔
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)
11✔
64
        if err != nil {
11✔
NEW
65
                log.Error(err, "invalid targetSelector in Profile")
×
NEW
66

×
NEW
67
                return ctrl.Result{}, nil
×
NEW
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✔
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{}
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✔
NEW
93
                        return ctrl.Result{}, errLogAndWrap(log, err, "failed to list ReleaseBindings")
×
NEW
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✔
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 {
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
                                GenerateName: truncateName(fmt.Sprintf("%s-%s", profile.Name, target.Name), 57) + "-",
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()
11✔
157
        profile.Status.MatchedTargets = len(targetList.Items)
11✔
158
        if profile.Status.MatchedTargets != original.Status.MatchedTargets {
17✔
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
11✔
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 {
134✔
181
        log := ctrl.LoggerFrom(ctx)
134✔
182

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

188
        profileList := &solarv1alpha1.ProfileList{}
134✔
189
        if err := r.List(ctx, profileList, client.InNamespace(target.Namespace)); err != nil {
134✔
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)
134✔
196
        var requests []reconcile.Request
134✔
197

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

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

211
        return requests
134✔
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