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

kubevirt / containerized-data-importer / #5959

23 Apr 2026 08:53PM UTC coverage: 49.561% (+0.02%) from 49.545%
#5959

push

travis-ci

web-flow
Add VDDK node selector override via annotation (#4096)

Introduce a new annotation `cdi.kubevirt.io/storage.pod.nodeSelector`
that allows overriding the CDI CR workload node selector for VDDK
importer pods. This enables scheduling VDDK imports on specific nodes
(e.g. ones with network access to the VMware host).

The annotation value is a JSON-encoded map[string]string that, when
present on the PVC, replaces the default NodeSelector from the CDI CR
while preserving tolerations and affinity. The override is applied only
when the import source is VDDK; all other import types and CDI pod types
(clone, upload, populator, cron) are unaffected.

Includes unit tests for annotation override, missing annotation
fallback, non-VDDK source ignoring the annotation, and invalid JSON
error handling.

Signed-off-by: yaacov <yzamir@redhat.com>

28 of 31 new or added lines in 1 file covered. (90.32%)

7 existing lines in 1 file now uncovered.

14944 of 30153 relevant lines covered (49.56%)

0.56 hits per line

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

71.07
/pkg/controller/import-controller.go
1
package controller
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "fmt"
7
        "net/url"
8
        "path"
9
        "reflect"
10
        "strconv"
11
        "strings"
12
        "time"
13

14
        "github.com/go-logr/logr"
15
        "github.com/pkg/errors"
16

17
        corev1 "k8s.io/api/core/v1"
18
        v1 "k8s.io/api/core/v1"
19
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
20
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21
        "k8s.io/apimachinery/pkg/runtime"
22
        "k8s.io/apimachinery/pkg/types"
23
        "k8s.io/apimachinery/pkg/util/sets"
24
        "k8s.io/client-go/tools/record"
25
        "k8s.io/utils/ptr"
26

27
        "sigs.k8s.io/controller-runtime/pkg/client"
28
        "sigs.k8s.io/controller-runtime/pkg/controller"
29
        "sigs.k8s.io/controller-runtime/pkg/handler"
30
        "sigs.k8s.io/controller-runtime/pkg/manager"
31
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
32
        "sigs.k8s.io/controller-runtime/pkg/source"
33

34
        cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
35
        "kubevirt.io/containerized-data-importer/pkg/common"
36
        cc "kubevirt.io/containerized-data-importer/pkg/controller/common"
37
        featuregates "kubevirt.io/containerized-data-importer/pkg/feature-gates"
38
        "kubevirt.io/containerized-data-importer/pkg/util"
39
        "kubevirt.io/containerized-data-importer/pkg/util/naming"
40
        sdkapi "kubevirt.io/controller-lifecycle-operator-sdk/api"
41
)
42

43
const (
44
        // ErrImportFailedPVC provides a const to indicate an import to the PVC failed
45
        ErrImportFailedPVC = "ErrImportFailed"
46
        // ImportSucceededPVC provides a const to indicate an import to the PVC failed
47
        ImportSucceededPVC = "ImportSucceeded"
48

49
        // creatingScratch provides a const to indicate scratch is being created.
50
        creatingScratch = "CreatingScratchSpace"
51

52
        // ImportTargetInUse is reason for event created when an import pvc is in use
53
        ImportTargetInUse = "ImportTargetInUse"
54

55
        // importPodImageStreamFinalizer ensures image stream import pod is deleted when pvc is deleted,
56
        // as in this case pod has no pvc OwnerReference
57
        importPodImageStreamFinalizer = "cdi.kubevirt.io/importImageStream"
58

59
        // secretExtraHeadersVolumeName is the format string that specifies where extra HTTP header secrets will be mounted
60
        secretExtraHeadersVolumeName = "cdi-secret-extra-headers-vol-%d"
61
)
62

63
// ImportReconciler members
64
type ImportReconciler struct {
65
        client             client.Client
66
        uncachedClient     client.Client
67
        recorder           record.EventRecorder
68
        scheme             *runtime.Scheme
69
        log                logr.Logger
70
        image              string
71
        verbose            string
72
        pullPolicy         string
73
        filesystemOverhead string //nolint:unused // TODO: check if need to remove this field
74
        cdiNamespace       string
75
        featureGates       featuregates.FeatureGates
76
        installerLabels    map[string]string
77
}
78

79
type importPodEnvVar struct {
80
        ep                        string
81
        secretName                string
82
        source                    string
83
        contentType               string
84
        imageSize                 string
85
        certConfigMap             string
86
        diskID                    string
87
        uuid                      string
88
        pullMethod                string
89
        readyFile                 string
90
        doneFile                  string
91
        backingFile               string
92
        thumbprint                string
93
        filesystemOverhead        string
94
        insecureTLS               bool
95
        currentCheckpoint         string
96
        previousCheckpoint        string
97
        finalCheckpoint           string
98
        preallocation             bool
99
        httpProxy                 string
100
        httpsProxy                string
101
        noProxy                   string
102
        certConfigMapProxy        string
103
        extraHeaders              []string
104
        secretExtraHeaders        []string
105
        cacheMode                 string
106
        registryImageArchitecture string
107
        checksum                  string
108
}
109

110
type importerPodArgs struct {
111
        image                   string
112
        importImage             string
113
        verbose                 string
114
        pullPolicy              string
115
        podEnvVar               *importPodEnvVar
116
        pvc                     *corev1.PersistentVolumeClaim
117
        scratchPvcName          *string
118
        podResourceRequirements *corev1.ResourceRequirements
119
        imagePullSecrets        []corev1.LocalObjectReference
120
        workloadNodePlacement   *sdkapi.NodePlacement
121
        vddkImageName           *string
122
        vddkExtraArgs           *string
123
        vddkNodeSelector        map[string]string
124
        priorityClassName       string
125
        serviceAccountName      string
126
}
127

128
// NewImportController creates a new instance of the import controller.
129
func NewImportController(mgr manager.Manager, log logr.Logger, importerImage, pullPolicy, verbose string, installerLabels map[string]string) (controller.Controller, error) {
×
130
        uncachedClient, err := client.New(mgr.GetConfig(), client.Options{
×
131
                Scheme: mgr.GetScheme(),
×
132
                Mapper: mgr.GetRESTMapper(),
×
133
        })
×
134
        if err != nil {
×
135
                return nil, err
×
136
        }
×
137
        client := mgr.GetClient()
×
138
        reconciler := &ImportReconciler{
×
139
                client:          client,
×
140
                uncachedClient:  uncachedClient,
×
141
                scheme:          mgr.GetScheme(),
×
142
                log:             log.WithName("import-controller"),
×
143
                image:           importerImage,
×
144
                verbose:         verbose,
×
145
                pullPolicy:      pullPolicy,
×
146
                recorder:        mgr.GetEventRecorderFor("import-controller"),
×
147
                cdiNamespace:    util.GetNamespace(),
×
148
                featureGates:    featuregates.NewFeatureGates(client),
×
149
                installerLabels: installerLabels,
×
150
        }
×
151
        importController, err := controller.New("import-controller", mgr, controller.Options{
×
152
                MaxConcurrentReconciles: 3,
×
153
                Reconciler:              reconciler,
×
154
        })
×
155
        if err != nil {
×
156
                return nil, err
×
157
        }
×
158
        if err := addImportControllerWatches(mgr, importController); err != nil {
×
159
                return nil, err
×
160
        }
×
161
        return importController, nil
×
162
}
163

164
func addImportControllerWatches(mgr manager.Manager, importController controller.Controller) error {
×
165
        // Setup watches
×
166
        if err := importController.Watch(source.Kind(mgr.GetCache(), &corev1.PersistentVolumeClaim{}, &handler.TypedEnqueueRequestForObject[*corev1.PersistentVolumeClaim]{})); err != nil {
×
167
                return err
×
168
        }
×
169
        if err := importController.Watch(source.Kind(mgr.GetCache(), &corev1.Pod{}, handler.TypedEnqueueRequestForOwner[*corev1.Pod](
×
170
                mgr.GetScheme(), mgr.GetClient().RESTMapper(), &corev1.PersistentVolumeClaim{}, handler.OnlyControllerOwner()))); err != nil {
×
171
                return err
×
172
        }
×
173

174
        return nil
×
175
}
176

177
func (r *ImportReconciler) shouldReconcilePVC(pvc *corev1.PersistentVolumeClaim,
178
        log logr.Logger) (bool, error) {
1✔
179
        _, pvcUsesExternalPopulator := pvc.Annotations[cc.AnnExternalPopulation]
1✔
180
        if pvcUsesExternalPopulator {
1✔
181
                return false, nil
×
182
        }
×
183

184
        waitForFirstConsumerEnabled, err := cc.IsWaitForFirstConsumerEnabled(pvc, r.featureGates)
1✔
185
        if err != nil {
1✔
186
                return false, err
×
187
        }
×
188

189
        return (!cc.IsPVCComplete(pvc) || cc.IsMultiStageImportInProgress(pvc)) &&
1✔
190
                        (checkPVC(pvc, cc.AnnEndpoint, log) || checkPVC(pvc, cc.AnnSource, log)) &&
1✔
191
                        shouldHandlePvc(pvc, waitForFirstConsumerEnabled, log),
1✔
192
                nil
1✔
193
}
194

