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

kubevirt / containerized-data-importer / #5784

21 Jan 2026 04:20AM UTC coverage: 49.631% (+0.2%) from 49.439%
#5784

Pull #4010

travis-ci

halfcrazy
fix review

Signed-off-by: Yan Zhu <hackzhuyan@gmail.com>
Pull Request #4010: feat: Add checksum validation for HTTP/HTTPS DataVolume sources

154 of 212 new or added lines in 14 files covered. (72.64%)

520 existing lines in 2 files now uncovered.

14781 of 29782 relevant lines covered (49.63%)

0.56 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

171
        return nil
×
172
}
173

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

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

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

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

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

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

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

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

229
        return r.reconcilePvc(pvc, log)
1✔
230
}
231

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

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

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

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

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

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

1✔
312
                return reconcile.Result{RequeueAfter: 2 * time.Second}, nil
1✔
313
        }
1✔
314
        return reconcile.Result{}, nil
1✔
315
}
316

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

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

356
func (r *ImportReconciler) initPvcPodName(pvc *corev1.PersistentVolumeClaim, log logr.Logger) error {
1✔
357
        currentPvcCopy := pvc.DeepCopyObject()
1✔
358

1✔
359
        log.V(1).Info("Init pod name on PVC")
1✔
360
        anno := pvc.GetAnnotations()
1✔
361

1✔
362
        anno[cc.AnnImportPod] = createImportPodNameFromPvc(pvc)
1✔
363

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

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

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

1✔
382
        log.V(1).Info("Updating PVC from pod")
1✔
383
        anno := pvc.GetAnnotations()
1✔
384

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

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

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

1✔
413
        if statuses := pod.Status.ContainerStatuses; len(statuses) > 0 {
2✔
414
                if isOOMKilled(statuses[0]) {
2✔
415
                        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✔
416
                        podModificationsNeeded = true
1✔
417
                        anno[cc.AnnRequiresDirectIO] = "true"
1✔
418
                }
1✔
419

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

426
        if anno[cc.AnnCurrentCheckpoint] != "" {
1✔
427
                anno[cc.AnnCurrentPodID] = string(pod.ObjectMeta.UID)
×
428
        }
×
429

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

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

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

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

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

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

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

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

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

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

528
func (r *ImportReconciler) createImporterPod(pvc *corev1.PersistentVolumeClaim) error {
1✔
529
        r.log.V(1).Info("Creating importer POD for PVC", "pvc.Name", pvc.Name)
1✔
530
        var scratchPvcName *string
1✔
531
        var vddkImageName *string
1✔
532
        var vddkExtraArgs *string
1✔
533
        var err error
1✔
534

1✔
535
        requiresScratch := r.requiresScratchSpace(pvc)
1✔
536
        if requiresScratch {
1✔
537
                name := createScratchNameFromPvc(pvc)
×
538
                scratchPvcName = &name
×
539
        }
×
540

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

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

568
        podEnvVar, err := r.createImportEnvVar(pvc)
1✔
569
        if err != nil {
1✔
570
                return err
×
571
        }
×
572
        // all checks passed, let's create the importer pod!
573
        podArgs := &importerPodArgs{
1✔
574
                image:             r.image,
1✔
575
                verbose:           r.verbose,
1✔
576
                pullPolicy:        r.pullPolicy,
1✔
577
                podEnvVar:         podEnvVar,
1✔
578
                pvc:               pvc,
1✔
579
                scratchPvcName:    scratchPvcName,
1✔
580
                vddkImageName:     vddkImageName,
1✔
581
                vddkExtraArgs:     vddkExtraArgs,
1✔
582
                priorityClassName: cc.GetPriorityClass(pvc),
1✔
583
        }
1✔
584

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

591
        r.log.V(1).Info("Created POD", "pod.Name", pod.Name)
1✔
592

1✔
593
        // If importing from image stream, add finalizer. Note we don't watch the importer pod in this case,
1✔
594
        // so to prevent a deadlock we add finalizer only if the pod is not retained after completion.
1✔
595
        if cc.IsImageStream(pvc) && pvc.GetAnnotations()[cc.AnnPodRetainAfterCompletion] != "true" {
1✔
596
                cc.AddFinalizer(pvc, importPodImageStreamFinalizer)
×
597
                if err := r.updatePVC(pvc, r.log); err != nil {
×
598
                        return err
×
599
                }
×
600
        }
601

602
        if requiresScratch {
1✔
603
                r.log.V(1).Info("Pod requires scratch space")
×
604
                return r.createScratchPvcForPod(pvc, pod)
×
605
        }
×
606

607
        return nil
1✔
608
}
609

610
func createScratchNameFromPvc(pvc *v1.PersistentVolumeClaim) string {
×
611
        return naming.GetResourceName(pvc.Name, common.ScratchNameSuffix)
×
612
}
×
613

614
func (r *ImportReconciler) createImportEnvVar(pvc *corev1.PersistentVolumeClaim) (*importPodEnvVar, error) {
1✔
615
        podEnvVar := &importPodEnvVar{}
1✔
616
        podEnvVar.source = cc.GetSource(pvc)
1✔
617
        podEnvVar.contentType = string(cc.GetPVCContentType(pvc))
1✔
618

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

1✔
653
                for annotation, value := range pvc.Annotations {
2✔
654
                        if strings.HasPrefix(annotation, cc.AnnExtraHeaders) {
1✔
655
                                podEnvVar.extraHeaders = append(podEnvVar.extraHeaders, value)
×
656
                        }
×
657
                        if strings.HasPrefix(annotation, cc.AnnSecretExtraHeaders) {
1✔
658
                                podEnvVar.secretExtraHeaders = append(podEnvVar.secretExtraHeaders, value)
×
659
                        }
×
660
                }
661

662
                var field string
1✔
663
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyHTTP); err != nil {
2✔
664
                        r.log.V(3).Info("no proxy http url will be supplied:", "error", err.Error())
1✔
665
                }
1✔
666
                podEnvVar.httpProxy = field
1✔
667
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyHTTPS); err != nil {
2✔
668
                        r.log.V(3).Info("no proxy https url will be supplied:", "error", err.Error())
1✔
669
                }
1✔
670
                podEnvVar.httpsProxy = field
1✔
671
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyNoProxy); err != nil {
2✔
672
                        r.log.V(3).Info("the noProxy field will not be supplied:", "error", err.Error())
1✔
673
                }
