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

k8snetworkplumbingwg / sriov-network-operator / 3751025296

pending completion
3751025296

Pull #365

github

GitHub
Merge 421284b55 into 788d76f7e
Pull Request #365: Implementation for new systemd configuration method

958 of 958 new or added lines in 18 files covered. (100.0%)

1971 of 8330 relevant lines covered (23.66%)

0.27 hits per line

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

50.39
/controllers/sriovoperatorconfig_controller.go
1
/*
2
Copyright 2021.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package controllers
18

19
import (
20
        "context"
21
        "fmt"
22
        "os"
23

24
        appsv1 "k8s.io/api/apps/v1"
25
        corev1 "k8s.io/api/core/v1"
26
        apierrors "k8s.io/apimachinery/pkg/api/errors"
27
        uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28
        "k8s.io/apimachinery/pkg/runtime"
29
        "k8s.io/apimachinery/pkg/types"
30
        kscheme "k8s.io/client-go/kubernetes/scheme"
31
        ctrl "sigs.k8s.io/controller-runtime"
32
        "sigs.k8s.io/controller-runtime/pkg/client"
33
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
34
        "sigs.k8s.io/controller-runtime/pkg/log"
35
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
36

37
        machinev1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
38

39
        sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
40
        apply "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/apply"
41
        constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
42
        render "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/render"
43
        utils "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
44
)
45

46
// SriovOperatorConfigReconciler reconciles a SriovOperatorConfig object
47
type SriovOperatorConfigReconciler struct {
48
        client.Client
49
        Scheme *runtime.Scheme
50
}
51

52
//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovoperatorconfigs,verbs=get;list;watch;create;update;patch;delete
53
//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovoperatorconfigs/status,verbs=get;update;patch
54
//+kubebuilder:rbac:groups=sriovnetwork.openshift.io,resources=sriovoperatorconfigs/finalizers,verbs=update
55

56
// Reconcile is part of the main kubernetes reconciliation loop which aims to
57
// move the current state of the cluster closer to the desired state.
58
// TODO(user): Modify the Reconcile function to compare the state specified by
59
// the SriovOperatorConfig object against the actual cluster state, and then
60
// perform operations to make the cluster state reflect the state specified by
61
// the user.
62
//
63
// For more details, check Reconcile and its Result here:
64
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile
65
func (r *SriovOperatorConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
1✔
66
        logger := log.FromContext(ctx).WithValues("sriovoperatorconfig", req.NamespacedName)
1✔
67

1✔
68
        logger.Info("Reconciling SriovOperatorConfig")
1✔
69

1✔
70
        enableAdmissionController := os.Getenv("ENABLE_ADMISSION_CONTROLLER") == "true"
1✔
71
        if !enableAdmissionController {
1✔
72
                logger.Info("SR-IOV Network Resource Injector and Operator Webhook are disabled.")
×
73
        }
×
74
        defaultConfig := &sriovnetworkv1.SriovOperatorConfig{}
1✔
75
        err := r.Get(context.TODO(), types.NamespacedName{
1✔
76
                Name: constants.DefaultConfigName, Namespace: namespace}, defaultConfig)
1✔
77
        if err != nil {
1✔
78
                if apierrors.IsNotFound(err) {
×
79
                        singleNode, err := utils.IsSingleNodeCluster(r.Client)
×
80
                        if err != nil {
×
81
                                return reconcile.Result{}, fmt.Errorf("couldn't get cluster single node status: %s", err)
×
82
                        }
×
83

84
                        // Default Config object not found, create it.
85
                        defaultConfig.SetNamespace(namespace)
×
86
                        defaultConfig.SetName(constants.DefaultConfigName)
×
87
                        defaultConfig.Spec = sriovnetworkv1.SriovOperatorConfigSpec{
×
88
                                EnableInjector:           func() *bool { b := enableAdmissionController; return &b }(),
×
89
                                EnableOperatorWebhook:    func() *bool { b := enableAdmissionController; return &b }(),
×
90
                                ConfigDaemonNodeSelector: map[string]string{},
91
                                LogLevel:                 2,
92
                                DisableDrain:             singleNode,
93
                                ConfigurationMode:        sriovnetworkv1.DaemonConfigurationMode,
94
                        }
95

96
                        err = r.Create(context.TODO(), defaultConfig)
×
97
                        if err != nil {
×
98
                                logger.Error(err, "Failed to create default Operator Config", "Namespace",
×
99
                                        namespace, "Name", constants.DefaultConfigName)
×
100
                                return reconcile.Result{}, err
×
101
                        }
×
102
                        return reconcile.Result{}, nil
×
103
                }
104
                // Error reading the object - requeue the request.
105
                return reconcile.Result{}, err
×
106
        }
107

108
        if req.Namespace != namespace {
1✔
109
                return reconcile.Result{}, nil
×
110
        }
×
111

112
        // Render and sync webhook objects
113
        if err = r.syncWebhookObjs(defaultConfig); err != nil {
1✔
114
                return reconcile.Result{}, err
×
115
        }
×
116

117
        // Sync SriovNetworkConfigDaemon objects
118
        if err = r.syncConfigDaemonSet(defaultConfig); err != nil {
1✔
119
                return reconcile.Result{}, err
×
120
        }
×
121

122
        if err = r.syncPluginDaemonSet(defaultConfig); err != nil {
1✔
123
                return reconcile.Result{}, err
×
124
        }
×
125

126
        // For Openshift we need to create the systemd files using a machine config
127
        if utils.ClusterType == utils.ClusterTypeOpenshift {
2✔
128
                if err = r.syncSystemdService(defaultConfig); err != nil {
1✔
129
                        return reconcile.Result{}, err
×
130
                }
×
131
        }
132
        return reconcile.Result{RequeueAfter: constants.ResyncPeriod}, nil
1✔
133
}
134

135
// SetupWithManager sets up the controller with the Manager.
136
func (r *SriovOperatorConfigReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
137
        return ctrl.NewControllerManagedBy(mgr).
1✔
138
                For(&sriovnetworkv1.SriovOperatorConfig{}).
1✔
139
                Owns(&appsv1.DaemonSet{}).
1✔
140
                Owns(&corev1.ConfigMap{}).
1✔
141
                Complete(r)
1✔
142
}
1✔
143

144
func (r *SriovOperatorConfigReconciler) syncPluginDaemonSet(dc *sriovnetworkv1.SriovOperatorConfig) error {
1✔
145
        logger := log.Log.WithName("syncConfigDaemonset")
1✔
146
        logger.Info("Start to sync SRIOV plugin daemonsets nodeSelector")
1✔
147
        ds := &appsv1.DaemonSet{}
1✔
148

1✔
149
        names := []string{"sriov-cni", "sriov-device-plugin"}
1✔
150

1✔
151
        if len(dc.Spec.ConfigDaemonNodeSelector) == 0 {
2✔
152
                return nil
1✔
153
        }
1✔
154
        for _, name := range names {
×
155
                err := r.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, ds)
×
156
                if err != nil {
×
157
                        if apierrors.IsNotFound(err) {
×
158
                                continue
×
159
                        }
160
                        logger.Error(err, "Couldn't get daemonset", "name", name)
×
161
                        return err
×
162
                }
163
                ds.Spec.Template.Spec.NodeSelector = dc.Spec.ConfigDaemonNodeSelector
×
164
                err = r.Client.Update(context.TODO(), ds)
×
165
                if err != nil {
×
166
                        logger.Error(err, "Couldn't update daemonset", "name", name)
×
167
                        return err
×
168
                }
×
169
        }
170

171
        return nil
×
172
}
173

174
func (r *SriovOperatorConfigReconciler) syncConfigDaemonSet(dc *sriovnetworkv1.SriovOperatorConfig) error {
1✔
175
        logger := log.Log.WithName("syncConfigDaemonset")
1✔
176
        logger.Info("Start to sync config daemonset")
1✔
177

1✔
178
        data := render.MakeRenderData()
1✔
179
        data.Data["Image"] = os.Getenv("SRIOV_NETWORK_CONFIG_DAEMON_IMAGE")
1✔
180
        data.Data["Namespace"] = namespace
1✔
181
        data.Data["SRIOVCNIImage"] = os.Getenv("SRIOV_CNI_IMAGE")
1✔
182
        data.Data["SRIOVInfiniBandCNIImage"] = os.Getenv("SRIOV_INFINIBAND_CNI_IMAGE")
1✔
183
        data.Data["ReleaseVersion"] = os.Getenv("RELEASEVERSION")
1✔
184
        data.Data["ClusterType"] = utils.ClusterType
1✔
185
        data.Data["DevMode"] = os.Getenv("DEV_MODE")
1✔
186
        data.Data["ImagePullSecrets"] = GetImagePullSecrets()
1✔
187
        if dc.Spec.ConfigurationMode == sriovnetworkv1.SystemdConfigurationMode {
1✔
188
                data.Data["UsedSystemdMode"] = true
×
189
        } else {
1✔
190
                data.Data["UsedSystemdMode"] = false
1✔
191
        }
1✔
192

193
        envCniBinPath := os.Getenv("SRIOV_CNI_BIN_PATH")
1✔
194
        if envCniBinPath == "" {
2✔
195
                data.Data["CNIBinPath"] = "/var/lib/cni/bin"
1✔
196
        } else {
1✔
197
                logger.Info("New cni bin found", "CNIBinPath", envCniBinPath)
×
198
                data.Data["CNIBinPath"] = envCniBinPath
×
199
        }
×
200
        objs, err := render.RenderDir(constants.ConfigDaemonPath, &data)
1✔
201
        if err != nil {
1✔
202
                logger.Error(err, "Fail to render config daemon manifests")
×
203
                return err
×
204
        }
×
205
        // Sync DaemonSets
206
        for _, obj := range objs {
2✔
207
                if obj.GetKind() == "DaemonSet" && len(dc.Spec.ConfigDaemonNodeSelector) > 0 {
1✔
208
                        scheme := kscheme.Scheme
×
209
                        ds := &appsv1.DaemonSet{}
×
210
                        err = scheme.Convert(obj, ds, nil)
×
211
                        if err != nil {
×
212
                                logger.Error(err, "Fail to convert to DaemonSet")
×
213
                                return err
×
214
                        }
×
215
                        ds.Spec.Template.Spec.NodeSelector = dc.Spec.ConfigDaemonNodeSelector
×
216
                        err = scheme.Convert(ds, obj, nil)
×
217
                        if err != nil {
×
218
                                logger.Error(err, "Fail to convert to Unstructured")
×
219
                                return err
×
220
                        }
×
221
                }
222
                err = r.syncK8sResource(dc, obj)
1✔
223
                if err != nil {
1✔
224
                        logger.Error(err, "Couldn't sync SR-IoV daemons objects")
×
225
                        return err
×
226
                }
×
227
        }
228
        return nil
1✔
229
}
230

231
func (r *SriovOperatorConfigReconciler) syncWebhookObjs(dc *sriovnetworkv1.SriovOperatorConfig) error {
1✔
232
        logger := log.Log.WithName("syncWebhookObjs")
1✔
233
        logger.Info("Start to sync webhook objects")
1✔
234

1✔
235
        for name, path := range webhooks {
2✔
236
                // Render Webhook manifests
1✔
237
                data := render.MakeRenderData()
1✔
238
                data.Data["Namespace"] = namespace
1✔
239
                data.Data["SRIOVMutatingWebhookName"] = name
1✔
240
                data.Data["NetworkResourcesInjectorImage"] = os.Getenv("NETWORK_RESOURCES_INJECTOR_IMAGE")
1✔
241
                data.Data["SriovNetworkWebhookImage"] = os.Getenv("SRIOV_NETWORK_WEBHOOK_IMAGE")
1✔
242
                data.Data["ReleaseVersion"] = os.Getenv("RELEASEVERSION")
1✔
243
                data.Data["ClusterType"] = utils.ClusterType
1✔
244
                data.Data["CaBundle"] = os.Getenv("WEBHOOK_CA_BUNDLE")
1✔
245
                data.Data["DevMode"] = os.Getenv("DEV_MODE")
1✔
246
                data.Data["ImagePullSecrets"] = GetImagePullSecrets()
1✔
247
                external, err := utils.IsExternalControlPlaneCluster(r.Client)
1✔
248
                if err != nil {
1✔
249
                        logger.Error(err, "Fail to get control plane topology")
×
250
                        return err
×
251
                }
×
252
                data.Data["ExternalControlPlane"] = external
1✔
253
                objs, err := render.RenderDir(path, &data)
1✔
254
                if err != nil {
1✔
255
                        logger.Error(err, "Fail to render webhook manifests")
×
256
                        return err
×
257
                }
×
258

259
                // Delete injector webhook
260
                if !*dc.Spec.EnableInjector && path == constants.InjectorWebHookPath {
2✔
261
                        for _, obj := range objs {
2✔
262
                                err = r.deleteWebhookObject(obj)
1✔
263
                                if err != nil {
1✔
264
                                        return err
×
265
                                }
×
266
                        }
267
                        logger.Info("SR-IOV Admission Controller is disabled.")
1✔
268
                        logger.Info("To enable SR-IOV Admission Controller,")
1✔
269
                        logger.Info("Set 'SriovOperatorConfig.Spec.EnableInjector' to true(bool).")
1✔
270
                        continue
1✔
271
                }
272
                // Delete operator webhook
273
                if !*dc.Spec.EnableOperatorWebhook && path == constants.OperatorWebHookPath {
2✔
274
                        for _, obj := range objs {
2✔
275
                                err = r.deleteWebhookObject(obj)
1✔
276
                                if err != nil {
1✔
277
                                        return err
×
278
                                }
×
279
                        }
280
                        logger.Info("Operator Admission Controller is disabled.")
1✔
281
                        logger.Info("To enable Operator Admission Controller,")
1✔
282
                        logger.Info("Set 'SriovOperatorConfig.Spec.EnableOperatorWebhook' to true(bool).")
1✔
283
                        continue
1✔
284
                }
285

286
                // Sync Webhook
287
                for _, obj := range objs {
2✔
288
                        err = r.syncK8sResource(dc, obj)
1✔
289
                        if err != nil {
1✔
290
                                logger.Error(err, "Couldn't sync webhook objects")
×
291
                                return err
×
292
                        }
×
293
                }
294
        }
295

296
        return nil
1✔
297
}
298

299
func (r *SriovOperatorConfigReconciler) deleteWebhookObject(obj *uns.Unstructured) error {
1✔
300
        if err := r.deleteK8sResource(obj); err != nil {
1✔
301
                return err
×
302
        }
×
303
        return nil
1✔
304
}
305

306
func (r *SriovOperatorConfigReconciler) deleteK8sResource(in *uns.Unstructured) error {
1✔
307
        if err := apply.DeleteObject(context.TODO(), r.Client, in); err != nil {
1✔
308
                return fmt.Errorf("failed to delete object %v with err: %v", in, err)
×
309
        }
×
310
        return nil
1✔
311
}
312

313
func (r *SriovOperatorConfigReconciler) syncK8sResource(cr *sriovnetworkv1.SriovOperatorConfig, in *uns.Unstructured) error {
1✔
314
        switch in.GetKind() {
1✔
315
        case "ClusterRole", "ClusterRoleBinding", "MutatingWebhookConfiguration", "ValidatingWebhookConfiguration", machineConfigCRDName:
1✔
316
        default:
1✔
317
                // set owner-reference only for namespaced objects
1✔
318
                if err := controllerutil.SetControllerReference(cr, in, r.Scheme); err != nil {
1✔
319
                        return err
×
320
                }
×
321
        }
322
        if err := apply.ApplyObject(context.TODO(), r.Client, in); err != nil {
1✔
323
                return fmt.Errorf("failed to apply object %v with err: %v", in, err)
×
324
        }
×
325
        return nil
1✔
326
}
327

328
// syncSystemdService creates the Machine Config to deploy the systemd service on openshift ONLY
329
func (r *SriovOperatorConfigReconciler) syncSystemdService(cr *sriovnetworkv1.SriovOperatorConfig) error {
1✔
330
        logger := log.Log.WithName("syncSystemdService")
1✔
331

1✔
332
        if cr.Spec.ConfigurationMode != sriovnetworkv1.SystemdConfigurationMode {
2✔
333
                obj := &machinev1.MachineConfig{}
1✔
334
                err := r.Get(context.TODO(), types.NamespacedName{Name: constants.SystemdServiceOcpMachineConfigName}, obj)
1✔
335
                if err != nil {
2✔
336
                        if apierrors.IsNotFound(err) {
2✔
337
                                return nil
1✔
338
                        }
1✔
339

340
                        logger.Error(err, "failed to get machine config for the sriov-systemd-service")
×
341
                        return err
×
342
                }
343

344
                logger.Info("Systemd service was deployed but the operator is now operating on daemonset mode, removing the machine config")
×
345
                err = r.Delete(context.TODO(), obj)
×
346
                if err != nil {
×
347
                        logger.Error(err, "failed to remove the systemd service machine config")
×
348
                        return err
×
349
                }
×
350

351
                return nil
×
352
        }
353

354
        logger.Info("Start to sync config systemd machine config for openshift")
×
355
        data := render.MakeRenderData()
×
356
        data.Data["LogLevel"] = cr.Spec.LogLevel
×
357
        objs, err := render.RenderDir(constants.SystemdServiceOcpPath, &data)
×
358
        if err != nil {
×
359
                logger.Error(err, "Fail to render config daemon manifests")
×
360
                return err
×
361
        }
×
362

363
        // Sync machine config
364
        for _, obj := range objs {
×
365
                if obj.GetKind() == machineConfigCRDName && len(cr.Spec.ConfigDaemonNodeSelector) > 0 {
×
366
                        scheme := kscheme.Scheme
×
367
                        mc := &machinev1.ControllerConfig{}
×
368
                        err = scheme.Convert(obj, mc, nil)
×
369
                        if err != nil {
×
370
                                logger.Error(err, "Fail to convert to MachineConfig")
×
371
                                return err
×
372
                        }
×
373
                        mc.Labels = cr.Spec.ConfigDaemonNodeSelector
×
374
                        err = scheme.Convert(mc, obj, nil)
×
375
                        if err != nil {
×
376
                                logger.Error(err, "Fail to convert to Unstructured")
×
377
                                return err
×
378
                        }
×
379
                }
380
                err = r.syncK8sResource(cr, obj)
×
381
                if err != nil {
×
382
                        logger.Error(err, "Couldn't sync SR-IoV daemons objects")
×
383
                        return err
×
384
                }
×
385
        }
386

387
        return nil
×
388
}
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

© 2025 Coveralls, Inc