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

opendefensecloud / solution-arsenal / 21165514518

20 Jan 2026 09:03AM UTC coverage: 45.33% (-25.5%) from 70.819%
21165514518

Pull #33

github

web-flow
Merge 5841e6569 into 7fc811398
Pull Request #33: Adds Discovery API to Solar Core

0 of 158 new or added lines in 2 files covered. (0.0%)

199 of 439 relevant lines covered (45.33%)

0.94 hits per line

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

0.0
/pkg/controller/discovery_controller.go
1
// Copyright 2026 BWI GmbH and Artefact Conduit contributors
2
// SPDX-License-Identifier: Apache-2.0
3

4
package controller
5

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

10
        solarv1alpha1 "go.opendefense.cloud/solar/api/solar/v1alpha1"
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/runtime"
15
        "k8s.io/client-go/tools/record"
16
        ctrl "sigs.k8s.io/controller-runtime"
17

18
        "k8s.io/client-go/kubernetes"
19
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
20

21
        "sigs.k8s.io/controller-runtime/pkg/client"
22
)
23

24
const (
25
        discoveryFinalizer = "arc.opendefense.cloud/discovery-finalizer"
26
)
27

28
// DiscoveryReconciler reconciles a Discovery object
29
type DiscoveryReconciler struct {
30
        client.Client
31
        ClientSet     kubernetes.Interface
32
        Scheme        *runtime.Scheme
33
        Recorder      record.EventRecorder
34
        WorkerImage   string
35
        WorkerCommand []string
36
        WorkerArgs    []string
37
}
38

39
//+kubebuilder:rbac:groups=arc.opendefense.cloud,resources=discoveries,verbs=get;list;watch;create;update;patch;delete
40
//+kubebuilder:rbac:groups=arc.opendefense.cloud,resources=discoveries/status,verbs=get;update;patch
41
//+kubebuilder:rbac:groups=arc.opendefense.cloud,resources=discoveries/finalizers,verbs=update
42
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;create;update;patch;delete
43
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
44
//+kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
45

46
// Reconcile moves the current state of the cluster closer to the desired state
NEW
47
func (r *DiscoveryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
×
NEW
48
        log := ctrl.LoggerFrom(ctx)
×
NEW
49
        ctrlResult := ctrl.Result{}
×
NEW
50

×
NEW
51
        // Fetch the Order instance
×
NEW
52
        res := &solarv1alpha1.Discovery{}
×
NEW
53
        if err := r.Get(ctx, req.NamespacedName, res); err != nil {
×
NEW
54
                if apierrors.IsNotFound(err) {
×
NEW
55
                        // Object not found, return. Created objects are automatically garbage collected.
×
NEW
56
                        return ctrlResult, nil
×
NEW
57
                }
×
NEW
58
                return ctrlResult, errLogAndWrap(log, err, "failed to get object")
×
59
        }
60

61
        // Handle deletion: cleanup artifact workflows, then remove finalizer
NEW
62
        if !res.DeletionTimestamp.IsZero() {
×
NEW
63
                log.V(1).Info("Discovery is being deleted")
×
NEW
64
                r.Recorder.Event(res, corev1.EventTypeWarning, "Deleting", "Discovery is being deleted, cleaning up worker")
×
NEW
65

×
NEW
66
                // Cleanup worker, if exists
×
NEW
67
                if err := r.Delete(ctx, &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: res.Namespace, Name: res.Name}}); err != nil && !apierrors.IsNotFound(err) {
×
NEW
68
                        return ctrlResult, errLogAndWrap(log, err, "pod deletion failed")
×
NEW
69
                }
×
70

71
                // Remove finalizer
NEW
72
                if slices.Contains(res.Finalizers, discoveryFinalizer) {
×
NEW
73
                        log.V(1).Info("Removing finalizer from resource")
×
NEW
74
                        res.Finalizers = slices.DeleteFunc(res.Finalizers, func(f string) bool {
×
NEW
75
                                return f == discoveryFinalizer
×
NEW
76
                        })
×
NEW
77
                        if err := r.Update(ctx, res); err != nil {
×
NEW
78
                                return ctrlResult, errLogAndWrap(log, err, "failed to remove finalizer")
×
NEW
79
                        }
×
80
                }