1✔
674
                podEnvVar.noProxy = field
1✔
675
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyConfigMapName); err != nil {
2✔
676
                        r.log.V(3).Info("no proxy CA certiticate will be supplied:", "error", err.Error())
1✔
677
                }
1✔
678
                podEnvVar.certConfigMapProxy = field
1✔
679
        }
680

681
        fsOverhead, err := GetFilesystemOverhead(context.TODO(), r.client, pvc)
1✔
682
        if err != nil {
1✔
683
                return nil, err
×
684
        }
×
685
        podEnvVar.filesystemOverhead = string(fsOverhead)
1✔
686

1✔
687
        if preallocation, err := strconv.ParseBool(getValueFromAnnotation(pvc, cc.AnnPreallocationRequested)); err == nil {
1✔
688
                podEnvVar.preallocation = preallocation
×
689
        } // else use the default "false"
×
690

691
        //get the requested image size.
692
        podEnvVar.imageSize, err = cc.GetRequestedImageSize(pvc)
1✔
693
        if err != nil {
1✔
694
                return nil, err
×
695
        }
×
696

697
        if v, ok := pvc.Annotations[cc.AnnRequiresDirectIO]; ok && v == "true" {
2✔
698
                podEnvVar.cacheMode = common.CacheModeTryNone
1✔
699
        }
1✔
700

701
        return podEnvVar, nil
1✔
702
}
703

704
func (r *ImportReconciler) isInsecureTLS(pvc *corev1.PersistentVolumeClaim, cdiConfig *cdiv1.CDIConfig) (bool, error) {
1✔
705
        // Check if insecureSkipVerify annotation is set (only applicable for ImageIO sources)
1✔
706
        source, sourceOk := pvc.Annotations[cc.AnnSource]
1✔
707
        if sourceOk && source == cc.SourceImageio {
1✔
708
                if insecureSkipVerify, ok := pvc.Annotations[cc.AnnInsecureSkipVerify]; ok && insecureSkipVerify == "true" {
×
709
                        return true, nil
×
710
                }
×
711
        }
712

713
        ep, ok := pvc.Annotations[cc.AnnEndpoint]
1✔
714
        if !ok || ep == "" {
2✔
715
                return false, nil
1✔
716
        }
1✔
717
        return IsInsecureTLS(ep, cdiConfig, r.log)
1✔
718
}
719

720
// IsInsecureTLS checks if TLS security is disabled for the given endpoint
721
func IsInsecureTLS(ep string, cdiConfig *cdiv1.CDIConfig, log logr.Logger) (bool, error) {
1✔
722
        url, err := url.Parse(ep)
1✔
723
        if err != nil {
1✔
724
                return false, err
×
725
        }
×
726

727
        if url.Scheme != "docker" {
2✔
728
                return false, nil
1✔
729
        }
1✔
730

731
        for _, value := range cdiConfig.Spec.InsecureRegistries {
2✔
732
                log.V(1).Info("Checking host against value", "host", url.Host, "value", value)
1✔
733
                if value == url.Host {
2✔
734
                        return true, nil
1✔
735
                }
1✔
736
        }
737
        return false, nil
1✔
738
}
739

740
func (r *ImportReconciler) getCertConfigMap(pvc *corev1.PersistentVolumeClaim) (string, error) {
1✔
741
        value, ok := pvc.Annotations[cc.AnnCertConfigMap]
1✔
742
        if !ok || value == "" {
2✔
743
                return "", nil
1✔
744
        }
1✔
745

746
        configMap := &corev1.ConfigMap{}
1✔
747
        if err := r.uncachedClient.Get(context.TODO(), types.NamespacedName{Name: value, Namespace: pvc.Namespace}, configMap); err != nil {
2✔
748
                if k8serrors.IsNotFound(err) {
2✔
749
                        r.log.V(1).Info("Configmap does not exist, pod will not start until it does", "configMapName", value)
1✔
750
                        return value, nil
1✔
751
                }
1✔
752

753
                return "", err
×
754
        }
755

756
        return value, nil
1✔
757
}
758

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

