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

kubevirt / containerized-data-importer / #4879

16 Aug 2024 03:54AM UTC coverage: 59.187% (-0.08%) from 59.27%
#4879

push

travis-ci

web-flow
Setup ginkgo cli build properly to avoid double dep (#3378)

* Setup ginkgo cli build properly to avoid double dep

Today we have the ginkgo CLI brought into the builder and
also to the project itself. This results in
```
 Ginkgo detected a version mismatch between the Ginkgo CLI and the version of Ginkgo imported by your packages:
  Ginkgo CLI Version:
    2.12.0
  Mismatched package versions found:
    2.17.1 used by tests
```
This commit provides the necessary build adaptations to get rid of the
builder ginkgo CLI dependency.

Signed-off-by: Alex Kalenyuk <akalenyu@redhat.com>

* update builder to latest

https://github.com/kubevirt/containerized-data-importer/pull/3379

Signed-off-by: Alex Kalenyuk <akalenyu@redhat.com>

---------

Signed-off-by: Alex Kalenyuk <akalenyu@redhat.com>

16609 of 28062 relevant lines covered (59.19%)

0.65 hits per line

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

62.44
/pkg/controller/populators/populator-base.go
1
/*
2
Copyright 2023 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 populators
18

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

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

25
        corev1 "k8s.io/api/core/v1"
26
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
27
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
        "k8s.io/apimachinery/pkg/runtime"
29
        "k8s.io/apimachinery/pkg/runtime/schema"
30
        "k8s.io/apimachinery/pkg/types"
31
        "k8s.io/client-go/tools/record"
32

33
        "sigs.k8s.io/controller-runtime/pkg/client"
34
        "sigs.k8s.io/controller-runtime/pkg/controller"
35
        "sigs.k8s.io/controller-runtime/pkg/handler"
36
        "sigs.k8s.io/controller-runtime/pkg/manager"
37
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
38
        "sigs.k8s.io/controller-runtime/pkg/source"
39

40
        "kubevirt.io/containerized-data-importer/pkg/common"
41
        cc "kubevirt.io/containerized-data-importer/pkg/controller/common"
42
        featuregates "kubevirt.io/containerized-data-importer/pkg/feature-gates"
43
        "kubevirt.io/containerized-data-importer/pkg/util"
44
)
45

46
type pvcModifierFunc func(pvc *corev1.PersistentVolumeClaim, source client.Object)
47

48
// ReconcilerBase members
49
type ReconcilerBase struct {
50
        client          client.Client
51
        recorder        record.EventRecorder
52
        scheme          *runtime.Scheme
53
        log             logr.Logger
54
        featureGates    featuregates.FeatureGates
55
        sourceKind      string
56
        installerLabels map[string]string
57
}
58

59
const (
60
        dataSourceRefField = "spec.dataSourceRef"
61

62
        uidField = "metadata.uid"
63
)
64

65
// Interface to store populator-specific methods
66
type populatorController interface {
67
        // Returns the specific populator CR
68
        getPopulationSource(pvc *corev1.PersistentVolumeClaim) (client.Object, error)
69
        // Prepares the PVC' to be populated according to the population source
70
        updatePVCForPopulation(pvc *corev1.PersistentVolumeClaim, source client.Object)
71
        // Reconciles the target PVC with populator-specific logic
72
        reconcileTargetPVC(pvc, pvcPrime *corev1.PersistentVolumeClaim) (reconcile.Result, error)
73
}
74

75
type indexArgs struct {
76
        obj          client.Object
77
        field        string
78
        extractValue client.IndexerFunc
79
}
80

81
func getIndexArgs() []indexArgs {
1✔
82
        return []indexArgs{
1✔
83
                {
1✔
84
                        obj:   &corev1.PersistentVolumeClaim{},
1✔
85
                        field: dataSourceRefField,
1✔
86
                        extractValue: func(obj client.Object) []string {
1✔
87
                                pvc := obj.(*corev1.PersistentVolumeClaim)
×
88
                                dataSourceRef := pvc.Spec.DataSourceRef
×
89
                                if isDataSourceRefValid(dataSourceRef) {
×
90
                                        namespace := getPopulationSourceNamespace(pvc)
×
91
                                        apiGroup := *dataSourceRef.APIGroup
×
92
                                        return []string{getPopulatorIndexKey(apiGroup, dataSourceRef.Kind, namespace, dataSourceRef.Name)}
×
93
                                }
×
94
                                return nil
×
95
                        },
96
                },
97
                {
98
                        obj:   &corev1.PersistentVolumeClaim{},
99
                        field: uidField,
100
                        extractValue: func(obj client.Object) []string {
×
101
                                return []string{string(obj.GetUID())}
×
102
                        },
×
103
                },
104
        }
105
}
106

107
// CreateCommonPopulatorIndexes creates indexes used by all populators
108
func CreateCommonPopulatorIndexes(mgr manager.Manager) error {
×
109
        for _, ia := range getIndexArgs() {
×
110
                if err := mgr.GetFieldIndexer().IndexField(context.TODO(), ia.obj, ia.field, ia.extractValue); err != nil {
×
111
                        return err
×
112
                }
×
113
        }
114
        return nil
×
115
}
116

117
func addCommonPopulatorsWatches(mgr manager.Manager, c controller.Controller, log logr.Logger, sourceKind string, sourceType client.Object) error {
×
118
        // Setup watches
×
119
        if err := c.Watch(source.Kind(mgr.GetCache(), &corev1.PersistentVolumeClaim{}, handler.TypedEnqueueRequestsFromMapFunc[*corev1.PersistentVolumeClaim](
×
120
                func(_ context.Context, pvc *corev1.PersistentVolumeClaim) []reconcile.Request {
×
121
                        if IsPVCDataSourceRefKind(pvc, sourceKind) {
×
122
                                pvcKey := types.NamespacedName{Namespace: pvc.Namespace, Name: pvc.Name}
×
123
                                return []reconcile.Request{{NamespacedName: pvcKey}}
×
124
                        }
×
125
                        if isPVCPrimeDataSourceRefKind(pvc, sourceKind) {
×
126
                                owner := metav1.GetControllerOf(pvc)
×
127
                                pvcKey := types.NamespacedName{Namespace: pvc.Namespace, Name: owner.Name}
×
128
                                return []reconcile.Request{{NamespacedName: pvcKey}}
×
129
                        }
×
130
                        return nil
×
131
                }),
132
        )); err != nil {
×
133
                return err
×
134
        }
×
135

136
        mapDataSourceRefToPVC := func(ctx context.Context, obj client.Object) (reqs []reconcile.Request) {
×
137
                var pvcs corev1.PersistentVolumeClaimList
×
138
                matchingFields := client.MatchingFields{
×
139
                        dataSourceRefField: getPopulatorIndexKey(cc.AnnAPIGroup, sourceKind, obj.GetNamespace(), obj.GetName()),
×
140
                }
×
141
                if err := mgr.GetClient().List(ctx, &pvcs, matchingFields); err != nil {
×
142
                        log.Error(err, "Unable to list PVCs", "matchingFields", matchingFields)
×
143
                        return reqs
×
144
                }
×
145
                for _, pvc := range pvcs.Items {
×
146
                        reqs = append(reqs, reconcile.Request{NamespacedName: types.NamespacedName{Namespace: pvc.Namespace, Name: pvc.Name}})
×
147
                }
×
148
                return reqs
×
149
        }
150

151
        if err := c.Watch(source.Kind(mgr.GetCache(), sourceType,
×
152
                handler.EnqueueRequestsFromMapFunc(mapDataSourceRefToPVC),
×
153
        )); err != nil {
×
154
                return err
×
155
        }
×
156

157
        return nil
×
158
}
159

160
func (r *ReconcilerBase) getPVCPrime(pvc *corev1.PersistentVolumeClaim) (*corev1.PersistentVolumeClaim, error) {
1✔
161
        pvcPrime := &corev1.PersistentVolumeClaim{}
1✔
162
        pvcPrimeKey := types.NamespacedName{Namespace: pvc.Namespace, Name: PVCPrimeName(pvc)}
1✔
163
        if err := r.client.Get(context.TODO(), pvcPrimeKey, pvcPrime); err != nil {
2✔
164
                if !k8serrors.IsNotFound(err) {
1✔
165
                        return nil, err
×
166
                }
×
167
                return nil, nil
1✔
168
        }
169
        return pvcPrime, nil
1✔
170
}
171

172
func (r *ReconcilerBase) createPVCPrime(pvc *corev1.PersistentVolumeClaim, source client.Object, waitForFirstConsumer bool, updatePVCForPopulation pvcModifierFunc) (*corev1.PersistentVolumeClaim, error) {
1✔
173
        labels := make(map[string]string)
1✔
174
        labels[common.CDILabelKey] = common.CDILabelValue
1✔
175
        annotations := make(map[string]string)
1✔
176
        annotations[cc.AnnImmediateBinding] = ""
1✔
177
        if waitForFirstConsumer {
2✔
178
                annotations[cc.AnnSelectedNode] = pvc.Annotations[cc.AnnSelectedNode]
1✔
179
        }
1✔
180
        if _, ok := pvc.Annotations[cc.AnnPodRetainAfterCompletion]; ok {
2✔
181
                annotations[cc.AnnPodRetainAfterCompletion] = pvc.Annotations[cc.AnnPodRetainAfterCompletion]
1✔
182
        }
1✔
183

184
        // Assemble PVC' spec
185
        pvcPrime := &corev1.PersistentVolumeClaim{
1✔
186
                ObjectMeta: metav1.ObjectMeta{
1✔
187
                        Name:        PVCPrimeName(pvc),
1✔
188
                        Namespace:   pvc.Namespace,
1✔
189
                        Labels:      labels,
1✔
190
                        Annotations: annotations,
1✔
191
                },
1✔
192
                Spec: corev1.PersistentVolumeClaimSpec{
1✔
193
                        AccessModes:      pvc.Spec.AccessModes,
1✔
194
                        Resources:        pvc.Spec.Resources,
1✔
195
                        StorageClassName: pvc.Spec.StorageClassName,
1✔
196
                        VolumeMode:       pvc.Spec.VolumeMode,
1✔
197
                },
1✔
198
        }
1✔
199
        pvcPrime.OwnerReferences = []metav1.OwnerReference{
1✔
200
                *metav1.NewControllerRef(pvc, schema.GroupVersionKind{
1✔
201
                        Group:   "",
1✔
202
                        Version: "v1",
1✔
203
                        Kind:    "PersistentVolumeClaim",
1✔
204
                }),
1✔
205
        }
1✔
206
        cc.CopyAllowedAnnotations(pvc, pvcPrime)
1✔
207
        util.SetRecommendedLabels(pvcPrime, r.installerLabels, "cdi-controller")
1✔
208

1✔
209
        // We use the populator-specific pvcModifierFunc to add required annotations
1✔
210
        if updatePVCForPopulation != nil {
2✔
211
                updatePVCForPopulation(pvcPrime, source)
1✔
212
        }
1✔
213

214
        if err := r.client.Create(context.TODO(), pvcPrime); err != nil {
1✔
215
                return nil, err
×
216
        }
×
217
        r.recorder.Eventf(pvc, corev1.EventTypeNormal, createdPVCPrimeSuccessfully, messageCreatedPVCPrimeSuccessfully)
1✔
218
        return pvcPrime, nil
1✔
219
}
220

221
type updatePVCAnnotationsFunc func(pvc, pvcPrime *corev1.PersistentVolumeClaim)
222

223
var desiredAnnotations = []string{cc.AnnPodPhase, cc.AnnPodReady, cc.AnnPodRestarts,
224
        cc.AnnPreallocationRequested, cc.AnnPreallocationApplied, cc.AnnCurrentCheckpoint, cc.AnnMultiStageImportDone,
225
        cc.AnnRunningCondition, cc.AnnRunningConditionMessage, cc.AnnRunningConditionReason}
226

227
func (r *ReconcilerBase) updatePVCWithPVCPrimeAnnotations(pvc, pvcPrime *corev1.PersistentVolumeClaim, updateFunc updatePVCAnnotationsFunc) (*corev1.PersistentVolumeClaim, error) {
1✔
228
        pvcCopy := pvc.DeepCopy()
1✔
229
        for _, ann := range desiredAnnotations {
2✔
230
                if value, ok := pvcPrime.GetAnnotations()[ann]; ok {
2✔
231
                        cc.AddAnnotation(pvcCopy, ann, value)
1✔
232
                } else if _, ok := pvcCopy.GetAnnotations()[ann]; ok {
3✔
233
                        // if the desired Annotation was deleted from pvcPrime
1✔
234
                        // delete it also in the target pvc
1✔
235
                        delete(pvcCopy.Annotations, ann)
1✔
236
                }
1✔
237
        }
238
        if updateFunc != nil {
2✔
239
                updateFunc(pvcCopy, pvcPrime)
1✔
240
        }
1✔
241

242
        if !reflect.DeepEqual(pvc.ObjectMeta, pvcCopy.ObjectMeta) {
2✔
243
                err := r.client.Update(context.TODO(), pvcCopy)
1✔
244
                if err != nil {
1✔
245
                        return nil, err
×
246
                }
×
247
        }
248

249
        return pvcCopy, nil
1✔
250
}
251

252
func (r *ReconcilerBase) updatePVCWithPVCPrimeLabels(pvc *corev1.PersistentVolumeClaim, pvcPrimeLabels map[string]string) (*corev1.PersistentVolumeClaim, error) {
1✔
253
        pvcCopy := pvc.DeepCopy()
1✔
254
        cc.CopyAllowedLabels(pvcPrimeLabels, pvcCopy, false)
1✔
255
        if !reflect.DeepEqual(pvc.ObjectMeta, pvcCopy.ObjectMeta) {
2✔
256
                if err := r.client.Update(context.TODO(), pvcCopy); err != nil {
1✔
257
                        return nil, err
×
258
                }
×
259
        }
260
        return pvcCopy, nil
1✔
261
}
262

263
// reconcile functions
264

265
func (r *ReconcilerBase) reconcile(req reconcile.Request, populator populatorController, pvcNameLogger logr.Logger) (reconcile.Result, error) {
1✔
266
        // Get the target PVC
1✔
267
        pvc := &corev1.PersistentVolumeClaim{}
1✔
268
        if err := r.client.Get(context.TODO(), req.NamespacedName, pvc); err != nil {
2✔
269
                if k8serrors.IsNotFound(err) {
2✔
270
                        return reconcile.Result{}, nil
1✔
271
                }
1✔
272
                return reconcile.Result{}, err
×
273
        }
274

275
        // We first perform the common reconcile steps.
276
        // We should only continue if we get a valid PVC'
277
        pvcPrime, err := r.reconcileCommon(pvc, populator, pvcNameLogger)
1✔
278
        if err != nil || pvcPrime == nil {
2✔
279
                return reconcile.Result{}, err
1✔
280
        }
1✔
281

282
        // Each populator reconciles the target PVC in a different way
283
        res, err := populator.reconcileTargetPVC(pvc, pvcPrime)
1✔
284
        if err != nil {
1✔
285
                return res, err
×
286
        }
×
287

288
        // Making sure to clean PVC once it is complete
289
        if cc.IsPVCComplete(pvc) && !cc.IsMultiStageImportInProgress(pvc) {
2✔
290
                res, err = r.reconcileCleanup(pvcPrime)
1✔
291
        }
1✔
292

293
        return res, err
1✔
294
}
295

296
func (r *ReconcilerBase) reconcileCommon(pvc *corev1.PersistentVolumeClaim, populator populatorController, pvcNameLogger logr.Logger) (*corev1.PersistentVolumeClaim, error) {
1✔
297
        if pvc.DeletionTimestamp != nil {
1✔
298
                pvcNameLogger.V(1).Info("PVC being terminated, ignoring")
×
299
                return nil, nil
×
300
        }
×
301

302
        // Get the PVC'. If it does exist, return it and skip the rest of the checks
303
        // If it doesn't, we'll attempt to create it.
304
        pvcPrime, err := r.getPVCPrime(pvc)
1✔
305
        if err != nil {
1✔
306
                return nil, err
×
307
        }
×
308
        if pvcPrime != nil {
2✔
309
                return pvcPrime, nil
1✔
310
        }
1✔
311

312
        // We should ignore PVCs that aren't for this populator to handle
313
        dataSourceRef := pvc.Spec.DataSourceRef
1✔
314
        if !IsPVCDataSourceRefKind(pvc, r.sourceKind) {
1✔
315
                pvcNameLogger.V(1).Info("reconciled unexpected PVC, ignoring")
×
316
                return nil, nil
×
317
        }
×
318
        // TODO: Remove this check once we support cross-namespace dataSourceRef
319
        if dataSourceRef.Namespace != nil {
2✔
320
                pvcNameLogger.V(1).Info("cross-namespace dataSourceRef not supported yet, ignoring")
1✔
321
                return nil, nil
1✔
322
        }
1✔
323

324
        // Wait until dataSourceRef exists
325
        populationSource, err := populator.getPopulationSource(pvc)
1✔
326
        if populationSource == nil {
2✔
327
                return nil, err
1✔
328
        }
1✔
329
        // Check storage class
330
        ready, nodeName, err := claimReadyForPopulation(context.TODO(), r.client, pvc)
1✔
331
        if !ready || err != nil {
2✔
332
                return nil, err
1✔
333
        }
1✔
334

335
        // If PVC' doesn't exist and target PVC is not bound, we should create the PVC' to start the population.
336
        // We still return nil as we'll get called again once PVC' exists.
337
        // If target PVC is bound, we don't really need to populate anything.
338
        if cc.IsUnbound(pvc) {
2✔
339
                _, err := r.createPVCPrime(pvc, populationSource, nodeName != "", populator.updatePVCForPopulation)
1✔
340
                if err != nil {
1✔
341
                        r.recorder.Eventf(pvc, corev1.EventTypeWarning, errCreatingPVCPrime, err.Error())
×
342
                        return nil, err
×
343
                }
×
344
        }
345

346
        return nil, nil
1✔
347
}
348

349
func (r *ReconcilerBase) reconcileCleanup(pvcPrime *corev1.PersistentVolumeClaim) (reconcile.Result, error) {
1✔
350
        if pvcPrime != nil {
2✔
351
                if pvcPrime.Annotations[cc.AnnPodRetainAfterCompletion] == "true" {
2✔
352
                        // Retaining PVC' in Lost state. We can then keep the pod for debugging purposes.
1✔
353
                        r.recorder.Eventf(pvcPrime, corev1.EventTypeWarning, retainedPVCPrime, messageRetainedPVCPrime)
1✔
354
                } else {
2✔
355
                        if err := r.client.Delete(context.TODO(), pvcPrime); err != nil {
1✔
356
                                return reconcile.Result{}, err
×
357
                        }
×
358
                }
359
        }
360
        return reconcile.Result{}, nil
1✔
361
}
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