195
// Reconcile the reconcile loop for the CDIConfig object.
196
func (r *ImportReconciler) Reconcile(_ context.Context, req reconcile.Request) (reconcile.Result, error) {
1✔
197
        log := r.log.WithValues("PVC", req.NamespacedName)
1✔
198
        log.V(1).Info("reconciling Import PVCs")
1✔
199

1✔
200
        // Get the PVC.
1✔
201
        pvc := &corev1.PersistentVolumeClaim{}
1✔
202
        if err := r.client.Get(context.TODO(), req.NamespacedName, pvc); err != nil {
2✔
203
                if k8serrors.IsNotFound(err) {
2✔
204
                        return reconcile.Result{}, nil
1✔
205
                }
1✔
206
                return reconcile.Result{}, err
×
207
        }
208

209
        // only want to update bound condition for relevant type
210
        if checkPVC(pvc, cc.AnnEndpoint, log) || checkPVC(pvc, cc.AnnSource, log) {
2✔
211
                if err := cc.UpdatePVCBoundContionFromEvents(pvc, r.client, log); err != nil {
1✔
212
                        return reconcile.Result{}, err
×
213
                }
×
214
        }
215

216
        shouldReconcile, err := r.shouldReconcilePVC(pvc, log)
1✔
217
        if err != nil {
1✔
218
                return reconcile.Result{}, err
×
219
        }
×
220
        if !shouldReconcile {
2✔
221
                multiStageImport := metav1.HasAnnotation(pvc.ObjectMeta, cc.AnnCurrentCheckpoint)
1✔
222
                multiStageAlreadyDone := metav1.HasAnnotation(pvc.ObjectMeta, cc.AnnMultiStageImportDone)
1✔
223

1✔
224
                log.V(3).Info("Should not reconcile this PVC",
1✔
225
                        "pvc.annotation.phase.complete", cc.IsPVCComplete(pvc),
1✔
226
                        "pvc.annotations.endpoint", checkPVC(pvc, cc.AnnEndpoint, log),
1✔
227
                        "pvc.annotations.source", checkPVC(pvc, cc.AnnSource, log),
1✔
228
                        "isBound", isBound(pvc, log), "isMultistage", multiStageImport, "multiStageDone", multiStageAlreadyDone)
1✔
229
                return reconcile.Result{}, nil
1✔
230
        }
1✔
231

232
        return r.reconcilePvc(pvc, log)
1✔
233
}
234

235
func (r *ImportReconciler) findImporterPod(pvc *corev1.PersistentVolumeClaim, log logr.Logger) (*corev1.Pod, error) {
1✔
236
        podName := getImportPodNameFromPvc(pvc)
1✔
237
        pod := &corev1.Pod{}
1✔
238
        if err := r.client.Get(context.TODO(), types.NamespacedName{Name: podName, Namespace: pvc.GetNamespace()}, pod); err != nil {
2✔
239
                if !k8serrors.IsNotFound(err) {
1✔
240
                        return nil, errors.Wrapf(err, "error getting import pod %s/%s", pvc.Namespace, podName)
×
241
                }
×
242
                return nil, nil
1✔
243
        }
244
        if !metav1.IsControlledBy(pod, pvc) && !cc.IsImageStream(pvc) {
2✔
245
                return nil, errors.Errorf("Pod is not owned by PVC")
1✔
246
        }
1✔
247
        log.V(1).Info("Pod is owned by PVC", pod.Name, pvc.Name)
1✔
248
        return pod, nil
1✔
249
}
250

251
func (r *ImportReconciler) reconcilePvc(pvc *corev1.PersistentVolumeClaim, log logr.Logger) (reconcile.Result, error) {
1✔
252
        // See if we have a pod associated with the PVC, we know the PVC has the needed annotations.
1✔
253
        pod, err := r.findImporterPod(pvc, log)
1✔
254
        if err != nil {
2✔
255
                return reconcile.Result{}, err
1✔
256
        }
1✔
257

258
        if pod == nil {
2✔
259
                if cc.IsPVCComplete(pvc) {
1✔
260
                        // Don't create the POD if the PVC is completed already
×
261
                        log.V(1).Info("PVC is already complete")
×
262
                } else if pvc.Annotations[cc.AnnImportFatalError] == "true" {
1✔
263
                        // Don't create the POD if there was a fatal error (e.g., checksum mismatch)
×
264
                        log.V(1).Info("PVC has fatal error annotation, will not create pod")
×
265
                } else if pvc.DeletionTimestamp == nil {
2✔
266
                        podsUsingPVC, err := cc.GetPodsUsingPVCs(context.TODO(), r.client, pvc.Namespace, sets.New(pvc.Name), false)
1✔
267
                        if err != nil {
1✔
268
                                return reconcile.Result{}, err
×
269
                        }
×
270

271
                        if len(podsUsingPVC) > 0 {
2✔
272
                                for _, pod := range podsUsingPVC {
2✔
273
                                        r.log.V(1).Info("can't create import pod, pvc in use by other pod",
1✔
274
                                                "namespace", pvc.Namespace, "name", pvc.Name, "pod", pod.Name)
1✔
275
                                        r.recorder.Eventf(pvc, corev1.EventTypeWarning, ImportTargetInUse,
1✔
276
                                                "pod %s/%s using PersistentVolumeClaim %s", pod.Namespace, pod.Name, pvc.Name)
1✔
277
                                }
1✔
278
                                return reconcile.Result{Requeue: true}, nil
1✔
279
                        }
280

281
                        if _, ok := pvc.Annotations[cc.AnnImportPod]; ok {
2✔
282
                                // Create importer pod, make sure the PVC owns it.
1✔
283
                                if err := r.createImporterPod(pvc); err != nil {
1✔
284
                                        return reconcile.Result{}, err
×
285
                                }
×
286
                        } else {
1✔
287
                                // Create importer pod Name and store in PVC?
1✔
288
                                if err := r.initPvcPodName(pvc, log); err != nil {
1✔
289
                                        return reconcile.Result{}, err
×
290
                                }
×
291
                        }
292
                }
293
        } else {
1✔
294
                if pvc.DeletionTimestamp != nil {
1✔
295
                        log.V(1).Info("PVC being terminated, delete pods", "pod.Name", pod.Name)
×
296
                        if err := r.cleanup(pvc, pod, log); err != nil {
×
297
                                return reconcile.Result{}, err
×
298
                        }
×
299
                } else {
1✔
300
                        // Copy import proxy ConfigMap (if exists) from cdi namespace to the import namespace
1✔
301
                        if err := r.copyImportProxyConfigMap(pvc, pod); err != nil {
1✔
302
                                return reconcile.Result{}, err
×
303
                        }
×
304
                        // Pod exists, we need to update the PVC status.
305
                        if err := r.updatePvcFromPod(pvc, pod, log); err != nil {
1✔
306
                                return reconcile.Result{}, err
×
307
                        }
×
308
                }
309
        }
310

311
        if !cc.IsPVCComplete(pvc) {
2✔
312
                // We are not done yet, force a re-reconcile in 2 seconds to get an update.
1✔
313
                log.V(1).Info("Force Reconcile pvc import not finished", "pvc.Name", pvc.Name)
1✔
314

1✔
315
                return reconcile.Result{RequeueAfter: 2 * time.Second}, nil
1✔
316
        }
1✔
317
        return reconcile.Result{}, nil
1✔
318
}
319

320
func (r *ImportReconciler) copyImportProxyConfigMap(pvc *corev1.PersistentVolumeClaim, pod *corev1.Pod) error {
1✔
321
        cdiConfig := &cdiv1.CDIConfig{}
1✔
322
        if err := r.client.Get(context.TODO(), types.NamespacedName{Name: common.ConfigName}, cdiConfig); err != nil {
1✔
323
                return err
×
324
        }
×
325
        cmName, err := GetImportProxyConfig(cdiConfig, common.ImportProxyConfigMapName)
1✔
326
        if err != nil || cmName == "" {
2✔
327
                return nil
1✔
328
        }
1✔
329
        cdiConfigMap := &corev1.ConfigMap{}
×
330
        if err := r.uncachedClient.Get(context.TODO(), types.NamespacedName{Name: cmName, Namespace: r.cdiNamespace}, cdiConfigMap); err != nil {
×
331
                return err
×
332
        }
×
333
        importConfigMap := &corev1.ConfigMap{
×
334
                ObjectMeta: metav1.ObjectMeta{
×
335
                        Name:      GetImportProxyConfigMapName(pvc.Name),
×
336
                        Namespace: pvc.Namespace,
×
337
                        OwnerReferences: []metav1.OwnerReference{{
×
338
                                APIVersion:         pod.APIVersion,
×
339
                                Kind:               pod.Kind,
×
340
                                Name:               pod.Name,
×
341
                                UID:                pod.UID,
×
342
                                BlockOwnerDeletion: ptr.To[bool](true),
×
343
                                Controller:         ptr.To[bool](true),
×
344
                        }},
×
345
                },
×
346
                Data: cdiConfigMap.Data,
×
347
        }
×
348
        if err := r.client.Create(context.TODO(), importConfigMap); err != nil && !k8serrors.IsAlreadyExists(err) {
×
349
                return err
×
350
        }
×
351
        return nil
×
352
}
353

354
// GetImportProxyConfigMapName returns the import proxy ConfigMap name
355
func GetImportProxyConfigMapName(pvcName string) string {
×
356
        return naming.GetResourceName("import-proxy-cm", pvcName)
×
357
}
×
358

359
func (r *ImportReconciler) initPvcPodName(pvc *corev1.PersistentVolumeClaim, log logr.Logger) error {
1✔
360
        currentPvcCopy := pvc.DeepCopyObject()
1✔
361

1✔
362
        log.V(1).Info("Init pod name on PVC")
1✔
363
        anno := pvc.GetAnnotations()
1✔
364

1✔
365
        anno[cc.AnnImportPod] = createImportPodNameFromPvc(pvc)
1✔
366

1✔
367
        requiresScratch := r.requiresScratchSpace(pvc)
1✔
368
        if requiresScratch {
1✔
369
                anno[cc.AnnRequiresScratch] = "true"
×
370
        }
×
371

372
        if !reflect.DeepEqual(currentPvcCopy, pvc) {
2✔
373
                if err := r.updatePVC(pvc, log); err != nil {
1✔
374
                        return err
×
375
                }
×
376
                log.V(1).Info("Updated PVC", "pvc.anno.AnnImportPod", anno[cc.AnnImportPod])
1✔
377
        }
378
        return nil
1✔
379
}
380