778
func (r *ImportReconciler) requiresScratchSpace(pvc *corev1.PersistentVolumeClaim) bool {
1✔
779
        scratchRequired := false
1✔
780
        contentType := cc.GetPVCContentType(pvc)
1✔
781
        // All archive requires scratch space.
1✔
782
        if contentType == cdiv1.DataVolumeArchive {
1✔
783
                scratchRequired = true
×
784
        } else {
1✔
785
                switch cc.GetSource(pvc) {
1✔
786
                case cc.SourceGlance:
×
787
                        scratchRequired = true
×
788
                case cc.SourceImageio:
×
789
                        if val, ok := pvc.Annotations[cc.AnnCurrentCheckpoint]; ok {
×
790
                                scratchRequired = val != ""
×
791
                        }
×
792
                case cc.SourceRegistry:
1✔
793
                        scratchRequired = pvc.Annotations[cc.AnnRegistryImportMethod] != string(cdiv1.RegistryPullNode)
1✔
794
                }
795
        }
796
        value, ok := pvc.Annotations[cc.AnnRequiresScratch]
1✔
797
        if ok {
2✔
798
                boolVal, _ := strconv.ParseBool(value)
1✔
799
                scratchRequired = scratchRequired || boolVal
1✔
800
        }
1✔
801
        return scratchRequired
1✔
802
}
803