81
        }
82

83
        // Add finalizer if not present and not deleting
NEW
84
        if res.DeletionTimestamp.IsZero() {
×
NEW
85
                if !slices.Contains(res.Finalizers, discoveryFinalizer) {
×
NEW
86
                        log.V(1).Info("Adding finalizer to resource")
×
NEW
87
                        res.Finalizers = append(res.Finalizers, discoveryFinalizer)
×
NEW
88
                        if err := r.Update(ctx, res); err != nil {
×
NEW
89
                                return ctrlResult, errLogAndWrap(log, err, "failed to add finalizer")
×
NEW
90
                        }
×
91
                        // Return without requeue; the Update event will trigger reconciliation again
NEW
92
                        return ctrlResult, nil
×
93
                }
94
        }
95

NEW
96
        pod, err := r.ClientSet.CoreV1().Pods(res.Namespace).Get(ctx, res.Name, metav1.GetOptions{})
×
NEW
97
        if err != nil && !apierrors.IsNotFound(err) {
×
NEW
98
                r.Recorder.Eventf(res, corev1.EventTypeWarning, "Reconcile", "Failed to get pod", err)
×
NEW
99
                return ctrlResult, errLogAndWrap(log, err, "failed to get pod information")
×
NEW
100
        }
×
101

102
        // No pod yet, create it.
NEW
103
        if pod == nil {
×
NEW
104
                if err := r.createPod(ctx, res); err != nil {
×
NEW
105
                        return ctrlResult, errLogAndWrap(log, err, "failed to create pod")
×
NEW
106
                }
×
NEW
107
                return ctrlResult, nil
×
108
        }
109

110
        // Pod exists, check if it's up to date with our configuration and if it is healthy.
NEW
111
        if res.Status.PodDiscoveryVersion != res.GetResourceVersion() {
×
NEW
112
                // Recreate pod, configuration mismatch
×
NEW
113
                r.Recorder.Eventf(res, corev1.EventTypeNormal, "Reconcile", "Configuration changed. Replacing pod.")
×
NEW
114
                if err := r.Delete(ctx, &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: res.Namespace, Name: res.Name}}); err != nil && !apierrors.IsNotFound(err) {
×
NEW
115
                        r.Recorder.Eventf(res, corev1.EventTypeWarning, "DeletionFailed", "Failed to delete pod", err)
×
NEW
116
                        return ctrlResult, errLogAndWrap(log, err, "pod deletion failed")
×
NEW
117
                }
×
NEW
118
                if err := r.createPod(ctx, res); err != nil {
×
NEW
119
                        return ctrlResult, errLogAndWrap(log, err, "failed to create pod")
×
NEW
120
                }
×
NEW
121
                return ctrlResult, nil
×
122
        }
123

124
        // TODO: Check pods health
125

NEW
126
        return ctrlResult, nil
×
127
}
128