381
func (r *ImportReconciler) updatePvcFromPod(pvc *corev1.PersistentVolumeClaim, pod *corev1.Pod, log logr.Logger) error {
1✔
382
        // Keep a copy of the original for comparison later.
1✔
383
        currentPvcCopy := pvc.DeepCopyObject()
1✔
384

1✔
385
        log.V(1).Info("Updating PVC from pod")
1✔
386
        anno := pvc.GetAnnotations()
1✔
387

1✔
388
        termMsg, err := parseTerminationMessage(pod)
1✔
389
        if err != nil {
2✔
390
                log.V(3).Info("Ignoring failure to parse termination message", "error", err.Error())
1✔
391
        }
1✔
392
        setAnnotationsFromPodWithPrefix(anno, pod, termMsg, cc.AnnRunningCondition)
1✔
393

1✔
394
        // Handle fatal errors reported via termination message even when the container exited with code 0.
1✔
395
        // In this branch we intentionally override the actual Pod phase (Succeeded) and mark the PVC as Failed + fatal
1✔
396
        // to stop retries; this keeps DV status aligned with the fatal outcome rather than the Pod phase.
1✔
397
        // Check for "checksum mismatch" which is the error message from ErrChecksumMismatch.
1✔
398
        if termMsg != nil && termMsg.Message != nil && strings.Contains(*termMsg.Message, "checksum mismatch") {
1✔
399
                log.Info("Checksum validation failed via termination message (permanent error, will not retry)", "pod.Name", pod.Name)
×
400
                anno[cc.AnnPodPhase] = string(corev1.PodFailed) // override Succeeded to reflect fatal state
×
401
                anno[cc.AnnImportFatalError] = "true"
×
402
                anno[cc.AnnRunningCondition] = "false"
×
403
                if anno[cc.AnnRunningConditionMessage] == "" {
×
404
                        anno[cc.AnnRunningConditionMessage] = simplifyKnownMessage(*termMsg.Message)
×
405
                }
×
406
                anno[cc.AnnRunningConditionReason] = "ChecksumError"
×
407
                r.recorder.Event(pvc, corev1.EventTypeWarning, ErrImportFailedPVC, simplifyKnownMessage(*termMsg.Message))
×
408
        }
409

410
        scratchSpaceRequired := termMsg != nil && termMsg.ScratchSpaceRequired != nil && *termMsg.ScratchSpaceRequired
1✔
411
        if scratchSpaceRequired {
2✔
412
                log.V(1).Info("Pod requires scratch space, terminating pod, and restarting with scratch space", "pod.Name", pod.Name)
1✔
413
        }
1✔
414
        podModificationsNeeded := scratchSpaceRequired
1✔
415

1✔
416
        if statuses := pod.Status.ContainerStatuses; len(statuses) > 0 {
2✔
417
                if isOOMKilled(statuses[0]) {
2✔
418
                        log.V(1).Info("Pod died of an OOM, deleting pod, and restarting with qemu cache mode=none if storage supports it", "pod.Name", pod.Name)
1✔
419
                        podModificationsNeeded = true
1✔
420
                        anno[cc.AnnRequiresDirectIO] = "true"
1✔
421
                }
1✔
422

423
                if terminated := statuses[0].State.Terminated; terminated != nil && terminated.ExitCode > 0 {
2✔
424
                        log.Info("Pod termination code", "pod.Name", pod.Name, "ExitCode", terminated.ExitCode)
1✔
425
                        r.recorder.Event(pvc, corev1.EventTypeWarning, ErrImportFailedPVC, terminated.Message)
1✔
426
                }
1✔
427
        }
428

429
        if anno[cc.AnnCurrentCheckpoint] != "" {
1✔
430
                anno[cc.AnnCurrentPodID] = string(pod.ObjectMeta.UID)
×
431
        }
×
432

433
        anno[cc.AnnImportPod] = pod.Name
1✔
434
        if !podModificationsNeeded && anno[cc.AnnImportFatalError] != "true" {
2✔
435
                // No scratch space required, update the phase based on the pod. If we require scratch space we don't want to update the
1✔
436
                // phase, because the pod might terminate cleanly and mistakenly mark the import complete.
1✔
437
                anno[cc.AnnPodPhase] = string(pod.Status.Phase)
1✔
438
        }
1✔
439

440
        anno[cc.AnnPodSchedulable] = "true"
1✔
441
        if phase, ok := anno[cc.AnnPodPhase]; ok && phase == string(corev1.PodPending) {
2✔
442
                for _, cond := range pod.Status.Conditions {
1✔
443
                        if cond.Type == corev1.PodScheduled && cond.Reason == corev1.PodReasonUnschedulable {
×
444
                                anno[cc.AnnPodSchedulable] = "false"
×
445
                                break
×
446
                        }
447
                }
448
        }
449

450
        for _, ev := range pod.Spec.Containers[0].Env {
2✔
451
                if ev.Name == common.CacheMode && ev.Value == common.CacheModeTryNone {
1✔
452
                        anno[cc.AnnRequiresDirectIO] = "false"
×
453
                }
×
454
        }
455

456
        // Check if the POD is waiting for scratch space, if so create some.
457
        if pod.Status.Phase == corev1.PodPending && r.requiresScratchSpace(pvc) {
2✔
458
                if err := r.createScratchPvcForPod(pvc, pod); err != nil {
1✔
459
                        if !k8serrors.IsAlreadyExists(err) {
×
460
                                return err
×
461
                        }
×
462
                }
463
        } else {
1✔
464
                // No scratch space, or scratch space is bound, remove annotation
1✔
465
                delete(anno, cc.AnnBoundCondition)
1✔
466
                delete(anno, cc.AnnBoundConditionMessage)
1✔
467
                delete(anno, cc.AnnBoundConditionReason)
1✔
468
        }
1✔
469

470
        if pvc.GetLabels() == nil {
2✔
471
                pvc.SetLabels(make(map[string]string, 0))
1✔
472
        }
1✔
473
        if !checkIfLabelExists(pvc, common.CDILabelKey, common.CDILabelValue) {
2✔
474
                pvc.GetLabels()[common.CDILabelKey] = common.CDILabelValue
1✔
475
        }
1✔
476
        if cc.IsPVCComplete(pvc) {
2✔
477
                pvc.SetLabels(addLabelsFromTerminationMessage(pvc.GetLabels(), termMsg))
1✔
478
        }
1✔
479

480
        if !reflect.DeepEqual(currentPvcCopy, pvc) {
2✔
481
                if err := r.updatePVC(pvc, log); err != nil {
1✔
482
                        return err
×
483
                }
×
484
                log.V(1).Info("Updated PVC", "pvc.anno.Phase", anno[cc.AnnPodPhase], "pvc.anno.Restarts", anno[cc.AnnPodRestarts])
1✔
485
        }
486

487
        // If fatal error detected, delete pod immediately to stop K8s restart loop
488
        if anno[cc.AnnImportFatalError] == "true" {
1✔
489
                log.V(1).Info("Deleting pod due to fatal error", "pod.Name", pod.Name)
×
490
                if err := r.cleanup(pvc, pod, log); err != nil {
×
491
                        return err
×
492
                }
×
493
                return nil
×
494
        }
495

496
        if cc.IsPVCComplete(pvc) || podModificationsNeeded {
2✔
497
                if !podModificationsNeeded {
2✔
498
                        r.recorder.Event(pvc, corev1.EventTypeNormal, ImportSucceededPVC, "Import Successful")
1✔
499
                        log.V(1).Info("Import completed successfully")
1✔
500
                }
1✔
501
                if cc.ShouldDeletePod(pvc) {
2✔
502
                        log.V(1).Info("Deleting pod", "pod.Name", pod.Name)
1✔
503
                        if err := r.cleanup(pvc, pod, log); err != nil {
1✔
504
                                return err
×
505
                        }
×
506
                }
507
        }
508
        return nil
1✔
509
}
510

511
func (r *ImportReconciler) cleanup(pvc *corev1.PersistentVolumeClaim, pod *corev1.Pod, log logr.Logger) error {
1✔
512
        if err := r.client.Delete(context.TODO(), pod); cc.IgnoreNotFound(err) != nil {
1✔
513
                return err
×
514
        }
×
515
        if cc.HasFinalizer(pvc, importPodImageStreamFinalizer) {
1✔
516
                cc.RemoveFinalizer(pvc, importPodImageStreamFinalizer)
×
517
                if err := r.updatePVC(pvc, log); err != nil {
×
518
                        return err
×
519
                }
×
520
        }
521
        return nil
1✔
522
}
523

524
func (r *ImportReconciler) updatePVC(pvc *corev1.PersistentVolumeClaim, log logr.Logger) error {
1✔
525
        if err := r.client.Update(context.TODO(), pvc); err != nil {
1✔
526
                return err
×
527
        }
×
528
        return nil
1✔
529
}
530