804
func (r *ImportReconciler) createScratchPvcForPod(pvc *corev1.PersistentVolumeClaim, pod *corev1.Pod) error {
1✔
805
        scratchPvc := &corev1.PersistentVolumeClaim{}
1✔
806
        scratchPVCName, exists := getScratchNameFromPod(pod)
1✔
807
        if !exists {
1✔
808
                return errors.New("Scratch Volume not configured for pod")
×
809
        }
×
810
        anno := pvc.GetAnnotations()
1✔
811
        err := r.client.Get(context.TODO(), types.NamespacedName{Namespace: pvc.GetNamespace(), Name: scratchPVCName}, scratchPvc)
1✔
812
        if cc.IgnoreNotFound(err) != nil {
1✔
813
                return err
×
814
        }
×
815
        if k8serrors.IsNotFound(err) {
2✔
816
                r.log.V(1).Info("Creating scratch space for POD and PVC", "pod.Name", pod.Name, "pvc.Name", pvc.Name)
1✔
817

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

843
// Get path to VDDK image from 'v2v-vmware' ConfigMap
844
func (r *ImportReconciler) getVddkImageName() (*string, error) {
1✔
845
        namespace := util.GetNamespace()
1✔
846

1✔
847
        cm := &corev1.ConfigMap{}
1✔
848
        err := r.uncachedClient.Get(context.TODO(), types.NamespacedName{Name: common.VddkConfigMap, Namespace: namespace}, cm)
1✔
849
        if k8serrors.IsNotFound(err) {
2✔
850
                return nil, errors.Errorf("No %s ConfigMap present in namespace %s", common.VddkConfigMap, namespace)
1✔
851
        }
1✔
852

853
        image, found := cm.Data[common.VddkConfigDataKey]
1✔
854
        if found {
2✔
855
                msg := fmt.Sprintf("Found %s ConfigMap in namespace %s, VDDK image path is: ", common.VddkConfigMap, namespace)
1✔
856
                r.log.V(1).Info(msg, common.VddkConfigDataKey, image)
1✔
857
                return &image, nil
1✔
858
        }
1✔
859

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

863
// returns the import image part of the endpoint string
864
func getRegistryImportImage(pvc *corev1.PersistentVolumeClaim) (string, error) {
1✔
865
        ep, err := cc.GetEndpoint(pvc)
1✔
866
        if err != nil {
1✔
867
                return "", nil
×
868
        }
×
869
        if cc.IsImageStream(pvc) {
1✔
870
                return ep, nil
×
871
        }
×
872
        url, err := url.Parse(ep)
1✔
873
        if err != nil {
1✔
874
                return "", errors.Errorf("illegal registry endpoint %s", ep)
×
875
        }
×
876
        return url.Host + url.Path, nil
1✔
877
}
878

879
// getValueFromAnnotation returns the value of an annotation
880
func getValueFromAnnotation(pvc *corev1.PersistentVolumeClaim, annotation string) string {
1✔
881
        return pvc.Annotations[annotation]
1✔
882
}
1✔
883

884
// If this pod is going to transfer one checkpoint in a multi-stage import, attach the checkpoint name to the pod name so
885
// that each checkpoint gets a unique pod. That way each pod can be inspected using the retainAfterCompletion annotation.
886
func podNameWithCheckpoint(pvc *corev1.PersistentVolumeClaim) string {
1✔
887
        if checkpoint := pvc.Annotations[cc.AnnCurrentCheckpoint]; checkpoint != "" {
2✔
888
                return pvc.Name + "-checkpoint-" + checkpoint
1✔
889
        }
1✔
890
        return pvc.Name
1✔
891
}
892

893
func getImportPodNameFromPvc(pvc *corev1.PersistentVolumeClaim) string {
1✔
894
        podName, ok := pvc.Annotations[cc.AnnImportPod]
1✔
895
        if ok {
2✔
896
                return podName
1✔
897
        }
1✔
898
        // fallback to legacy naming, in fact the following function is fully compatible with legacy
899
        // name concatenation "importer-{pvc.Name}" if the name length is under the size limits,
900
        return naming.GetResourceName(common.ImporterPodName, podNameWithCheckpoint(pvc))
1✔
901
}
902

903
func createImportPodNameFromPvc(pvc *corev1.PersistentVolumeClaim) string {
1✔
904
        return naming.GetResourceName(common.ImporterPodName, podNameWithCheckpoint(pvc))
1✔
905
}
1✔
906

907
// createImporterPod creates and returns a pointer to a pod which is created based on the passed-in endpoint, secret
908
// name, and pvc. A nil secret means the endpoint credentials are not passed to the
909
// importer pod.
910
func createImporterPod(ctx context.Context, log logr.Logger, client client.Client, args *importerPodArgs, installerLabels map[string]string) (*corev1.Pod, error) {
1✔
911
        var err error
1✔
912
        args.podResourceRequirements, err = cc.GetDefaultPodResourceRequirements(client)
1✔
913
        if err != nil {
1✔
914
                return nil, err
×
915
        }
×
916

917
        args.imagePullSecrets, err = cc.GetImagePullSecrets(client)
1✔
918
        if err != nil {
1✔
919
                return nil, err
×
920
        }
×
921

922
        args.workloadNodePlacement, err = cc.GetWorkloadNodePlacement(ctx, client)
1✔
923
        if err != nil {
1✔
924
                return nil, err
×
925
        }
×
926

927
        if isRegistryNodeImport(args) {
2✔
928
                args.importImage, err = getRegistryImportImage(args.pvc)
1✔
929
                if err != nil {
1✔
930
                        return nil, err
×
931
                }
×
932
                setRegistryNodeImportEnvVars(args)
1✔
933
                if args.podEnvVar.registryImageArchitecture != "" {
1✔
934
                        setRegistryNodeImportNodeSelector(args)
×
935
                }
×
936
        }
937

938
        pod := makeImporterPodSpec(args)
1✔
939

1✔
940
        util.SetRecommendedLabels(pod, installerLabels, "cdi-controller")
1✔
941

1✔
942
        // add any labels from pvc to the importer pod
1✔
943
        util.MergeLabels(args.pvc.Labels, pod.Labels)
1✔
944

1✔
945
        if err = client.Create(context.TODO(), pod); err != nil {
1✔
946
                return nil, err
×
947
        }
×
948

949
        log.V(3).Info("importer pod created\n", "pod.Name", pod.Name, "pod.Namespace", pod.Namespace, "image name", args.image)
1✔
950
        return pod, nil
1✔
951
}
952

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

1✔
958
        pod := &corev1.Pod{
1✔
959
                TypeMeta: metav1.TypeMeta{
1✔
960
                        Kind:       "Pod",
1✔
961
                        APIVersion: "v1",
1✔
962
                },
1✔
963
                ObjectMeta: metav1.ObjectMeta{
1✔
964
                        Name:      podName,
1✔
965
                        Namespace: args.pvc.Namespace,
1✔
966
                        Annotations: map[string]string{
1✔
967
                                cc.AnnCreatedBy: "yes",
1✔
968
                        },
1✔
969
                        Labels: map[string]string{
1✔
970
                                common.CDILabelKey:        common.CDILabelValue,
1✔
971
                                common.CDIComponentLabel:  common.ImporterPodName,
1✔
972
                                common.PrometheusLabelKey: common.PrometheusLabelValue,
1✔
973
                        },
1✔
974
                        OwnerReferences: []metav1.OwnerReference{
1✔
975
                                {
1✔
976
                                        APIVersion:         "v1",
1✔
977
                                        Kind:               "PersistentVolumeClaim",
1✔
978
                                        Name:               args.pvc.Name,
1✔
979
                                        UID:                args.pvc.GetUID(),
1✔
980
                                        BlockOwnerDeletion: ptr.To[bool](true),
1✔
981
                                        Controller:         ptr.To[bool](true),
1✔
982
                                },
1✔
983
                        },
1✔
984
                },
1✔
985
                Spec: corev1.PodSpec{
1✔
986
                        Containers:        makeImporterContainerSpec(args),
1✔
987
                        InitContainers:    makeImporterInitContainersSpec(args),
1✔
988
                        Volumes:           makeImporterVolumeSpec(args),
1✔
989
                        RestartPolicy:     corev1.RestartPolicyOnFailure,
1✔
990
                        NodeSelector:      args.workloadNodePlacement.NodeSelector,
1✔
991
                        Tolerations:       args.workloadNodePlacement.Tolerations,
1✔
992
                        Affinity:          args.workloadNodePlacement.Affinity,
1✔
993
                        PriorityClassName: args.priorityClassName,
1✔
994
                        ImagePullSecrets:  args.imagePullSecrets,
1✔
995
                },
1✔
996
        }
1✔
997

1✔
998
        /**
1✔
999
        FIXME: When registry source is ImageStream, if we set importer pod OwnerReference (to its pvc, like all other cases),
1✔
1000
        for some reason (OCP issue?) we get the following error:
1✔
1001
                Failed to pull image "imagestream-name": rpc error: code = Unknown
1✔
1002
                desc = Error reading manifest latest in docker.io/library/imagestream-name: errors:
1✔
1003
                denied: requested access to the resource is denied
1✔
1004
                unauthorized: authentication required
1✔
1005
        When we don't set pod OwnerReferences, all works well.
1✔
1006
        */
1✔
1007
        if isRegistryNodeImport(args) && cc.IsImageStream(args.pvc) {
1✔
1008
                pod.OwnerReferences = nil
×
1009
                pod.Annotations[cc.AnnOpenShiftImageLookup] = "*"
×
1010
        }
×
1011

1012
        cc.CopyAllowedAnnotations(args.pvc, pod)
1✔
1013
        cc.SetRestrictedSecurityContext(&pod.Spec)
1✔
1014
        // We explicitly define a NodeName for dynamically provisioned PVCs
1✔
1015
        // when the PVC is being handled by a populator (PVC')
1✔
1016
        cc.SetNodeNameIfPopulator(args.pvc, &pod.Spec)
1✔
1017

1✔
1018
        return pod
1✔
1019
}
1020

1021
func makeImporterContainerSpec(args *importerPodArgs) []corev1.Container {
1✔
1022
        containers := []corev1.Container{
1✔
1023
                {
1✔
1024
                        Name:            common.ImporterPodName,
1✔
1025
                        Image:           args.image,
1✔
1026
                        ImagePullPolicy: corev1.PullPolicy(args.pullPolicy),
1✔
1027
                        Args:            []string{"-v=" + args.verbose},
1✔
1028
                        Env:             makeImportEnv(args.podEnvVar, getOwnerUID(args)),
1✔
1029
                        Ports: []corev1.ContainerPort{
1✔
1030
                                {
1✔
1031
                                        Name:          "metrics",
1✔
1032
                                        ContainerPort: 8443,
1✔
1033
                                        Protocol:      corev1.ProtocolTCP,
1✔
1034
                                },
1✔
1035
                        },
1✔
1036
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1037
                },
1✔
1038
        }
1✔
1039
        if cc.GetVolumeMode(args.pvc) == corev1.PersistentVolumeBlock {
2✔
1040
                containers[0].VolumeDevices = cc.AddVolumeDevices()
1✔
1041
        } else {
2✔
1042
                containers[0].VolumeMounts = cc.AddImportVolumeMounts()
1✔
1043
        }
1✔
1044
        if isRegistryNodeImport(args) {
2✔
1045
                containers = append(containers, corev1.Container{
1✔
1046
                        Name:            "server",
1✔
1047
                        Image:           args.importImage,
1✔
1048
                        ImagePullPolicy: corev1.PullPolicy(args.pullPolicy),
1✔
1049
                        Command:         []string{"/shared/server", "-p", "8100", "-image-dir", "/disk", "-ready-file", "/shared/ready", "-done-file", "/shared/done"},
1✔
1050
                        VolumeMounts: []corev1.VolumeMount{
1✔
1051
                                {
1✔
1052
                                        MountPath: "/shared",
1✔
1053
                                        Name:      "shared-volume",
1✔
1054
                                },
1✔
1055
                        },
1✔
1056
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1057
                })
1✔
1058
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1059
                        MountPath: "/shared",
1✔
1060
                        Name:      "shared-volume",
1✔
1061
                })
1✔
1062
        }
1✔
1063
        if args.scratchPvcName != nil {
2✔
1064
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1065
                        Name:      cc.ScratchVolName,
1✔
1066
                        MountPath: common.ScratchDataDir,
1✔
1067
                })