129
// createPod creates a new pod for the given discovery resource
NEW
130
func (r *DiscoveryReconciler) createPod(ctx context.Context, res *solarv1alpha1.Discovery) error {
×
NEW
131
        log := ctrl.LoggerFrom(ctx)
×
NEW
132

×
NEW
133
        // Create secret
×
NEW
134
        // TODO: Use the actual configuration file instead of a dummy one
×
NEW
135
        secret := &corev1.Secret{
×
NEW
136
                ObjectMeta: metav1.ObjectMeta{
×
NEW
137
                        Namespace: res.Namespace,
×
NEW
138
                        Name:      res.Name,
×
NEW
139
                },
×
NEW
140
                StringData: map[string]string{
×
NEW
141
                        "config.yaml": "not implemented",
×
NEW
142
                },
×
NEW
143
        }
×
NEW
144
        _, err := r.ClientSet.CoreV1().Secrets(res.Namespace).Create(ctx, secret, metav1.CreateOptions{})
×
NEW
145
        if err != nil {
×
NEW
146
                r.Recorder.Eventf(res, corev1.EventTypeWarning, "CreationFailed", "Failed to create secret", err)
×
NEW
147
                return errLogAndWrap(log, err, "failed to create secret")
×
NEW
148
        }
×
NEW
149
        r.Recorder.Eventf(res, corev1.EventTypeNormal, "PodCreated", "Worker pod created")
×
NEW
150

×
NEW
151
        // Set owner references
×
NEW
152
        if err := controllerutil.SetControllerReference(res, secret, r.Scheme); err != nil {
×
NEW
153
                return errLogAndWrap(log, err, "failed to set controller reference")
×
NEW
154
        }
×
155

156
        // Create pod
NEW
157
        var args []string
×
NEW
158
        args = append(args, r.WorkerArgs...)
×
NEW
159
        args = append(args, "--config", "/etc/worker/config.yaml")
×
NEW
160
        pod := &corev1.Pod{
×
NEW
161
                ObjectMeta: metav1.ObjectMeta{
×
NEW
162
                        Name:        res.Name,
×
NEW
163
                        Namespace:   res.Namespace,
×
NEW
164
                        Labels:      res.Labels,
×
NEW
165
                        Annotations: res.Annotations,
×
NEW
166
                },
×
NEW
167
                Spec: corev1.PodSpec{
×
NEW
168
                        Containers: []corev1.Container{
×
NEW
169
                                {
×
NEW
170
                                        Name:    "worker",
×
NEW
171
                                        Image:   r.WorkerImage,
×
NEW
172
                                        Command: r.WorkerCommand,
×
NEW
173
                                        Args:    args,
×
NEW
174
                                        VolumeMounts: []corev1.VolumeMount{
×
NEW
175
                                                {
×
NEW
176
                                                        Name:      "config",
×
NEW
177
                                                        ReadOnly:  true,
×
NEW
178
                                                        MountPath: "/etc/worker"},
×
NEW
179
                                        },
×
NEW
180
                                },
×
NEW
181
                        },
×
NEW
182
                        Volumes: []corev1.Volume{
×
NEW
183
                                {
×
NEW
184
                                        Name: "config",
×
NEW
185
                                        VolumeSource: corev1.VolumeSource{
×
NEW
186
                                                Secret: &corev1.SecretVolumeSource{
×
NEW
187
                                                        SecretName: res.Name,
×
NEW
188
                                                },
×
NEW
189
                                        },
×
NEW
190
                                },
×
NEW
191
                        },
×
NEW
192
                },
×
NEW
193
        }
×
NEW
194

×
NEW
195
        // Set owner references
×
NEW
196
        if err := controllerutil.SetControllerReference(res, pod, r.Scheme); err != nil {
×
NEW
197
                return errLogAndWrap(log, err, "failed to set controller reference")
×
NEW
198
        }
×
199

NEW
200
        _, err = r.ClientSet.CoreV1().Pods(res.Namespace).Create(ctx, pod, metav1.CreateOptions{})
×
NEW
201
        if err != nil {
×
NEW
202
                r.Recorder.Eventf(res, corev1.EventTypeWarning, "CreationFailed", "Failed to create pod", err)
×
NEW
203
                return errLogAndWrap(log, err, "failed to create pod")
×
NEW
204
        }
×
NEW
205
        r.Recorder.Eventf(res, corev1.EventTypeNormal, "PodCreated", "Worker pod created")
×
NEW
206

×
NEW
207
        // Update discovery version in status
×
NEW
208
        res.Status.PodDiscoveryVersion = res.GetResourceVersion()
×
NEW
209
        if err := r.Status().Update(ctx, res); err != nil {
×
NEW
210
                return errLogAndWrap(log, err, "failed to update order status")
×
NEW
211
        }
×
212

NEW
213
        return nil
×
214
}
215

216
// SetupWithManager sets up the controller with the Manager.
NEW
217
func (r *DiscoveryReconciler) SetupWithManager(mgr ctrl.Manager) error {
×
NEW
218
        return ctrl.NewControllerManagedBy(mgr).
×
NEW
219
                For(&solarv1alpha1.Discovery{}).
×
NEW
220
                Owns(&corev1.Pod{}).
×
NEW
221
                Owns(&corev1.Secret{}).
×
NEW
222
                Complete(r)
×
NEW
223
}
×
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