531
func (r *ImportReconciler) createImporterPod(pvc *corev1.PersistentVolumeClaim) error {
1✔
532
        r.log.V(1).Info("Creating importer POD for PVC", "pvc.Name", pvc.Name)
1✔
533
        var scratchPvcName *string
1✔
534
        var vddkImageName *string
1✔
535
        var vddkExtraArgs *string
1✔
536
        var vddkNodeSelector map[string]string
1✔
537
        var err error
1✔
538

1✔
539
        requiresScratch := r.requiresScratchSpace(pvc)
1✔
540
        if requiresScratch {
1✔
541
                name := createScratchNameFromPvc(pvc)
×
542
                scratchPvcName = &name
×
543
        }
×
544

545
        if cc.GetSource(pvc) == cc.SourceVDDK {
2✔
546
                r.log.V(1).Info("Pod requires VDDK sidecar for VMware transfer")
1✔
547
                anno := pvc.GetAnnotations()
1✔
548
                if imageName, ok := anno[cc.AnnVddkInitImageURL]; ok {
2✔
549
                        vddkImageName = &imageName
1✔
550
                } else {
2✔
551
                        if vddkImageName, err = r.getVddkImageName(); err != nil {
2✔
552
                                r.log.V(1).Error(err, "failed to get VDDK image name from configmap")
1✔
553
                        }
1✔
554
                }
555
                if vddkImageName == nil {
2✔
556
                        message := fmt.Sprintf("waiting for %s configmap or %s annotation for VDDK image", common.VddkConfigMap, cc.AnnVddkInitImageURL)
1✔
557
                        anno[cc.AnnBoundCondition] = "false"
1✔
558
                        anno[cc.AnnBoundConditionMessage] = message
1✔
559
                        anno[cc.AnnBoundConditionReason] = common.AwaitingVDDK
1✔
560
                        if err := r.updatePVC(pvc, r.log); err != nil {
1✔
561
                                return err
×
562
                        }
×
563
                        return errors.New(message)
1✔
564
                }
565

566
                if extraArgs, ok := anno[cc.AnnVddkExtraArgs]; ok && extraArgs != "" {
2✔
567
                        r.log.V(1).Info("Mounting extra VDDK args ConfigMap to importer pod", "ConfigMap", extraArgs)
1✔
568
                        vddkExtraArgs = &extraArgs
1✔
569

1✔
570
                        vddkNodeSelector, err = r.getVddkNodeSelector(pvc.Namespace, extraArgs)
1✔
571
                        if err != nil {
2✔
572
                                return err
1✔
573
                        }
1✔
574
                }
575
        }
576

577
        podEnvVar, err := r.createImportEnvVar(pvc)
1✔
578
        if err != nil {
1✔
579
                return err
×
580
        }
×
581
        // all checks passed, let's create the importer pod!
582
        podArgs := &importerPodArgs{
1✔
583
                image:              r.image,
1✔
584
                verbose:            r.verbose,
1✔
585
                pullPolicy:         r.pullPolicy,
1✔
586
                podEnvVar:          podEnvVar,
1✔
587
                pvc:                pvc,
1✔
588
                scratchPvcName:     scratchPvcName,
1✔
589
                vddkImageName:      vddkImageName,
1✔
590
                vddkExtraArgs:      vddkExtraArgs,
1✔
591
                vddkNodeSelector:   vddkNodeSelector,
1✔
592
                priorityClassName:  cc.GetPriorityClass(pvc),
1✔
593
                serviceAccountName: cc.GetPodServiceAccount(pvc),
1✔
594
        }
1✔
595

1✔
596
        pod, err := createImporterPod(context.TODO(), r.log, r.client, podArgs, r.installerLabels)
1✔
597
        // Check if pod has failed and, in that case, record an event with the error
1✔
598
        if podErr := cc.HandleFailedPod(err, pvc.Annotations[cc.AnnImportPod], pvc, r.recorder, r.client); podErr != nil {
1✔
599
                return podErr
×
600
        }
×
601

602
        r.log.V(1).Info("Created POD", "pod.Name", pod.Name)
1✔
603

1✔
604
        // If importing from image stream, add finalizer. Note we don't watch the importer pod in this case,
1✔
605
        // so to prevent a deadlock we add finalizer only if the pod is not retained after completion.
1✔
606
        if cc.IsImageStream(pvc) && pvc.GetAnnotations()[cc.AnnPodRetainAfterCompletion] != "true" {
1✔
607
                cc.AddFinalizer(pvc, importPodImageStreamFinalizer)
×
608
                if err := r.updatePVC(pvc, r.log); err != nil {
×
609
                        return err
×
610
                }
×
611
        }
612

613
        if requiresScratch {
1✔
614
                r.log.V(1).Info("Pod requires scratch space")
×
615
                return r.createScratchPvcForPod(pvc, pod)
×
616
        }
×
617

618
        return nil
1✔
619
}
620

621
func createScratchNameFromPvc(pvc *v1.PersistentVolumeClaim) string {
×
622
        return naming.GetResourceName(pvc.Name, common.ScratchNameSuffix)
×
623
}
×
624

625
func (r *ImportReconciler) createImportEnvVar(pvc *corev1.PersistentVolumeClaim) (*importPodEnvVar, error) {
1✔
626
        podEnvVar := &importPodEnvVar{}
1✔
627
        podEnvVar.source = cc.GetSource(pvc)
1✔
628
        podEnvVar.contentType = string(cc.GetPVCContentType(pvc))
1✔
629

1✔
630
        var err error
1✔
631
        if podEnvVar.source != cc.SourceNone {
2✔
632
                podEnvVar.ep, err = cc.GetEndpoint(pvc)
1✔
633
                if err != nil {
1✔
634
                        return nil, err
×
635
                }
×
636
                podEnvVar.secretName = r.getSecretName(pvc)
1✔
637
                if podEnvVar.secretName == "" {
2✔
638
                        r.log.V(2).Info("no secret will be supplied to endpoint", "endPoint", podEnvVar.ep)
1✔
639
                }
1✔
640
                //get the CDIConfig to extract the proxy configuration to be used to import an image
641
                cdiConfig := &cdiv1.CDIConfig{}
1✔
642
                err = r.client.Get(context.TODO(), types.NamespacedName{Name: common.ConfigName}, cdiConfig)
1✔
643
                if err != nil {
1✔
644
                        return nil, err
×
645
                }
×
646
                podEnvVar.certConfigMap, err = r.getCertConfigMap(pvc)
1✔
647
                if err != nil {
1✔
648
                        return nil, err
×
649
                }
×
650
                podEnvVar.insecureTLS, err = r.isInsecureTLS(pvc, cdiConfig)
1✔
651
                if err != nil {
1✔
652
                        return nil, err
×
653
                }
×
654
                podEnvVar.diskID = getValueFromAnnotation(pvc, cc.AnnDiskID)
1✔
655
                podEnvVar.backingFile = getValueFromAnnotation(pvc, cc.AnnBackingFile)
1✔
656
                podEnvVar.uuid = getValueFromAnnotation(pvc, cc.AnnUUID)
1✔
657
                podEnvVar.thumbprint = getValueFromAnnotation(pvc, cc.AnnThumbprint)
1✔
658
                podEnvVar.previousCheckpoint = getValueFromAnnotation(pvc, cc.AnnPreviousCheckpoint)
1✔
659
                podEnvVar.currentCheckpoint = getValueFromAnnotation(pvc, cc.AnnCurrentCheckpoint)
1✔
660
                podEnvVar.finalCheckpoint = getValueFromAnnotation(pvc, cc.AnnFinalCheckpoint)
1✔
661
                podEnvVar.registryImageArchitecture = getValueFromAnnotation(pvc, cc.AnnRegistryImageArchitecture)
1✔
662
                podEnvVar.checksum = getValueFromAnnotation(pvc, cc.AnnChecksum)
1✔
663

1✔
664
                for annotation, value := range pvc.Annotations {
2✔
665
                        if strings.HasPrefix(annotation, cc.AnnExtraHeaders) {
1✔
666
                                podEnvVar.extraHeaders = append(podEnvVar.extraHeaders, value)
×
667
                        }
×
668
                        if strings.HasPrefix(annotation, cc.AnnSecretExtraHeaders) {
1✔
669
                                podEnvVar.secretExtraHeaders = append(podEnvVar.secretExtraHeaders, value)
×
670
                        }
×
671
                }
672

673
                var field string
1✔
674
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyHTTP); err != nil {
2✔
675
                        r.log.V(3).Info("no proxy http url will be supplied:", "error", err.Error())
1✔
676
                }
1✔
677
                podEnvVar.httpProxy = field
1✔
678
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyHTTPS); err != nil {
2✔
679
                        r.log.V(3).Info("no proxy https url will be supplied:", "error", err.Error())
1✔
680
                }
1✔
681
                podEnvVar.httpsProxy = field
1✔
682
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyNoProxy); err != nil {
2✔
683
                        r.log.V(3).Info("the noProxy field will not be supplied:", "error", err.Error())
1✔
684
                }
1✔
685
                podEnvVar.noProxy = field
1✔
686
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyConfigMapName); err != nil {
2✔
687
                        r.log.V(3).Info("no proxy CA certiticate will be supplied:", "error", err.Error())
1✔
688
                }
1✔
689
                podEnvVar.certConfigMapProxy = field
1✔
690
        }
691

692
        fsOverhead, err := GetFilesystemOverhead(context.TODO(), r.client, pvc)
1✔
693
        if err != nil {
1✔
694
                return nil, err
×
695
        }
×
696
        podEnvVar.filesystemOverhead = string(fsOverhead)
1✔
697

1✔
698
        if preallocation, err := strconv.ParseBool(getValueFromAnnotation(pvc, cc.AnnPreallocationRequested)); err == nil {
1✔
699
                podEnvVar.preallocation = preallocation
×
700
        } // else use the default "false"
×
701

702
        //get the requested image size.
703
        podEnvVar.imageSize, err = cc.GetRequestedImageSize(pvc)
1✔
704
        if err != nil {
1✔
705
                return nil, err
×
706
        }
×
707

708
        if v, ok := pvc.Annotations[cc.AnnRequiresDirectIO]; ok && v == "true" {
2✔
709
                podEnvVar.cacheMode = common.CacheModeTryNone
1✔
710
        }
1✔
711

712
        return podEnvVar, nil
1✔
713
}
714

715
func (r *ImportReconciler) isInsecureTLS(pvc *corev1.PersistentVolumeClaim, cdiConfig *cdiv1.CDIConfig) (bool, error) {
1✔
716
        // Check if insecureSkipVerify annotation is set (only applicable for ImageIO sources)
1✔
717
        source, sourceOk := pvc.Annotations[cc.AnnSource]
1✔
718
        if sourceOk && source == cc.SourceImageio {
1✔
719
                if insecureSkipVerify, ok := pvc.Annotations[cc.AnnInsecureSkipVerify]; ok && insecureSkipVerify == "true" {
×
720
                        return true, nil
×
721
                }
×
722
        }
723

724
        ep, ok := pvc.Annotations[cc.AnnEndpoint]
1✔
725
        if !ok || ep == "" {
2✔
726
                return false, nil
1✔
727
        }
1✔
728
        return IsInsecureTLS(ep, cdiConfig, r.log)
1✔
729
}
730