1✔
1068
        }
1✔
1069
        if args.vddkImageName != nil {
2✔
1070
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1071
                        Name:      "vddk-vol-mount",
1✔
1072
                        MountPath: "/opt",
1✔
1073
                })
1✔
1074
        }
1✔
1075
        if args.vddkExtraArgs != nil {
2✔
1076
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1077
                        Name:      common.VddkArgsVolName,
1✔
1078
                        MountPath: common.VddkArgsDir,
1✔
1079
                })
1✔
1080
        }
1✔
1081
        if args.podEnvVar.certConfigMap != "" {
1✔
1082
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
1083
                        Name:      CertVolName,
×
1084
                        MountPath: common.ImporterCertDir,
×
1085
                })
×
1086
        }
×
1087
        if args.podEnvVar.certConfigMapProxy != "" {
1✔
1088
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
1089
                        Name:      ProxyCertVolName,
×
1090
                        MountPath: common.ImporterProxyCertDir,
×
1091
                })
×
1092
        }
×
1093
        if args.podEnvVar.source == cc.SourceGCS && args.podEnvVar.secretName != "" {
1✔
1094
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
1095
                        Name:      SecretVolName,
×
1096
                        MountPath: common.ImporterGoogleCredentialDir,
×
1097
                })
×
1098
        }
×
1099
        for index := range args.podEnvVar.secretExtraHeaders {
1✔
1100
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
1101
                        Name:      fmt.Sprintf(secretExtraHeadersVolumeName, index),
×
1102
                        MountPath: path.Join(common.ImporterSecretExtraHeadersDir, fmt.Sprint(index)),
×
1103
                })
×
1104
        }
×
1105
        if args.podResourceRequirements != nil {
1✔
1106
                for i := range containers {
×
1107
                        containers[i].Resources = *args.podResourceRequirements
×
1108
                }
×
1109
        }
1110
        return containers
1✔
1111
}
1112

