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

kubevirt / containerized-data-importer / #5624

08 Oct 2025 08:13PM UTC coverage: 59.095% (+0.004%) from 59.091%
#5624

push

travis-ci

web-flow
DV Recreated with Wrong Status (#3912)

* Add check during updateStatus that prevents DVs from updating
its status from a PVC that is marked for deletion

Signed-off-by: dsanatar <dsanatar@redhat.com>

* add unit test to make sure PVCs marked for deletion do not
update the status of existing DVs

Signed-off-by: dsanatar <dsanatar@redhat.com>

---------

Signed-off-by: dsanatar <dsanatar@redhat.com>

1 of 1 new or added line in 1 file covered. (100.0%)

223 existing lines in 5 files now uncovered.

17245 of 29182 relevant lines covered (59.09%)

0.65 hits per line

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

54.21
/pkg/operator/controller/callbacks.go
1
/*
2
Copyright 2018 The CDI Authors.
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 controller
18

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

24
        "github.com/go-logr/logr"
25

26
        admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
27
        appsv1 "k8s.io/api/apps/v1"
28
        corev1 "k8s.io/api/core/v1"
29
        extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
30
        "k8s.io/apimachinery/pkg/api/errors"
31
        "k8s.io/apimachinery/pkg/api/meta"
32
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34
        "k8s.io/apimachinery/pkg/labels"
35
        "k8s.io/apimachinery/pkg/runtime"
36
        "k8s.io/apimachinery/pkg/runtime/schema"
37

38
        "sigs.k8s.io/controller-runtime/pkg/client"
39
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
40

41
        cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
42
        "kubevirt.io/containerized-data-importer/pkg/common"
43
        cdicontroller "kubevirt.io/containerized-data-importer/pkg/controller"
44
        cc "kubevirt.io/containerized-data-importer/pkg/controller/common"
45
        featuregates "kubevirt.io/containerized-data-importer/pkg/feature-gates"
46
        "kubevirt.io/containerized-data-importer/pkg/operator/resources/cluster"
47
        "kubevirt.io/containerized-data-importer/pkg/operator/resources/utils"
48
        sdk "kubevirt.io/controller-lifecycle-operator-sdk/pkg/sdk"
49
        "kubevirt.io/controller-lifecycle-operator-sdk/pkg/sdk/callbacks"
50
)
51

52
func addReconcileCallbacks(r *ReconcileCDI) {
1✔
53
        r.reconciler.AddCallback(&appsv1.Deployment{}, reconcileDeleteControllerDeployment)
1✔
54
        r.reconciler.AddCallback(&appsv1.Deployment{}, reconcileUpdateControllerDeploymentSelector)
1✔
55
        r.reconciler.AddCallback(&corev1.ServiceAccount{}, reconcileSCC)
1✔
56
        r.reconciler.AddCallback(&appsv1.Deployment{}, reconcileCreatePrometheusInfra)
1✔
57
        r.reconciler.AddCallback(&appsv1.Deployment{}, reconcileRemainingRelationshipLabels)
1✔
58
        r.reconciler.AddCallback(&appsv1.Deployment{}, reconcileDeleteDeprecatedResources)
1✔
59
        r.reconciler.AddCallback(&appsv1.Deployment{}, reconcileCDICRD)
1✔
60
        r.reconciler.AddCallback(&appsv1.Deployment{}, reconcilePvcMutatingWebhook)
1✔
61
        r.reconciler.AddCallback(&extv1.CustomResourceDefinition{}, reconcileSetConfigAuthority)
1✔
62
        r.reconciler.AddCallback(&extv1.CustomResourceDefinition{}, reconcileHandleOldVersion)
1✔
63
        if r.haveRoutes {
2✔
64
                r.reconciler.AddCallback(&appsv1.Deployment{}, reconcileRoute)
1✔
65
        }
1✔
66
        if r.haveVolumeDataSourceValidator {
2✔
67
                r.reconciler.AddCallback(&appsv1.Deployment{}, reconcileVolumePopulators)
1✔
68
        }
1✔
69
}
70

71
func isControllerDeployment(d *appsv1.Deployment) bool {
1✔
72
        return d.Name == "cdi-deployment"
1✔
73
}
1✔
74

75
func reconcileUpdateControllerDeploymentSelector(args *callbacks.ReconcileCallbackArgs) error {
1✔
76
        if args.State != callbacks.ReconcileStatePostRead {
2✔
77
                return nil
1✔
78
        }
1✔
79

80
        if args.CurrentObject == nil || args.DesiredObject == nil {
1✔
UNCOV
81
                return nil
×
UNCOV
82
        }
×
83

84
        deployment := args.DesiredObject.(*appsv1.Deployment)
1✔
85

1✔
86
        if !isControllerDeployment(deployment) {
2✔
87
                return nil
1✔
88
        }
1✔
89

90
        if !reflect.DeepEqual(args.CurrentObject.(*appsv1.Deployment).Spec.Selector.MatchLabels, deployment.Spec.Selector.MatchLabels) {
2✔
91
                args.Logger.Info("Mismatching selector detected for CDI deployment, cleaning up")
1✔
92
                if err := deleteControllerDeployment(deployment, args); err != nil {
1✔
UNCOV
93
                        return err
×
UNCOV
94
                }
×
95
        }
96

97
        return nil
1✔
98
}
99

100
func reconcileDeleteControllerDeployment(args *callbacks.ReconcileCallbackArgs) error {
1✔
101
        switch args.State {
1✔
102
        case callbacks.ReconcileStatePostDelete, callbacks.ReconcileStateOperatorDelete:
1✔
103
        default:
1✔
104
                return nil
1✔
105
        }
106

107
        var deployment *appsv1.Deployment
1✔
108
        if args.DesiredObject != nil {
2✔
109
                deployment = args.DesiredObject.(*appsv1.Deployment)
1✔
110
        } else if args.CurrentObject != nil {
3✔
111
                deployment = args.CurrentObject.(*appsv1.Deployment)
1✔
112
        } else {
1✔
UNCOV
113
                args.Logger.Info("Received callback with no desired/current object")
×
UNCOV
114
                return nil
×
UNCOV
115
        }
×
116

117
        if !isControllerDeployment(deployment) {
2✔
118
                return nil
1✔
119
        }
1✔
120

121
        if err := deleteControllerDeployment(deployment, args); err != nil {
1✔
UNCOV
122
                return err
×
UNCOV
123
        }
×
124

125
        cr := args.Resource.(runtime.Object)
1✔
126
        if err := deleteWorkerResources(args.Logger, args.Client); err != nil {
1✔
127
                args.Logger.Error(err, "Error deleting worker resources")
×
UNCOV
128
                args.Recorder.Event(cr, corev1.EventTypeWarning, deleteResourceFailed, fmt.Sprintf("Failed to deleted worker resources %v", err))
×
UNCOV
129
                return err
×
UNCOV
130
        }
×
131
        args.Recorder.Event(cr, corev1.EventTypeNormal, deleteResourceSuccess, "Deleted worker resources successfully")
1✔
132

1✔
133
        return nil
1✔
134
}
135

136
func deleteControllerDeployment(deployment *appsv1.Deployment, args *callbacks.ReconcileCallbackArgs) error {
1✔
137
        args.Logger.Info("Deleting CDI deployment and all import/upload/clone pods/services")
1✔
138
        err := args.Client.Delete(context.TODO(), deployment, &client.DeleteOptions{
1✔
139
                PropagationPolicy: &[]metav1.DeletionPropagation{metav1.DeletePropagationForeground}[0],
1✔
140
        })
1✔
141
        cr := args.Resource.(runtime.Object)
1✔
142
        if err != nil && !errors.IsNotFound(err) {
1✔
143
                args.Logger.Error(err, "Error deleting cdi controller deployment")
×
UNCOV
144
                args.Recorder.Event(cr, corev1.EventTypeWarning, deleteResourceFailed, fmt.Sprintf("Failed to delete deployment %s, %v", deployment.Name, err))
×
UNCOV
145
                return err
×
UNCOV
146
        }
×
147
        args.Recorder.Event(cr, corev1.EventTypeNormal, deleteResourceSuccess, fmt.Sprintf("Deleted deployment %s successfully", deployment.Name))
1✔
148

1✔
149
        return nil
1✔
150
}
151

152
func reconcileRoute(args *callbacks.ReconcileCallbackArgs) error {
1✔
153
        if args.State != callbacks.ReconcileStatePostRead {
2✔
154
                return nil
1✔
155
        }
1✔
156

157
        deployment := args.CurrentObject.(*appsv1.Deployment)
1✔
158
        if !isControllerDeployment(deployment) || !sdk.CheckDeploymentReady(deployment) {
2✔
159
                return nil
1✔
160
        }
1✔
161

162
        cr := args.Resource.(runtime.Object)
1✔
163
        updated, err := ensureUploadProxyRouteExists(context.TODO(), args.Logger, args.Client, args.Scheme, deployment)
1✔
164
        if err != nil {
1✔
UNCOV
165
                args.Recorder.Event(cr, corev1.EventTypeWarning, createResourceFailed, fmt.Sprintf("Failed to ensure upload proxy route exists, %v", err))
×
UNCOV
166
                return err
×
UNCOV
167
        }
×
168

169
        if updated {
2✔
170
                args.Recorder.Event(cr, corev1.EventTypeNormal, createResourceSuccess, "Successfully ensured upload proxy route exists")
1✔
171
        }
1✔
172

173
        if err := updateUserRoutes(context.TODO(), args.Logger, args.Client, args.Recorder); err != nil {
1✔
UNCOV
174
                return err
×
UNCOV
175
        }
×
176

177
        return nil
1✔
178
}
179

180
func reconcileVolumePopulators(args *callbacks.ReconcileCallbackArgs) error {
1✔
181
        if args.State != callbacks.ReconcileStatePostRead {
2✔
182
                return nil
1✔
183
        }
1✔
184

185
        deployment := args.CurrentObject.(*appsv1.Deployment)
1✔
186
        if !isControllerDeployment(deployment) || !sdk.CheckDeploymentReady(deployment) {
2✔
187
                return nil
1✔
188
        }
1✔
189

190
        cr := args.Resource.(runtime.Object)
1✔
191
        updated, err := ensureVolumePopulatorsExist(context.TODO(), args.Client, args.Scheme)
1✔
192
        if err != nil {
1✔
193
                args.Recorder.Event(cr, corev1.EventTypeWarning, createResourceFailed, fmt.Sprintf("Failed to ensure volume populators exist, %v", err))
×
194
                return err
×
UNCOV
195
        }
×
196

197
        if updated {
2✔
198
                args.Recorder.Event(cr, corev1.EventTypeNormal, createResourceSuccess, "Successfully ensured volume populators exist")
1✔
199
        }
1✔
200

201
        return nil
1✔
202
}
203

204
func reconcileSCC(args *callbacks.ReconcileCallbackArgs) error {
1✔
205
        switch args.State {
1✔
206
        case callbacks.ReconcileStatePreCreate, callbacks.ReconcileStatePostRead:
1✔
207
        default:
1✔
208
                return nil
1✔
209
        }
210

211
        sa := args.DesiredObject.(*corev1.ServiceAccount)
1✔
212
        if sa.Name != common.ControllerServiceAccountName {
2✔
213
                return nil
1✔
214
        }
1✔
215

216
        cr := args.Resource.(runtime.Object)
1✔
217
        updated, err := ensureSCCExists(context.TODO(), args.Logger, args.Client, args.Namespace, common.ControllerServiceAccountName, common.CronJobServiceAccountName)
1✔
218
        if err != nil {
1✔
UNCOV
219
                args.Recorder.Event(cr, corev1.EventTypeWarning, createResourceFailed, fmt.Sprintf("Failed to ensure SecurityContextConstraint exists, %v", err))
×
UNCOV
220
                return err
×
221
        }
×
222

223
        if updated {
2✔
224
                args.Recorder.Event(cr, corev1.EventTypeNormal, createResourceSuccess, "Successfully ensured SecurityContextConstraint exists")
1✔
225
        }
1✔
226

227
        return nil
1✔
228
}
229

230
func reconcileCreatePrometheusInfra(args *callbacks.ReconcileCallbackArgs) error {
1✔
231
        if args.State != callbacks.ReconcileStatePostRead {
2✔
232
                return nil
1✔
233
        }
1✔
234

235
        deployment := args.CurrentObject.(*appsv1.Deployment)
1✔
236
        // we don't check sdk.CheckDeploymentReady(deployment) since we want Prometheus to cover NotReady state as well
1✔
237
        if !isControllerDeployment(deployment) {
2✔
238
                return nil
1✔
239
        }
1✔
240

241
        cr := args.Resource.(runtime.Object)
1✔
242
        namespace := deployment.GetNamespace()
1✔
243
        if namespace == "" {
1✔
UNCOV
244
                return fmt.Errorf("cluster scoped owner not supported")
×
UNCOV
245
        }
×
246

247
        if deployed, err := isPrometheusDeployed(args.Logger, args.Client, namespace); err != nil {
1✔
UNCOV
248
                return err
×
249
        } else if !deployed {
1✔
250
                return nil
×
251
        }
×
252
        if err := ensurePrometheusResourcesExist(context.TODO(), args.Client, args.Scheme, deployment); err != nil {
1✔
UNCOV
253
                args.Recorder.Event(cr, corev1.EventTypeWarning, createResourceFailed, fmt.Sprintf("Failed to ensure prometheus resources exists, %v", err))
×
UNCOV
254
                return err
×
UNCOV
255
        }
×
256

257
        return nil
1✔
258
}
259

260
func deleteWorkerResources(l logr.Logger, c client.Client) error {
1✔
261
        listTypes := []client.ObjectList{&corev1.PodList{}, &corev1.ServiceList{}}
1✔
262

1✔
263
        ls, err := labels.Parse(fmt.Sprintf("cdi.kubevirt.io in (%s, %s, %s)",
1✔
264
                common.ImporterPodName, common.UploadServerCDILabel, common.ClonerSourcePodName))
1✔
265
        if err != nil {
1✔
UNCOV
266
                return err
×
UNCOV
267
        }
×
268

269
        for _, lt := range listTypes {
2✔
270
                lo := &client.ListOptions{
1✔
271
                        LabelSelector: ls,
1✔
272
                }
1✔
273

1✔
274
                l.V(1).Info("Deleting worker resources", "type", reflect.TypeOf(lt).Elem().Name())
1✔
275

1✔
276
                if err := cc.BulkDeleteResources(context.TODO(), c, lt, lo); err != nil {
1✔
UNCOV
277
                        return err
×
UNCOV
278
                }
×
279
        }
280

281
        return nil
1✔
282
}
283

284
func reconcileSetConfigAuthority(args *callbacks.ReconcileCallbackArgs) error {
1✔
285
        if args.State != callbacks.ReconcileStatePostRead {
2✔
286
                return nil
1✔
287
        }
1✔
288

289
        crd := args.CurrentObject.(*extv1.CustomResourceDefinition)
1✔
290
        if crd.Name != "cdiconfigs.cdi.kubevirt.io" {
2✔
291
                return nil
1✔
292
        }
1✔
293

294
        cdi, ok := args.Resource.(*cdiv1.CDI)
1✔
295
        if !ok {
1✔
UNCOV
296
                return nil
×
UNCOV
297
        }
×
298

299
        if _, ok = cdi.Annotations[cdicontroller.AnnConfigAuthority]; ok {
2✔
300
                return nil
1✔
301
        }
1✔
302

303
        if cdi.Spec.Config == nil {
2✔
304
                cl := &cdiv1.CDIConfigList{}
1✔
305
                err := args.Client.List(context.TODO(), cl)
1✔
306
                if err != nil {
1✔
UNCOV
307
                        if meta.IsNoMatchError(err) {
×
UNCOV
308
                                return nil
×
UNCOV
309
                        }
×
310

UNCOV
311
                        return err
×
312
                }
313

314
                if len(cl.Items) != 1 {
2✔
315
                        return nil
1✔
316
                }
1✔
317

318
                cs := cl.Items[0].Spec.DeepCopy()
1✔
319
                if !reflect.DeepEqual(cs, &cdiv1.CDIConfigSpec{}) {
2✔
320
                        cdi.Spec.Config = cs
1✔
321
                }
1✔
322
        }
323

324
        if cdi.Annotations == nil {
2✔
325
                cdi.Annotations = map[string]string{}
1✔
326
        }
1✔
327
        cdi.Annotations[cdicontroller.AnnConfigAuthority] = ""
1✔
328

1✔
329
        return args.Client.Update(context.TODO(), cdi)
1✔
330
}
331

332
func getSpecVersion(version string, crd *extv1.CustomResourceDefinition) *extv1.CustomResourceDefinitionVersion {
1✔
333
        for _, v := range crd.Spec.Versions {
2✔
334
                if v.Name == version {
2✔
335
                        return &v
1✔
336
                }
1✔
337
        }
338
        return nil
1✔
339
}
340

341
func rewriteOldObjects(args *callbacks.ReconcileCallbackArgs, version string, crd *extv1.CustomResourceDefinition) error {
×
UNCOV
342
        args.Logger.Info("Rewriting old objects")
×
UNCOV
343
        kind := crd.Spec.Names.Kind
×
344
        gvk := schema.GroupVersionKind{
×
345
                Group:   crd.Spec.Group,
×
346
                Version: version,
×
347
                Kind:    kind,
×
348
        }
×
UNCOV
349
        ul := &unstructured.UnstructuredList{}
×
UNCOV
350
        ul.SetGroupVersionKind(gvk)
×
UNCOV
351
        err := args.Client.List(context.TODO(), ul, &client.ListOptions{})
×
UNCOV
352
        if err != nil {
×
UNCOV
353
                return err
×
UNCOV
354
        }
×
UNCOV
355
        for _, item := range ul.Items {
×
UNCOV
356
                nn := client.ObjectKey{Namespace: item.GetNamespace(), Name: item.GetName()}
×
UNCOV
357
                u := &unstructured.Unstructured{}
×
UNCOV
358
                u.SetGroupVersionKind(item.GetObjectKind().GroupVersionKind())
×
UNCOV
359
                err = args.Client.Get(context.TODO(), nn, u)
×
UNCOV
360
                if err != nil {
×
361
                        return err
×
362
                }
×
363
                err = args.Client.Update(context.TODO(), u)
×
364
                if err != nil {
×
365
                        return err
×
366
                }
×
367
        }
368
        return nil
×
369
}
370

371
func removeStoredVersion(args *callbacks.ReconcileCallbackArgs, desiredVersion string, crd *extv1.CustomResourceDefinition) error {
×
UNCOV
372
        args.Logger.Info("Removing stored version")
×
UNCOV
373
        crd.Status.StoredVersions = []string{desiredVersion}
×
UNCOV
374
        return args.Client.Status().Update(context.TODO(), crd)
×
UNCOV
375
}
×
376

377
// Handle upgrade from clusters that had v1alpha1 as a storage version
378
// and remove it from all CRDs managed by us
379
func reconcileHandleOldVersion(args *callbacks.ReconcileCallbackArgs) error {
1✔
380
        if args.State != callbacks.ReconcileStatePostRead {
2✔
381
                return nil
1✔
382
        }
1✔
383
        currentCrd := args.CurrentObject.(*extv1.CustomResourceDefinition)
1✔
384
        desiredCrd := args.DesiredObject.(*extv1.CustomResourceDefinition)
1✔
385
        desiredVersion := newestVersion(desiredCrd)
1✔
386

1✔
387
        if olderVersionsExist(desiredVersion, currentCrd) {
1✔
388
                restoreOlderVersions(currentCrd, desiredCrd)
×
UNCOV
389
                if !desiredIsStorage(desiredVersion, currentCrd) {
×
UNCOV
390
                        // Let kubernetes add it
×
UNCOV
391
                        return nil
×
UNCOV
392
                }
×
UNCOV
393
                if err := rewriteOldObjects(args, desiredVersion, currentCrd); err != nil {
×
UNCOV
394
                        return err
×
UNCOV
395
                }
×
UNCOV
396
                if err := removeStoredVersion(args, desiredVersion, currentCrd); err != nil {
×
UNCOV
397
                        return err
×
398
                }
×
399
        }
400
        return nil
1✔
401
}
402

403
func olderVersionsExist(desiredVersion string, crd *extv1.CustomResourceDefinition) bool {
1✔
404
        for _, version := range crd.Status.StoredVersions {
1✔
405
                if version != desiredVersion {
×
406
                        return true
×
407
                }
×
408
        }
409
        return false
1✔
410
}
411

412
func desiredIsStorage(desiredVersion string, crd *extv1.CustomResourceDefinition) bool {
×
413
        specVersion := getSpecVersion(desiredVersion, crd)
×
UNCOV
414
        return specVersion != nil && specVersion.Storage
×
415
}
×
416

417
func newestVersion(crd *extv1.CustomResourceDefinition) string {
1✔
418
        orderedVersions := []string{"v1", "v1beta1", "v1alpha1"}
1✔
419
        for _, version := range orderedVersions {
2✔
420
                specVersion := getSpecVersion(version, crd)
1✔
421
                if specVersion != nil {
2✔
422
                        return version
1✔
423
                }
1✔
424
        }
UNCOV
425
        return ""
×
426
}
427

428
// Merge both old and new versions into new CRD, so we have both in the desiredCrd object
UNCOV
429
func restoreOlderVersions(currentCrd, desiredCrd *extv1.CustomResourceDefinition) *extv1.CustomResourceDefinition {
×
UNCOV
430
        for _, version := range currentCrd.Status.StoredVersions {
×
UNCOV
431
                specVersion := getSpecVersion(version, desiredCrd)
×
UNCOV
432
                if specVersion == nil {
×
UNCOV
433
                        // Not available in desired CRD, restore from current CRD
×
UNCOV
434
                        specVersion := getSpecVersion(version, currentCrd)
×
UNCOV
435
                        // We are only allowed one storage version
×
UNCOV
436
                        // The desired CRD already has one
×
437
                        specVersion.Storage = false
×
438

×
UNCOV
439
                        desiredCrd.Spec.Versions = append(desiredCrd.Spec.Versions, *specVersion)
×
UNCOV
440
                }
×
441
        }
UNCOV
442
        return desiredCrd
×
443
}
444

445
func reconcilePvcMutatingWebhook(args *callbacks.ReconcileCallbackArgs) error {
1✔
446
        if args.State != callbacks.ReconcileStatePostRead {
2✔
447
                return nil
1✔
448
        }
1✔
449

450
        deployment, ok := args.DesiredObject.(*appsv1.Deployment)
1✔
451
        if !ok || deployment.Name != common.CDIApiServerResourceName {
2✔
452
                return nil
1✔
453
        }
1✔
454

455
        enabled, err := featuregates.IsWebhookPvcRenderingEnabled(args.Client)
1✔
456
        if err != nil {
2✔
457
                return cc.IgnoreNotFound(err)
1✔
458
        }
1✔
459

460
        whc := &admissionregistrationv1.MutatingWebhookConfiguration{}
1✔
461
        key := client.ObjectKey{Name: "cdi-api-pvc-mutate"}
1✔
462
        err = args.Client.Get(context.TODO(), key, whc)
1✔
463
        if err != nil && !errors.IsNotFound(err) {
1✔
464
                return err
×
UNCOV
465
        }
×
466

467
        exists := err == nil
1✔
468
        if !enabled {
2✔
469
                if !exists {
2✔
470
                        return nil
1✔
471
                }
1✔
472
                err = args.Client.Delete(context.TODO(), whc)
×
473
                return client.IgnoreNotFound(err)
×
474
        }
475

476
        if !exists {
×
477
                if err := initPvcMutatingWebhook(whc, args); err != nil {
×
478
                        return err
×
479
                }
×
480
                return args.Client.Create(context.TODO(), whc)
×
481
        }
482

483
        whcCopy := whc.DeepCopy()
×
484
        if err := initPvcMutatingWebhook(whc, args); err != nil {
×
485
                return err
×
486
        }
×
487
        if !reflect.DeepEqual(whc, whcCopy) {
×
488
                return args.Client.Update(context.TODO(), whc)
×
489
        }
×
490

491
        return nil
×
492
}
493

494
func initPvcMutatingWebhook(whc *admissionregistrationv1.MutatingWebhookConfiguration, args *callbacks.ReconcileCallbackArgs) error {
×
495
        path := "/pvc-mutate"
×
496
        defaultServicePort := int32(443)
×
497
        allScopes := admissionregistrationv1.AllScopes
×
498
        exactPolicy := admissionregistrationv1.Exact
×
499
        failurePolicy := admissionregistrationv1.Fail
×
500
        defaultTimeoutSeconds := int32(10)
×
501
        reinvocationNever := admissionregistrationv1.NeverReinvocationPolicy
×
502
        sideEffect := admissionregistrationv1.SideEffectClassNone
×
503
        bundle := cluster.GetAPIServerCABundle(args.Namespace, args.Client, args.Logger)
×
504

×
505
        whc.Name = "cdi-api-pvc-mutate"
×
506
        whc.Labels = map[string]string{utils.CDILabel: cluster.APIServerServiceName}
×
507
        whc.Webhooks = []admissionregistrationv1.MutatingWebhook{
×
508
                {
×
509
                        Name: "pvc-mutate.cdi.kubevirt.io",
×
510
                        Rules: []admissionregistrationv1.RuleWithOperations{{
×
511
                                Operations: []admissionregistrationv1.OperationType{
×
512
                                        admissionregistrationv1.Create,
×
513
                                },
×
514
                                Rule: admissionregistrationv1.Rule{
×
515
                                        APIGroups:   []string{corev1.SchemeGroupVersion.Group},
×
516
                                        APIVersions: []string{corev1.SchemeGroupVersion.Version},
×
517
                                        Resources:   []string{"persistentvolumeclaims"},
×
518
                                        Scope:       &allScopes,
×
519
                                },
×
520
                        }},
×
521
                        ClientConfig: admissionregistrationv1.WebhookClientConfig{
×
522
                                Service: &admissionregistrationv1.ServiceReference{
×
523
                                        Namespace: args.Namespace,
×
UNCOV
524
                                        Name:      cluster.APIServerServiceName,
×
525
                                        Path:      &path,
×
UNCOV
526
                                        Port:      &defaultServicePort,
×
UNCOV
527
                                },
×
UNCOV
528
                                CABundle: bundle,
×
UNCOV
529
                        },
×
UNCOV
530
                        FailurePolicy:     &failurePolicy,
×
UNCOV
531
                        SideEffects:       &sideEffect,
×
UNCOV
532
                        MatchPolicy:       &exactPolicy,
×
UNCOV
533
                        NamespaceSelector: &metav1.LabelSelector{},
×
UNCOV
534
                        TimeoutSeconds:    &defaultTimeoutSeconds,
×
UNCOV
535
                        AdmissionReviewVersions: []string{
×
UNCOV
536
                                "v1",
×
UNCOV
537
                        },
×
UNCOV
538
                        ObjectSelector: &metav1.LabelSelector{
×
UNCOV
539
                                MatchLabels: map[string]string{
×
UNCOV
540
                                        common.PvcApplyStorageProfileLabel: "true",
×
UNCOV
541
                                },
×
UNCOV
542
                        },
×
UNCOV
543
                        ReinvocationPolicy: &reinvocationNever,
×
UNCOV
544
                },
×
UNCOV
545
        }
×
UNCOV
546

×
UNCOV
547
        cdi, err := cc.GetActiveCDI(context.TODO(), args.Client)
×
UNCOV
548
        if err != nil {
×
UNCOV
549
                return err
×
UNCOV
550
        }
×
551

UNCOV
552
        return controllerutil.SetControllerReference(cdi, whc, args.Scheme)
×
553
}
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