731
// IsInsecureTLS checks if TLS security is disabled for the given endpoint
732
func IsInsecureTLS(ep string, cdiConfig *cdiv1.CDIConfig, log logr.Logger) (bool, error) {
1✔
733
        url, err := url.Parse(ep)
1✔
734
        if err != nil {
1✔
735
                return false, err
×
736
        }
×
737

738
        if url.Scheme != "docker" {
2✔
739
                return false, nil
1✔
740
        }
1✔
741

742
        for _, value := range cdiConfig.Spec.InsecureRegistries {
2✔
743
                log.V(1).Info("Checking host against value", "host", url.Host, "value", value)
1✔
744
                if value == url.Host {
2✔
745
                        return true, nil
1✔
746
                }
1✔
747
        }
748
        return false, nil
1✔
749
}
750

751
func (r *ImportReconciler) getCertConfigMap(pvc *corev1.PersistentVolumeClaim) (string, error) {
1✔
752
        value, ok := pvc.Annotations[cc.AnnCertConfigMap]
1✔
753
        if !ok || value == "" {
2✔
754
                return "", nil
1✔
755
        }
1✔
756

757
        configMap := &corev1.ConfigMap{}
1✔
758
        if err := r.uncachedClient.Get(context.TODO(), types.NamespacedName{Name: value, Namespace: pvc.Namespace}, configMap); err != nil {
2✔
759
                if k8serrors.IsNotFound(err) {
2✔
760
                        r.log.V(1).Info("Configmap does not exist, pod will not start until it does", "configMapName", value)
1✔
761
                        return value, nil
1✔
762
                }
1✔
763

764
                return "", err
×
765
        }
766

767
        return value, nil
1✔
768
}
769

770
// returns the name of the secret containing endpoint credentials consumed by the importer pod.
771
// A value of "" implies there are no credentials for the endpoint being used. A returned error
772
// causes processNextItem() to stop.
773
func (r *ImportReconciler) getSecretName(pvc *corev1.PersistentVolumeClaim) string {
1✔
774
        ns := pvc.Namespace
1✔
775
        name, found := pvc.Annotations[cc.AnnSecret]
1✔
776
        if !found || name == "" {
2✔
777
                msg := "getEndpointSecret: "
1✔
778
                if !found {
2✔
779
                        msg += fmt.Sprintf("annotation %q is missing in pvc \"%s/%s\"", cc.AnnSecret, ns, pvc.Name)
1✔
780
                } else {
1✔
781
                        msg += fmt.Sprintf("secret name is missing from annotation %q in pvc \"%s/%s\"", cc.AnnSecret, ns, pvc.Name)
×
782
                }
×
783
                r.log.V(2).Info(msg)
1✔
784
                return "" // importer pod will not contain secret credentials
1✔
785
        }
786
        return name
1✔
787
}
788

789
func (r *ImportReconciler) requiresScratchSpace(pvc *corev1.PersistentVolumeClaim) bool {
1✔
790
        scratchRequired := false
1✔
791
        contentType := cc.GetPVCContentType(pvc)
1✔
792
        // All archive requires scratch space.
1✔
793
        if contentType == cdiv1.DataVolumeArchive {
1✔
794
                scratchRequired = true
×
795
        } else {
1✔
796
                switch cc.GetSource(pvc) {
1✔
797
                case cc.SourceGlance:
×
798
                        scratchRequired = true
×
799
                case cc.SourceImageio:
×
800
                        if val, ok := pvc.Annotations[cc.AnnCurrentCheckpoint]; ok {
×
801
                                scratchRequired = val != ""
×
802
                        }
×
803
                case cc.SourceRegistry:
1✔
804
                        scratchRequired = pvc.Annotations[cc.AnnRegistryImportMethod] != string(cdiv1.RegistryPullNode)
1✔
805
                }
806
        }
807
        value, ok := pvc.Annotations[cc.AnnRequiresScratch]
1✔
808
        if ok {
2✔
809
                boolVal, _ := strconv.ParseBool(value)
1✔
810
                scratchRequired = scratchRequired || boolVal
1✔
811
        }
1✔
812
        return scratchRequired
1✔
813
}
814

815
func (r *ImportReconciler) createScratchPvcForPod(pvc *corev1.PersistentVolumeClaim, pod *corev1.Pod) error {
1✔
816
        scratchPvc := &corev1.PersistentVolumeClaim{}
1✔
817
        scratchPVCName, exists := getScratchNameFromPod(pod)
1✔
818
        if !exists {
1✔
819
                return errors.New("Scratch Volume not configured for pod")
×
820
        }
×
821
        anno := pvc.GetAnnotations()
1✔
822
        err := r.client.Get(context.TODO(), types.NamespacedName{Namespace: pvc.GetNamespace(), Name: scratchPVCName}, scratchPvc)
1✔
823
        if cc.IgnoreNotFound(err) != nil {
1✔
824
                return err
×
825
        }
×
826
        if k8serrors.IsNotFound(err) {
2✔
827
                r.log.V(1).Info("Creating scratch space for POD and PVC", "pod.Name", pod.Name, "pvc.Name", pvc.Name)
1✔
828

1✔
829
                storageClassName := GetScratchPvcStorageClass(r.client, pvc)
1✔
830
                // Scratch PVC doesn't exist yet, create it. Determine which storage class to use.
1✔
831
                _, err = createScratchPersistentVolumeClaim(r.client, pvc, pod, scratchPVCName, storageClassName, r.installerLabels, r.recorder)
1✔
832
                if err != nil {
1✔
833
                        return err
×
834
                }
×
835
                anno[cc.AnnBoundCondition] = "false"
1✔
836
                anno[cc.AnnBoundConditionMessage] = "Creating scratch space"
1✔
837
                anno[cc.AnnBoundConditionReason] = creatingScratch
1✔
838
        } else {
×
839
                if scratchPvc.DeletionTimestamp != nil {
×
840
                        // Delete the pod since we are in a deadlock situation now. The scratch PVC from the previous import is not gone
×
841
                        // yet but terminating, and the new pod is still being created and the scratch PVC now has a finalizer on it.
×
842
                        // Only way to break it, is to delete the importer pod, and give the pvc a chance to disappear.
×
843
                        err = r.client.Delete(context.TODO(), pod)
×
844
                        if err != nil {
×
845
                                return err
×
846
                        }
×
847
                        return fmt.Errorf("terminating scratch space found, deleting pod %s", pod.Name)
×
848
                }
849
        }
850
        anno[cc.AnnRequiresScratch] = "false"
1✔
851
        return nil
1✔
852
}
853

854
// Get path to VDDK image from 'v2v-vmware' ConfigMap
855
func (r *ImportReconciler) getVddkImageName() (*string, error) {
1✔
856
        namespace := util.GetNamespace()
1✔
857

1✔
858
        cm := &corev1.ConfigMap{}
1✔
859
        err := r.uncachedClient.Get(context.TODO(), types.NamespacedName{Name: common.VddkConfigMap, Namespace: namespace}, cm)
1✔
860
        if k8serrors.IsNotFound(err) {
2✔
861
                return nil, errors.Errorf("No %s ConfigMap present in namespace %s", common.VddkConfigMap, namespace)
1✔
862
        }
1✔
863

864
        image, found := cm.Data[common.VddkConfigDataKey]
1✔
865
        if found {
2✔
866
                msg := fmt.Sprintf("Found %s ConfigMap in namespace %s, VDDK image path is: ", common.VddkConfigMap, namespace)
1✔
867
                r.log.V(1).Info(msg, common.VddkConfigDataKey, image)
1✔
868
                return &image, nil
1✔
869
        }
1✔
870

871
        return nil, errors.Errorf("found %s ConfigMap in namespace %s, but it does not contain a '%s' entry", common.VddkConfigMap, namespace, common.VddkConfigDataKey)
×
872
}
873

874
// getVddkNodeSelector reads the optional node-selector key from the VDDK extra-args
875
// ConfigMap and returns the parsed map, or nil if the key is absent.
876
func (r *ImportReconciler) getVddkNodeSelector(namespace, cmName string) (map[string]string, error) {
1✔
877
        cm := &corev1.ConfigMap{}
1✔
878
        if err := r.uncachedClient.Get(context.TODO(), types.NamespacedName{Name: cmName, Namespace: namespace}, cm); err != nil {
2✔
879
                if k8serrors.IsNotFound(err) {
2✔
880
                        return nil, nil
1✔
881
                }
1✔
NEW
882
                return nil, err
×
883
        }
884
        val, ok := cm.Data[common.VddkNodeSelectorKey]
1✔
885
        if !ok || val == "" {
2✔
886
                return nil, nil
1✔
887
        }
1✔
888
        nodeSelector := make(map[string]string)
1✔
889
        if err := json.Unmarshal([]byte(val), &nodeSelector); err != nil {
2✔
890
                return nil, fmt.Errorf("failed to parse %s key in ConfigMap %s/%s: %w",
1✔
891
                        common.VddkNodeSelectorKey, namespace, cmName, err)
1✔
892
        }
1✔
893
        return nodeSelector, nil
1✔
894
}
895

896
// returns the import image part of the endpoint string
897
func getRegistryImportImage(pvc *corev1.PersistentVolumeClaim) (string, error) {
1✔
898
        ep, err := cc.GetEndpoint(pvc)
1✔
899
        if err != nil {
1✔
900
                return "", nil
×
901
        }
×
902
        if cc.IsImageStream(pvc) {
1✔
903
                return ep, nil
×
904
        }
×
905
        url, err := url.Parse(ep)
1✔
906
        if err != nil {
1✔
907
                return "", errors.Errorf("illegal registry endpoint %s", ep)
×
908
        }
×
909
        return url.Host + url.Path, nil
1✔
910
}
911