1113
func makeImporterVolumeSpec(args *importerPodArgs) []corev1.Volume {
1✔
1114
        volumes := []corev1.Volume{
1✔
1115
                {
1✔
1116
                        Name: cc.DataVolName,
1✔
1117
                        VolumeSource: corev1.VolumeSource{
1✔
1118
                                PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
1✔
1119
                                        ClaimName: args.pvc.Name,
1✔
1120
                                        ReadOnly:  false,
1✔
1121
                                },
1✔
1122
                        },
1✔
1123
                },
1✔
1124
        }
1✔
1125
        if isRegistryNodeImport(args) {
2✔
1126
                volumes = append(volumes, corev1.Volume{
1✔
1127
                        Name: "shared-volume",
1✔
1128
                        VolumeSource: corev1.VolumeSource{
1✔
1129
                                EmptyDir: &corev1.EmptyDirVolumeSource{},
1✔
1130
                        },
1✔
1131
                })
1✔
1132
        }
1✔
1133
        if args.scratchPvcName != nil {
2✔
1134
                volumes = append(volumes, corev1.Volume{
1✔
1135
                        Name: cc.ScratchVolName,
1✔
1136
                        VolumeSource: corev1.VolumeSource{
1✔
1137
                                PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
1✔
1138
                                        ClaimName: *args.scratchPvcName,
1✔
1139
                                        ReadOnly:  false,
1✔
1140
                                },
1✔
1141
                        },
1✔
1142
                })
1✔
1143
        }
1✔
1144
        if args.vddkImageName != nil {
2✔
1145
                volumes = append(volumes, corev1.Volume{
1✔
1146
                        Name: "vddk-vol-mount",
1✔
1147
                        VolumeSource: corev1.VolumeSource{
1✔
1148
                                EmptyDir: &corev1.EmptyDirVolumeSource{},
1✔
1149
                        },
1✔
1150
                })
1✔
1151
        }
1✔
1152
        if args.vddkExtraArgs != nil {
2✔
1153
                volumes = append(volumes, corev1.Volume{
1✔
1154
                        Name: common.VddkArgsVolName,
1✔
1155
                        VolumeSource: corev1.VolumeSource{
1✔
1156
                                ConfigMap: &v1.ConfigMapVolumeSource{
1✔
1157
                                        LocalObjectReference: v1.LocalObjectReference{
1✔
1158
                                                Name: *args.vddkExtraArgs,
1✔
1159
                                        },
1✔
1160
                                },
1✔
1161
                        },
1✔
1162
                })
1✔
1163
        }
1✔
1164
        if args.podEnvVar.certConfigMap != "" {
1✔
1165
                volumes = append(volumes, createConfigMapVolume(CertVolName, args.podEnvVar.certConfigMap))
×
1166
        }
×
1167
        if args.podEnvVar.certConfigMapProxy != "" {
1✔
1168
                volumes = append(volumes, createConfigMapVolume(ProxyCertVolName, GetImportProxyConfigMapName(args.pvc.Name)))
×
1169
        }
×
1170
        if args.podEnvVar.source == cc.SourceGCS && args.podEnvVar.secretName != "" {
1✔
1171
                volumes = append(volumes, createSecretVolume(SecretVolName, args.podEnvVar.secretName))
×
1172
        }
×
1173
        for index, header := range args.podEnvVar.secretExtraHeaders {
1✔
1174
                volumes = append(volumes, corev1.Volume{
×
1175
                        Name: fmt.Sprintf(secretExtraHeadersVolumeName, index),
×
1176
                        VolumeSource: corev1.VolumeSource{
×
1177
                                Secret: &corev1.SecretVolumeSource{
×
1178
                                        SecretName: header,
×
1179
                                },
×
1180
                        },
×
1181
                })
×
1182
        }
×
1183
        return volumes
1✔
1184
}
1185

1186
func makeImporterInitContainersSpec(args *importerPodArgs) []corev1.Container {
1✔
1187
        var initContainers []corev1.Container
1✔
1188
        if isRegistryNodeImport(args) {
2✔
1189
                initContainers = append(initContainers, corev1.Container{
1✔
1190
                        Name:            "init",
1✔
1191
                        Image:           args.image,
1✔
1192
                        ImagePullPolicy: corev1.PullPolicy(args.pullPolicy),
1✔
1193
                        Command:         []string{"sh", "-c", "cp /usr/bin/cdi-containerimage-server /shared/server"},
1✔
1194
                        VolumeMounts: []corev1.VolumeMount{
1✔
1195
                                {
1✔
1196
                                        MountPath: "/shared",
1✔
1197
                                        Name:      "shared-volume",
1✔
1198
                                },
1✔
1199
                        },
1✔
1200
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1201
                })
1✔
1202
        }
1✔
1203
        if args.vddkImageName != nil {
2✔
1204
                initContainers = append(initContainers, corev1.Container{
1✔
1205
                        Name:  "vddk-side-car",
1✔
1206
                        Image: *args.vddkImageName,
1✔
1207
                        VolumeMounts: []corev1.VolumeMount{
1✔
1208
                                {
1✔
1209
                                        Name:      "vddk-vol-mount",
1✔
1210
                                        MountPath: "/opt",
1✔
1211
                                },
1✔
1212
                        },
1✔
1213
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1214
                })
1✔
1215
        }
1✔
1216
        if args.podResourceRequirements != nil {
1✔
1217
                for i := range initContainers {
×
1218
                        initContainers[i].Resources = *args.podResourceRequirements
×
1219
                }
×
1220
        }
1221
        return initContainers
1✔
1222
}
1223

1224
func isRegistryNodeImport(args *importerPodArgs) bool {
1✔
1225
        return cc.GetSource(args.pvc) == cc.SourceRegistry &&
1✔
1226
                args.pvc.Annotations[cc.AnnRegistryImportMethod] == string(cdiv1.RegistryPullNode)
1✔
1227
}
1✔
1228

1229
func getOwnerUID(args *importerPodArgs) types.UID {
1✔
1230
        if len(args.pvc.OwnerReferences) == 1 {
1✔
1231
                return args.pvc.OwnerReferences[0].UID
×
1232
        }
×
1233
        return args.pvc.UID
1✔
1234
}
1235

1236
func setRegistryNodeImportEnvVars(args *importerPodArgs) {
1✔
1237
        args.podEnvVar.source = cc.SourceHTTP
1✔
1238
        args.podEnvVar.ep = "http://localhost:8100/disk.img"
1✔
1239
        args.podEnvVar.pullMethod = string(cdiv1.RegistryPullNode)
1✔
1240
        args.podEnvVar.readyFile = "/shared/ready"
1✔
1241
        args.podEnvVar.doneFile = "/shared/done"
1✔
1242
}
1✔
1243

1244
func setRegistryNodeImportNodeSelector(args *importerPodArgs) {
×
1245
        if args.workloadNodePlacement.NodeSelector == nil {
×
1246
                args.workloadNodePlacement.NodeSelector = make(map[string]string, 0)
×
1247
        }
×
1248
        args.workloadNodePlacement.NodeSelector[v1.LabelArchStable] = args.podEnvVar.registryImageArchitecture
×
1249
}
1250

1251
func createConfigMapVolume(certVolName, objRef string) corev1.Volume {
1✔
1252
        return corev1.Volume{
1✔
1253
                Name: certVolName,
1✔
1254
                VolumeSource: corev1.VolumeSource{
1✔
1255
                        ConfigMap: &corev1.ConfigMapVolumeSource{
1✔
1256
                                LocalObjectReference: corev1.LocalObjectReference{
1✔
1257
                                        Name: objRef,
1✔
1258
                                },
1✔
1259
                        },
1✔
1260
                },
1✔
1261
        }
1✔
1262
}
1✔
1263

1264
func createSecretVolume(thisVolName, objRef string) corev1.Volume {
×
1265
        return corev1.Volume{
×
1266
                Name: thisVolName,
×
1267
                VolumeSource: corev1.VolumeSource{
×
1268
                        Secret: &corev1.SecretVolumeSource{
×
1269
                                SecretName: objRef,
×
1270
                        },
×
1271
                },
×
1272
        }
×
1273
}
×
1274