912
// getValueFromAnnotation returns the value of an annotation
913
func getValueFromAnnotation(pvc *corev1.PersistentVolumeClaim, annotation string) string {
1✔
914
        return pvc.Annotations[annotation]
1✔
915
}
1✔
916

917
// If this pod is going to transfer one checkpoint in a multi-stage import, attach the checkpoint name to the pod name so
918
// that each checkpoint gets a unique pod. That way each pod can be inspected using the retainAfterCompletion annotation.
919
func podNameWithCheckpoint(pvc *corev1.PersistentVolumeClaim) string {
1✔
920
        if checkpoint := pvc.Annotations[cc.AnnCurrentCheckpoint]; checkpoint != "" {
2✔
921
                return pvc.Name + "-checkpoint-" + checkpoint
1✔
922
        }
1✔
923
        return pvc.Name
1✔
924
}
925

926
func getImportPodNameFromPvc(pvc *corev1.PersistentVolumeClaim) string {
1✔
927
        podName, ok := pvc.Annotations[cc.AnnImportPod]
1✔
928
        if ok {
2✔
929
                return podName
1✔
930
        }
1✔
931
        // fallback to legacy naming, in fact the following function is fully compatible with legacy
932
        // name concatenation "importer-{pvc.Name}" if the name length is under the size limits,
933
        return naming.GetResourceName(common.ImporterPodName, podNameWithCheckpoint(pvc))
1✔
934
}
935

936
func createImportPodNameFromPvc(pvc *corev1.PersistentVolumeClaim) string {
1✔
937
        return naming.GetResourceName(common.ImporterPodName, podNameWithCheckpoint(pvc))
1✔
938
}
1✔
939

940
// createImporterPod creates and returns a pointer to a pod which is created based on the passed-in endpoint, secret
941
// name, and pvc. A nil secret means the endpoint credentials are not passed to the
942
// importer pod.
943
func createImporterPod(ctx context.Context, log logr.Logger, client client.Client, args *importerPodArgs, installerLabels map[string]string) (*corev1.Pod, error) {
1✔
944
        var err error
1✔
945
        args.podResourceRequirements, err = cc.GetDefaultPodResourceRequirements(client)
1✔
946
        if err != nil {
1✔
947
                return nil, err
×
948
        }
×
949

950
        args.imagePullSecrets, err = cc.GetImagePullSecrets(client)
1✔
951
        if err != nil {
1✔
952
                return nil, err
×
953
        }
×
954

955
        args.workloadNodePlacement, err = cc.GetWorkloadNodePlacement(ctx, client)
1✔
956
        if err != nil {
1✔
957
                return nil, err
×
958
        }
×
959

960
        if isRegistryNodeImport(args) {
2✔
961
                args.importImage, err = getRegistryImportImage(args.pvc)
1✔
962
                if err != nil {
1✔
963
                        return nil, err
×
964
                }
×
965
                setRegistryNodeImportEnvVars(args)
1✔
966
                if args.podEnvVar.registryImageArchitecture != "" {
1✔
967
                        setRegistryNodeImportNodeSelector(args)
×
968
                }
×
969
        }
970

971
        pod := makeImporterPodSpec(args)
1✔
972

1✔
973
        util.SetRecommendedLabels(pod, installerLabels, "cdi-controller")
1✔
974

1✔
975
        srcLabels := args.pvc.Labels
1✔
976
        if _, isPopulator := args.pvc.Annotations[cc.AnnPopulatorKind]; isPopulator {
2✔
977
                ownerRef := metav1.GetControllerOf(args.pvc)
1✔
978
                if ownerRef == nil || ownerRef.Kind != "PersistentVolumeClaim" {
1✔
979
                        return nil, fmt.Errorf("pvc %s/%s does not have a valid owner reference", args.pvc.Namespace, args.pvc.Name)
×
980
                }
×
981

982
                pvc := &corev1.PersistentVolumeClaim{}
1✔
983
                if err := client.Get(context.TODO(), types.NamespacedName{Namespace: args.pvc.Namespace, Name: ownerRef.Name}, pvc); err != nil {
1✔
984
                        return nil, err
×
985
                }
×
986
                srcLabels = pvc.GetLabels()
1✔
987
        }
988

989
        // add any labels from pvc to the importer pod
990
        util.MergeLabels(srcLabels, pod.Labels)
1✔
991

1✔
992
        if err = client.Create(context.TODO(), pod); err != nil {
1✔
993
                return nil, err
×
994
        }
×
995

996
        log.V(3).Info("importer pod created\n", "pod.Name", pod.Name, "pod.Namespace", pod.Namespace, "image name", args.image)
1✔
997
        return pod, nil
1✔
998
}
999

1000
// makeImporterPodSpec creates and return the importer pod spec based on the passed-in endpoint, secret and pvc.
1001
func makeImporterPodSpec(args *importerPodArgs) *corev1.Pod {
1✔
1002
        // importer pod name contains the pvc name
1✔
1003
        podName := args.pvc.Annotations[cc.AnnImportPod]
1✔
1004

1✔
1005
        pod := &corev1.Pod{
1✔
1006
                TypeMeta: metav1.TypeMeta{
1✔
1007
                        Kind:       "Pod",
1✔
1008
                        APIVersion: "v1",
1✔
1009
                },
1✔
1010
                ObjectMeta: metav1.ObjectMeta{
1✔
1011
                        Name:      podName,
1✔
1012
                        Namespace: args.pvc.Namespace,
1✔
1013
                        Annotations: map[string]string{
1✔
1014
                                cc.AnnCreatedBy: "yes",
1✔
1015
                        },
1✔
1016
                        Labels: map[string]string{
1✔
1017
                                common.CDILabelKey:        common.CDILabelValue,
1✔
1018
                                common.CDIComponentLabel:  common.ImporterPodName,
1✔
1019
                                common.PrometheusLabelKey: common.PrometheusLabelValue,
1✔
1020
                        },
1✔
1021
                        OwnerReferences: []metav1.OwnerReference{
1✔
1022
                                {
1✔
1023
                                        APIVersion:         "v1",
1✔
1024
                                        Kind:               "PersistentVolumeClaim",
1✔
1025
                                        Name:               args.pvc.Name,
1✔
1026
                                        UID:                args.pvc.GetUID(),
1✔
1027
                                        BlockOwnerDeletion: ptr.To[bool](true),
1✔
1028
                                        Controller:         ptr.To[bool](true),
1✔
1029
                                },
1✔
1030
                        },
1✔
1031
                },
1✔
1032
                Spec: corev1.PodSpec{
1✔
1033
                        Containers:         makeImporterContainerSpec(args),
1✔
1034
                        InitContainers:     makeImporterInitContainersSpec(args),
1✔
1035
                        Volumes:            makeImporterVolumeSpec(args),
1✔
1036
                        RestartPolicy:      corev1.RestartPolicyOnFailure,
1✔
1037
                        NodeSelector:       args.workloadNodePlacement.NodeSelector,
1✔
1038
                        Tolerations:        args.workloadNodePlacement.Tolerations,
1✔
1039
                        Affinity:           args.workloadNodePlacement.Affinity,
1✔
1040
                        PriorityClassName:  args.priorityClassName,
1✔
1041
                        ServiceAccountName: args.serviceAccountName,
1✔
1042
                        ImagePullSecrets:   args.imagePullSecrets,
1✔
1043
                        // https://kubernetes.io/docs/concepts/services-networking/service/#environment-variables
1✔
1044
                        // Disable service environment variable injection to avoid 'argument list too long'
1✔
1045
                        // errors in namespaces with many Services (each injects ~7 env vars).
1✔
1046
                        EnableServiceLinks: ptr.To(false),
1✔
1047
                },
1✔
1048
        }
1✔
1049

1✔
1050
        if len(args.vddkNodeSelector) > 0 {
2✔
1051
                if pod.Spec.NodeSelector == nil {
1✔
NEW
1052
                        pod.Spec.NodeSelector = make(map[string]string, len(args.vddkNodeSelector))
×
NEW
1053
                }
×
1054
                for k, v := range args.vddkNodeSelector {
2✔
1055
                        pod.Spec.NodeSelector[k] = v
1✔
1056
                }
1✔
1057
        }
1058

1059
        /**
1060
        FIXME: When registry source is ImageStream, if we set importer pod OwnerReference (to its pvc, like all other cases),
1061
        for some reason (OCP issue?) we get the following error:
1062
                Failed to pull image "imagestream-name": rpc error: code = Unknown
1063
                desc = Error reading manifest latest in docker.io/library/imagestream-name: errors:
1064
                denied: requested access to the resource is denied
1065
                unauthorized: authentication required
1066
        When we don't set pod OwnerReferences, all works well.
1067
        */
1068
        if isRegistryNodeImport(args) && cc.IsImageStream(args.pvc) {
1✔
1069
                pod.OwnerReferences = nil
×
1070
                pod.Annotations[cc.AnnOpenShiftImageLookup] = "*"
×
1071
        }
×
1072

1073
        cc.CopyAllowedAnnotations(args.pvc, pod)
1✔
1074
        cc.SetRestrictedSecurityContext(&pod.Spec)
1✔
1075
        // We explicitly define a NodeName for dynamically provisioned PVCs
1✔
1076
        // when the PVC is being handled by a populator (PVC')
1✔
1077
        cc.SetNodeNameIfPopulator(args.pvc, &pod.Spec)
1✔
1078

1✔
1079
        return pod
1✔
1080
}
1081

1082
func makeImporterContainerSpec(args *importerPodArgs) []corev1.Container {
1✔
1083
        containers := []corev1.Container{
1✔
1084
                {
1✔
1085
                        Name:            common.ImporterPodName,
1✔
1086
                        Image:           args.image,
1✔
1087
                        ImagePullPolicy: corev1.PullPolicy(args.pullPolicy),
1✔
1088
                        Args:            []string{"-v=" + args.verbose},
1✔
1089
                        Env:             makeImportEnv(args.podEnvVar, getOwnerUID(args)),
1✔
1090
                        Ports: []corev1.ContainerPort{
1✔
1091
                                {
1✔
1092
                                        Name:          "metrics",
1✔
1093
                                        ContainerPort: 8443,
1✔
1094
                                        Protocol:      corev1.ProtocolTCP,
1✔
1095
                                },
1✔
1096
                        },
1✔
1097
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1098
                },
1✔
1099
        }
1✔
1100
        if cc.GetVolumeMode(args.pvc) == corev1.PersistentVolumeBlock {
2✔
1101
                containers[0].VolumeDevices = cc.AddVolumeDevices()
1✔
1102
        } else {
2✔
1103
                containers[0].VolumeMounts = cc.AddImportVolumeMounts()
1✔
1104
        }
1✔
1105
        if isRegistryNodeImport(args) {
2✔
1106
                containers = append(containers, corev1.Container{
1✔
1107
                        Name:            "server",
1✔
1108
                        Image:           args.importImage,
1✔
1109
                        ImagePullPolicy: corev1.PullPolicy(args.pullPolicy),
1✔
1110
                        Command:         []string{"/shared/server", "-p", "8100", "-image-dir", "/disk", "-ready-file", "/shared/ready", "-done-file", "/shared/done"},
1✔
1111
                        VolumeMounts: []corev1.VolumeMount{
1✔
1112
                                {
1✔
1113
                                        MountPath: "/shared",
1✔
1114
                                        Name:      "shared-volume",
1✔
1115
                                },
1✔
1116
                        },
1✔
1117
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1118
                })
1✔
1119
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1120
                        MountPath: "/shared",
1✔
1121
                        Name:      "shared-volume",
1✔
1122
                })
1✔
1123
        }
1✔
1124
        if args.scratchPvcName != nil {
2✔
1125
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1126
                        Name:      cc.ScratchVolName,
1✔
1127
                        MountPath: common.ScratchDataDir,
1✔
1128
                })
1✔
1129
        }
1✔
1130
        if args.vddkImageName != nil {
2✔
1131
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1132
                        Name:      "vddk-vol-mount",
1✔
1133
                        MountPath: "/opt",
1✔
1134
                })
1✔
1135
        }
1✔
1136
        if args.vddkExtraArgs != nil {
2✔
1137
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1138
                        Name:      common.VddkArgsVolName,
1✔
1139
                        MountPath: common.VddkArgsDir,
1✔
1140
                })
1✔
1141
        }
1✔
1142
        if args.podEnvVar.certConfigMap != "" {
1✔
1143
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
1144
                        Name:      CertVolName,
×
1145
                        MountPath: common.ImporterCertDir,
×
1146
                })
×
1147
        }
×
1148
        if args.podEnvVar.certConfigMapProxy != "" {
1✔
1149
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
1150
                        Name:      ProxyCertVolName,
×
1151
                        MountPath: common.ImporterProxyCertDir,
×
1152
                })
×
1153
        }
×
1154
        if args.podEnvVar.source == cc.SourceGCS && args.podEnvVar.secretName != "" {
1✔
1155
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
1156
                        Name:      SecretVolName,
×
1157
                        MountPath: common.ImporterGoogleCredentialDir,
×
1158
                })
×
1159
        }
×
1160
        for index := range args.podEnvVar.secretExtraHeaders {
1✔
1161
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
1162
                        Name:      fmt.Sprintf(secretExtraHeadersVolumeName, index),
×
1163
                        MountPath: path.Join(common.ImporterSecretExtraHeadersDir, fmt.Sprint(index)),
×
1164
                })
×
1165
        }
×
1166
        if args.podResourceRequirements != nil {
1✔
1167
                for i := range containers {
×
1168
                        containers[i].Resources = *args.podResourceRequirements
×
1169
                }
×
1170
        }
1171
        return containers
1✔
1172
}
1173

1174
func makeImporterVolumeSpec(args *importerPodArgs) []corev1.Volume {
1✔
1175
        volumes := []corev1.Volume{
1✔
1176
                {
1✔
1177
                        Name: cc.DataVolName,
1✔
1178
                        VolumeSource: corev1.VolumeSource{
1✔
1179
                                PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
1✔
1180
                                        ClaimName: args.pvc.Name,
1✔
1181
                                        ReadOnly:  false,
1✔
1182
                                },
1✔
1183
                        },
1✔
1184
                },
1✔
1185
        }
1✔
1186
        if isRegistryNodeImport(args) {
2✔
1187
                volumes = append(volumes, corev1.Volume{
1✔
1188
                        Name: "shared-volume",
1✔
1189
                        VolumeSource: corev1.VolumeSource{
1✔
1190
                                EmptyDir: &corev1.EmptyDirVolumeSource{},
1✔
1191
                        },
1✔
1192
                })
1✔
1193
        }
1✔
1194
        if args.scratchPvcName != nil {
2✔
1195
                volumes = append(volumes, corev1.Volume{
1✔
1196
                        Name: cc.ScratchVolName,
1✔
1197
                        VolumeSource: corev1.VolumeSource{
1✔
1198
                                PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
1✔
1199
                                        ClaimName: *args.scratchPvcName,
1✔
1200
                                        ReadOnly:  false,
1✔
1201
                                },
1✔
1202
                        },
1✔
1203
                })
1✔
1204
        }
1✔
1205
        if args.vddkImageName != nil {
2✔
1206
                volumes = append(volumes, corev1.Volume{
1✔
1207
                        Name: "vddk-vol-mount",
1✔
1208
                        VolumeSource: corev1.VolumeSource{
1✔
1209
                                EmptyDir: &corev1.EmptyDirVolumeSource{},
1✔
1210
                        },
1✔
1211
                })
1✔
1212
        }
1✔
1213
        if args.vddkExtraArgs != nil {
2✔
1214
                volumes = append(volumes, corev1.Volume{
1✔
1215
                        Name: common.VddkArgsVolName,
1✔
1216
                        VolumeSource: corev1.VolumeSource{
1✔
1217
                                ConfigMap: &v1.ConfigMapVolumeSource{
1✔
1218
                                        LocalObjectReference: v1.LocalObjectReference{
1✔
1219
                                                Name: *args.vddkExtraArgs,
1✔
1220
                                        },
1✔
1221
                                },
1✔
1222
                        },
1✔
1223
                })
1✔
1224
        }
1✔
1225
        if args.podEnvVar.certConfigMap != "" {
1✔
1226
                volumes = append(volumes, createConfigMapVolume(CertVolName, args.podEnvVar.certConfigMap))
×
1227
        }
×
1228
        if args.podEnvVar.certConfigMapProxy != "" {
1✔
1229
                volumes = append(volumes, createConfigMapVolume(ProxyCertVolName, GetImportProxyConfigMapName(args.pvc.Name)))
×
1230
        }
×
1231
        if args.podEnvVar.source == cc.SourceGCS && args.podEnvVar.secretName != "" {
1✔
1232
                volumes = append(volumes, createSecretVolume(SecretVolName, args.podEnvVar.secretName))
×
1233
        }
×
1234
        for index, header := range args.podEnvVar.secretExtraHeaders {
1✔
1235
                volumes = append(volumes, corev1.Volume{
×
1236
                        Name: fmt.Sprintf(secretExtraHeadersVolumeName, index),
×
1237
                        VolumeSource: corev1.VolumeSource{
×
1238
                                Secret: &corev1.SecretVolumeSource{
×
1239
                                        SecretName: header,
×
1240
                                },
×
1241
                        },
×
1242
                })
×
1243
        }
×
1244
        return volumes
1✔
1245
}
1246

1247
func makeImporterInitContainersSpec(args *importerPodArgs) []corev1.Container {
1✔
1248
        var initContainers []corev1.Container
1✔
1249
        if isRegistryNodeImport(args) {
2✔
1250
                initContainers = append(initContainers, corev1.Container{
1✔
1251
                        Name:            "init",
1✔
1252
                        Image:           args.image,
1✔
1253
                        ImagePullPolicy: corev1.PullPolicy(args.pullPolicy),
1✔
1254
                        Command:         []string{"sh", "-c", "cp /usr/bin/cdi-containerimage-server /shared/server"},
1✔
1255
                        VolumeMounts: []corev1.VolumeMount{
1✔
1256
                                {
1✔
1257
                                        MountPath: "/shared",
1✔
1258
                                        Name:      "shared-volume",
1✔
1259
                                },
1✔
1260
                        },
1✔
1261
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1262
                })
1✔
1263
        }
1✔
1264
        if args.vddkImageName != nil {
2✔
1265
                initContainers = append(initContainers, corev1.Container{
1✔
1266
                        Name:  "vddk-side-car",
1✔
1267
                        Image: *args.vddkImageName,
1✔
1268
                        VolumeMounts: []corev1.VolumeMount{
1✔
1269
                                {
1✔
1270
                                        Name:      "vddk-vol-mount",
1✔
1271
                                        MountPath: "/opt",
1✔
1272
                                },
1✔
1273
                        },
1✔
1274
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1275
                })
1✔
1276
        }
1✔
1277
        if args.podResourceRequirements != nil {
1✔
1278
                for i := range initContainers {
×
1279
                        initContainers[i].Resources = *args.podResourceRequirements
×
1280
                }
×
1281
        }
1282
        return initContainers
1✔
1283
}
1284

1285
func isRegistryNodeImport(args *importerPodArgs) bool {
1✔
1286
        return cc.GetSource(args.pvc) == cc.SourceRegistry &&
1✔
1287
                args.pvc.Annotations[cc.AnnRegistryImportMethod] == string(cdiv1.RegistryPullNode)
1✔
1288
}
1✔
1289