1275
// return the Env portion for the importer container.
1276
func makeImportEnv(podEnvVar *importPodEnvVar, uid types.UID) []corev1.EnvVar {
1✔
1277
        env := []corev1.EnvVar{
1✔
1278
                {
1✔
1279
                        Name:  common.ImporterSource,
1✔
1280
                        Value: podEnvVar.source,
1✔
1281
                },
1✔
1282
                {
1✔
1283
                        Name:  common.ImporterEndpoint,
1✔
1284
                        Value: podEnvVar.ep,
1✔
1285
                },
1✔
1286
                {
1✔
1287
                        Name:  common.ImporterContentType,
1✔
1288
                        Value: podEnvVar.contentType,
1✔
1289
                },
1✔
1290
                {
1✔
1291
                        Name:  common.ImporterImageSize,
1✔
1292
                        Value: podEnvVar.imageSize,
1✔
1293
                },
1✔
1294
                {
1✔
1295
                        Name:  common.OwnerUID,
1✔
1296
                        Value: string(uid),
1✔
1297
                },
1✔
1298
                {
1✔
1299
                        Name:  common.FilesystemOverheadVar,
1✔
1300
                        Value: podEnvVar.filesystemOverhead,
1✔
1301
                },
1✔
1302
                {
1✔
1303
                        Name:  common.InsecureTLSVar,
1✔
1304
                        Value: strconv.FormatBool(podEnvVar.insecureTLS),
1✔
1305
                },
1✔
1306
                {
1✔
1307
                        Name:  common.ImporterDiskID,
1✔
1308
                        Value: podEnvVar.diskID,
1✔
1309
                },
1✔
1310
                {
1✔
1311
                        Name:  common.ImporterUUID,
1✔
1312
                        Value: podEnvVar.uuid,
1✔
1313
                },
1✔
1314
                {
1✔
1315
                        Name:  common.ImporterPullMethod,
1✔
1316
                        Value: podEnvVar.pullMethod,
1✔
1317
                },
1✔
1318
                {
1✔
1319
                        Name:  common.ImporterReadyFile,
1✔
1320
                        Value: podEnvVar.readyFile,
1✔
1321
                },
1✔
1322
                {
1✔
1323
                        Name:  common.ImporterDoneFile,
1✔
1324
                        Value: podEnvVar.doneFile,
1✔
1325
                },
1✔
1326
                {
1✔
1327
                        Name:  common.ImporterBackingFile,
1✔
1328
                        Value: podEnvVar.backingFile,
1✔
1329
                },
1✔
1330
                {
1✔
1331
                        Name:  common.ImporterThumbprint,
1✔
1332
                        Value: podEnvVar.thumbprint,
1✔
1333
                },
1✔
1334
                {
1✔
1335
                        Name:  common.ImportProxyHTTP,
1✔
1336
                        Value: podEnvVar.httpProxy,
1✔
1337
                },
1✔
1338
                {
1✔
1339
                        Name:  common.ImportProxyHTTPS,
1✔
1340
                        Value: podEnvVar.httpsProxy,
1✔
1341
                },
1✔
1342
                {
1✔
1343
                        Name:  common.ImportProxyNoProxy,
1✔
1344
                        Value: podEnvVar.noProxy,
1✔
1345
                },
1✔
1346
                {
1✔
1347
                        Name:  common.ImporterCurrentCheckpoint,
1✔
1348
                        Value: podEnvVar.currentCheckpoint,
1✔
1349
                },
1✔
1350
                {
1✔
1351
                        Name:  common.ImporterPreviousCheckpoint,
1✔
1352
                        Value: podEnvVar.previousCheckpoint,
1✔
1353
                },
1✔
1354
                {
1✔
1355
                        Name:  common.ImporterFinalCheckpoint,
1✔
1356
                        Value: podEnvVar.finalCheckpoint,
1✔
1357
                },
1✔
1358
                {
1✔
1359
                        Name:  common.Preallocation,
1✔
1360
                        Value: strconv.FormatBool(podEnvVar.preallocation),
1✔
1361
                },
1✔
1362
                {
1✔
1363
                        Name:  common.CacheMode,
1✔
1364
                        Value: podEnvVar.cacheMode,
1✔
1365
                },
1✔
1366
                {
1✔
1367
                        Name:  common.ImporterRegistryImageArchitecture,
1✔
1368
                        Value: podEnvVar.registryImageArchitecture,
1✔
1369
                },
1✔
1370
                {
1✔
1371
                        Name:  common.ImporterChecksum,
1✔
1372
                        Value: podEnvVar.checksum,
1✔
1373
                },
1✔
1374
        }
1✔
1375
        if podEnvVar.secretName != "" && podEnvVar.source != cc.SourceGCS {
1✔
1376
                env = append(env, corev1.EnvVar{
×
1377
                        Name: common.ImporterAccessKeyID,
×
1378
                        ValueFrom: &corev1.EnvVarSource{
×
1379
                                SecretKeyRef: &corev1.SecretKeySelector{
×
1380
                                        LocalObjectReference: corev1.LocalObjectReference{
×
1381
                                                Name: podEnvVar.secretName,
×
1382
                                        },
×
1383
                                        Key: common.KeyAccess,
×
1384
                                },
×
1385
                        },
×
1386
                }, corev1.EnvVar{
×
1387
                        Name: common.ImporterSecretKey,
×
1388
                        ValueFrom: &corev1.EnvVarSource{
×
1389
                                SecretKeyRef: &corev1.SecretKeySelector{
×
1390
                                        LocalObjectReference: corev1.LocalObjectReference{
×
1391
                                                Name: podEnvVar.secretName,
×
1392
                                        },
×
1393
                                        Key: common.KeySecret,
×
1394
                                },
×
1395
                        },
×
1396
                })
×
1397
        }
×
1398
        if podEnvVar.secretName != "" && podEnvVar.source == cc.SourceGCS {
1✔
1399
                env = append(env, corev1.EnvVar{
×
1400
                        Name:  common.ImporterGoogleCredentialFileVar,
×
1401
                        Value: common.ImporterGoogleCredentialFile,
×
1402
                })
×
1403
        }
×
1404
        if podEnvVar.certConfigMap != "" {
1✔
1405
                env = append(env, corev1.EnvVar{
×
1406
                        Name:  common.ImporterCertDirVar,
×
1407
                        Value: common.ImporterCertDir,
×
1408
                })
×
1409
        }
×
1410
        if podEnvVar.certConfigMapProxy != "" {
1✔
1411
                env = append(env, corev1.EnvVar{
×
1412
                        Name:  common.ImporterProxyCertDirVar,
×
1413
                        Value: common.ImporterProxyCertDir,
×
1414
                })
×
1415
        }
×
1416
        for index, header := range podEnvVar.extraHeaders {
1✔
1417
                env = append(env, corev1.EnvVar{
×
1418
                        Name:  fmt.Sprintf("%s%d", common.ImporterExtraHeader, index),
×
1419
                        Value: header,
×
1420
                })
×
1421
        }
×
1422
        return env
1✔
1423
}
1424

1425
func isOOMKilled(status v1.ContainerStatus) bool {
1✔
1426
        if terminated := status.State.Terminated; terminated != nil {
2✔
1427
                if terminated.Reason == cc.OOMKilledReason {
2✔
1428
                        return true
1✔
1429
                }
1✔
1430
        }
1431
        if terminated := status.LastTerminationState.Terminated; terminated != nil {
2✔
1432
                if terminated.Reason == cc.OOMKilledReason {
1✔
1433
                        return true
×
1434
                }
×
1435
        }
1436

1437
        return false
1✔
1438
}
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