1290
func getOwnerUID(args *importerPodArgs) types.UID {
1✔
1291
        if len(args.pvc.OwnerReferences) == 1 {
2✔
1292
                return args.pvc.OwnerReferences[0].UID
1✔
1293
        }
1✔
1294
        return args.pvc.UID
1✔
1295
}
1296

1297
func setRegistryNodeImportEnvVars(args *importerPodArgs) {
1✔
1298
        args.podEnvVar.source = cc.SourceHTTP
1✔
1299
        args.podEnvVar.ep = "http://localhost:8100/disk.img"
1✔
1300
        args.podEnvVar.pullMethod = string(cdiv1.RegistryPullNode)
1✔
1301
        args.podEnvVar.readyFile = "/shared/ready"
1✔
1302
        args.podEnvVar.doneFile = "/shared/done"
1✔
1303
}
1✔
1304

1305
func setRegistryNodeImportNodeSelector(args *importerPodArgs) {
×
1306
        if args.workloadNodePlacement.NodeSelector == nil {
×
1307
                args.workloadNodePlacement.NodeSelector = make(map[string]string, 0)
×
1308
        }
×
1309
        args.workloadNodePlacement.NodeSelector[v1.LabelArchStable] = args.podEnvVar.registryImageArchitecture
×
1310
}
1311

1312
func createConfigMapVolume(certVolName, objRef string) corev1.Volume {
1✔
1313
        return corev1.Volume{
1✔
1314
                Name: certVolName,
1✔
1315
                VolumeSource: corev1.VolumeSource{
1✔
1316
                        ConfigMap: &corev1.ConfigMapVolumeSource{
1✔
1317
                                LocalObjectReference: corev1.LocalObjectReference{
1✔
1318
                                        Name: objRef,
1✔
1319
                                },
1✔
1320
                        },
1✔
1321
                },
1✔
1322
        }
1✔
1323
}
1✔
1324

1325
func createSecretVolume(thisVolName, objRef string) corev1.Volume {
×
1326
        return corev1.Volume{
×
1327
                Name: thisVolName,
×
1328
                VolumeSource: corev1.VolumeSource{
×
1329
                        Secret: &corev1.SecretVolumeSource{
×
1330
                                SecretName: objRef,
×
1331
                        },
×
1332
                },
×
1333
        }
×
1334
}
×
1335

1336
// return the Env portion for the importer container.
1337
func makeImportEnv(podEnvVar *importPodEnvVar, uid types.UID) []corev1.EnvVar {
1✔
1338
        env := []corev1.EnvVar{
1✔
1339
                {
1✔
1340
                        Name:  common.ImporterSource,
1✔
1341
                        Value: podEnvVar.source,
1✔
1342
                },
1✔
1343
                {
1✔
1344
                        Name:  common.ImporterEndpoint,
1✔
1345
                        Value: podEnvVar.ep,
1✔
1346
                },
1✔
1347
                {
1✔
1348
                        Name:  common.ImporterContentType,
1✔
1349
                        Value: podEnvVar.contentType,
1✔
1350
                },
1✔
1351
                {
1✔
1352
                        Name:  common.ImporterImageSize,
1✔
1353
                        Value: podEnvVar.imageSize,
1✔
1354
                },
1✔
1355
                {
1✔
1356
                        Name:  common.OwnerUID,
1✔
1357
                        Value: string(uid),
1✔
1358
                },
1✔
1359
                {
1✔
1360
                        Name:  common.FilesystemOverheadVar,
1✔
1361
                        Value: podEnvVar.filesystemOverhead,
1✔
1362
                },
1✔
1363
                {
1✔
1364
                        Name:  common.InsecureTLSVar,
1✔
1365
                        Value: strconv.FormatBool(podEnvVar.insecureTLS),
1✔
1366
                },
1✔
1367
                {
1✔
1368
                        Name:  common.ImporterDiskID,
1✔
1369
                        Value: podEnvVar.diskID,
1✔
1370
                },
1✔
1371
                {
1✔
1372
                        Name:  common.ImporterUUID,
1✔
1373
                        Value: podEnvVar.uuid,
1✔
1374
                },
1✔
1375
                {
1✔
1376
                        Name:  common.ImporterPullMethod,
1✔
1377
                        Value: podEnvVar.pullMethod,
1✔
1378
                },
1✔
1379
                {
1✔
1380
                        Name:  common.ImporterReadyFile,
1✔
1381
                        Value: podEnvVar.readyFile,
1✔
1382
                },
1✔
1383
                {
1✔
1384
                        Name:  common.ImporterDoneFile,
1✔
1385
                        Value: podEnvVar.doneFile,
1✔
1386
                },
1✔
1387
                {
1✔
1388
                        Name:  common.ImporterBackingFile,
1✔
1389
                        Value: podEnvVar.backingFile,
1✔
1390
                },
1✔
1391
                {
1✔
1392
                        Name:  common.ImporterThumbprint,
1✔
1393
                        Value: podEnvVar.thumbprint,
1✔
1394
                },
1✔
1395
                {
1✔
1396
                        Name:  common.ImportProxyHTTP,
1✔
1397
                        Value: podEnvVar.httpProxy,
1✔
1398
                },
1✔
1399
                {
1✔
1400
                        Name:  common.ImportProxyHTTPS,
1✔
1401
                        Value: podEnvVar.httpsProxy,
1✔
1402
                },
1✔
1403
                {
1✔
1404
                        Name:  common.ImportProxyNoProxy,
1✔
1405
                        Value: podEnvVar.noProxy,
1✔
1406
                },
1✔
1407
                {
1✔
1408
                        Name:  common.ImporterCurrentCheckpoint,
1✔
1409
                        Value: podEnvVar.currentCheckpoint,
1✔
1410
                },
1✔
1411
                {
1✔
1412
                        Name:  common.ImporterPreviousCheckpoint,
1✔
1413
                        Value: podEnvVar.previousCheckpoint,
1✔
1414
                },
1✔
1415
                {
1✔
1416
                        Name:  common.ImporterFinalCheckpoint,
1✔
1417
                        Value: podEnvVar.finalCheckpoint,
1✔
1418
                },
1✔
1419
                {
1✔
1420
                        Name:  common.Preallocation,
1✔
1421
                        Value: strconv.FormatBool(podEnvVar.preallocation),
1✔
1422
                },
1✔
1423
                {
1✔
1424
                        Name:  common.CacheMode,
1✔
1425
                        Value: podEnvVar.cacheMode,
1✔
1426
                },
1✔
1427
                {
1✔
1428
                        Name:  common.ImporterRegistryImageArchitecture,
1✔
1429
                        Value: podEnvVar.registryImageArchitecture,
1✔
1430
                },
1✔
1431
                {
1✔
1432
                        Name:  common.ImporterChecksum,
1✔
1433
                        Value: podEnvVar.checksum,
1✔
1434
                },
1✔
1435
        }
1✔
1436
        if podEnvVar.secretName != "" && podEnvVar.source != cc.SourceGCS {
1✔
1437
                env = append(env, corev1.EnvVar{
×
1438
                        Name: common.ImporterAccessKeyID,
×
1439
                        ValueFrom: &corev1.EnvVarSource{
×
1440
                                SecretKeyRef: &corev1.SecretKeySelector{
×
1441
                                        LocalObjectReference: corev1.LocalObjectReference{
×
1442
                                                Name: podEnvVar.secretName,
×
1443
                                        },
×
1444
                                        Key: common.KeyAccess,
×
1445
                                },
×
1446
                        },
×
1447
                }, corev1.EnvVar{
×
1448
                        Name: common.ImporterSecretKey,
×
1449
                        ValueFrom: &corev1.EnvVarSource{
×
1450
                                SecretKeyRef: &corev1.SecretKeySelector{
×
1451
                                        LocalObjectReference: corev1.LocalObjectReference{
×
1452
                                                Name: podEnvVar.secretName,
×
1453
                                        },
×
1454
                                        Key: common.KeySecret,
×
1455
                                },
×
1456
                        },
×
1457
                })
×
1458
        }
×
1459
        if podEnvVar.secretName != "" && podEnvVar.source == cc.SourceGCS {
1✔
1460
                env = append(env, corev1.EnvVar{
×
1461
                        Name:  common.ImporterGoogleCredentialFileVar,
×
1462
                        Value: common.ImporterGoogleCredentialFile,
×
1463
                })
×
1464
        }
×
1465
        if podEnvVar.certConfigMap != "" {
1✔
1466
                env = append(env, corev1.EnvVar{
×
1467
                        Name:  common.ImporterCertDirVar,
×
1468
                        Value: common.ImporterCertDir,
×
1469
                })
×
1470
        }
×
1471
        if podEnvVar.certConfigMapProxy != "" {
1✔
1472
                env = append(env, corev1.EnvVar{
×
1473
                        Name:  common.ImporterProxyCertDirVar,
×
1474
                        Value: common.ImporterProxyCertDir,
×
1475
                })
×
1476
        }
×
1477
        for index, header := range podEnvVar.extraHeaders {
1✔
1478
                env = append(env, corev1.EnvVar{
×
1479
                        Name:  fmt.Sprintf("%s%d", common.ImporterExtraHeader, index),
×
1480
                        Value: header,
×
1481
                })
×
1482
        }
×
1483
        return env
1✔
1484
}
1485

1486
func isOOMKilled(status v1.ContainerStatus) bool {
1✔
1487
        if terminated := status.State.Terminated; terminated != nil {
2✔
1488
                if terminated.Reason == cc.OOMKilledReason {
2✔
1489
                        return true
1✔
1490
                }
1✔
1491
        }
1492
        if terminated := status.LastTerminationState.Terminated; terminated != nil {
2✔
1493
                if terminated.Reason == cc.OOMKilledReason {
1✔
1494
                        return true
×
1495
                }
×
1496
        }
1497

1498
        return false
1✔
1499
}
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