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

kubevirt / containerized-data-importer / #5761

13 Jan 2026 07:24AM UTC coverage: 49.643% (+0.3%) from 49.388%
#5761

Pull #4010

travis-ci

halfcrazy
feat: Add checksum validation for HTTP/HTTPS DataVolume sources

Introduces cryptographic hash validation for HTTP/HTTPS
import sources to prevent data tampering during download.

**Usage Example:**

```yaml
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
  name: fedora-dv
spec:
  source:
    http:
      url: "https://download.fedoraproject.org/pub/fedora/linux/releases/39/Cloud/x86_64/images/Fedora-Cloud-Base-39-1.5.x86_64.qcow2"
      checksum: "sha256:c5b50f903e39b3c5d3b7c7bb9a4c5e4f3"
  pvc:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 10Gi
```

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

153 of 171 new or added lines in 6 files covered. (89.47%)

619 existing lines in 7 files now uncovered.

14755 of 29722 relevant lines covered (49.64%)

0.56 hits per line

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

71.4
/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.DeletionTimestamp == nil {
2✔
260
                        podsUsingPVC, err := cc.GetPodsUsingPVCs(context.TODO(), r.client, pvc.Namespace, sets.New(pvc.Name), false)
1✔
261
                        if err != nil {
1✔
UNCOV
262
                                return reconcile.Result{}, err
×
UNCOV
263
                        }
×
264

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

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

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

1✔
309
                return reconcile.Result{RequeueAfter: 2 * time.Second}, nil
1✔
310
        }
1✔
311
        return reconcile.Result{}, nil
1✔
312
}
313

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

348
// GetImportProxyConfigMapName returns the import proxy ConfigMap name
UNCOV
349
func GetImportProxyConfigMapName(pvcName string) string {
×
UNCOV
350
        return naming.GetResourceName("import-proxy-cm", pvcName)
×
UNCOV
351
}
×
352

353
func (r *ImportReconciler) initPvcPodName(pvc *corev1.PersistentVolumeClaim, log logr.Logger) error {
1✔
354
        currentPvcCopy := pvc.DeepCopyObject()
1✔
355

1✔
356
        log.V(1).Info("Init pod name on PVC")
1✔
357
        anno := pvc.GetAnnotations()
1✔
358

1✔
359
        anno[cc.AnnImportPod] = createImportPodNameFromPvc(pvc)
1✔
360

1✔
361
        requiresScratch := r.requiresScratchSpace(pvc)
1✔
362
        if requiresScratch {
1✔
UNCOV
363
                anno[cc.AnnRequiresScratch] = "true"
×
UNCOV
364
        }
×
365

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

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

1✔
379
        log.V(1).Info("Updating PVC from pod")
1✔
380
        anno := pvc.GetAnnotations()
1✔
381

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

1✔
388
        scratchSpaceRequired := termMsg != nil && termMsg.ScratchSpaceRequired != nil && *termMsg.ScratchSpaceRequired
1✔
389
        if scratchSpaceRequired {
2✔
390
                log.V(1).Info("Pod requires scratch space, terminating pod, and restarting with scratch space", "pod.Name", pod.Name)
1✔
391
        }
1✔
392
        podModificationsNeeded := scratchSpaceRequired
1✔
393

1✔
394
        if statuses := pod.Status.ContainerStatuses; len(statuses) > 0 {
2✔
395
                if isOOMKilled(statuses[0]) {
2✔
396
                        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✔
397
                        podModificationsNeeded = true
1✔
398
                        anno[cc.AnnRequiresDirectIO] = "true"
1✔
399
                }
1✔
400
                if terminated := statuses[0].State.Terminated; terminated != nil && terminated.ExitCode > 0 {
2✔
401
                        log.Info("Pod termination code", "pod.Name", pod.Name, "ExitCode", terminated.ExitCode)
1✔
402
                        r.recorder.Event(pvc, corev1.EventTypeWarning, ErrImportFailedPVC, terminated.Message)
1✔
403
                }
1✔
404
        }
405

406
        if anno[cc.AnnCurrentCheckpoint] != "" {
1✔
UNCOV
407
                anno[cc.AnnCurrentPodID] = string(pod.ObjectMeta.UID)
×
UNCOV
408
        }
×
409

410
        anno[cc.AnnImportPod] = pod.Name
1✔
411
        if !podModificationsNeeded {
2✔
412
                // No scratch space required, update the phase based on the pod. If we require scratch space we don't want to update the
1✔
413
                // phase, because the pod might terminate cleanly and mistakenly mark the import complete.
1✔
414
                anno[cc.AnnPodPhase] = string(pod.Status.Phase)
1✔
415
        }
1✔
416

417
        anno[cc.AnnPodSchedulable] = "true"
1✔
418
        if phase, ok := anno[cc.AnnPodPhase]; ok && phase == string(corev1.PodPending) {
2✔
419
                for _, cond := range pod.Status.Conditions {
1✔
UNCOV
420
                        if cond.Type == corev1.PodScheduled && cond.Reason == corev1.PodReasonUnschedulable {
×
UNCOV
421
                                anno[cc.AnnPodSchedulable] = "false"
×
UNCOV
422
                                break
×
423
                        }
424
                }
425
        }
426

427
        for _, ev := range pod.Spec.Containers[0].Env {
2✔
428
                if ev.Name == common.CacheMode && ev.Value == common.CacheModeTryNone {
1✔
UNCOV
429
                        anno[cc.AnnRequiresDirectIO] = "false"
×
UNCOV
430
                }
×
431
        }
432

433
        // Check if the POD is waiting for scratch space, if so create some.
434
        if pod.Status.Phase == corev1.PodPending && r.requiresScratchSpace(pvc) {
2✔
435
                if err := r.createScratchPvcForPod(pvc, pod); err != nil {
1✔
UNCOV
436
                        if !k8serrors.IsAlreadyExists(err) {
×
UNCOV
437
                                return err
×
UNCOV
438
                        }
×
439
                }
440
        } else {
1✔
441
                // No scratch space, or scratch space is bound, remove annotation
1✔
442
                delete(anno, cc.AnnBoundCondition)
1✔
443
                delete(anno, cc.AnnBoundConditionMessage)
1✔
444
                delete(anno, cc.AnnBoundConditionReason)
1✔
445
        }
1✔
446

447
        if pvc.GetLabels() == nil {
2✔
448
                pvc.SetLabels(make(map[string]string, 0))
1✔
449
        }
1✔
450
        if !checkIfLabelExists(pvc, common.CDILabelKey, common.CDILabelValue) {
2✔
451
                pvc.GetLabels()[common.CDILabelKey] = common.CDILabelValue
1✔
452
        }
1✔
453
        if cc.IsPVCComplete(pvc) {
2✔
454
                pvc.SetLabels(addLabelsFromTerminationMessage(pvc.GetLabels(), termMsg))
1✔
455
        }
1✔
456

457
        if !reflect.DeepEqual(currentPvcCopy, pvc) {
2✔
458
                if err := r.updatePVC(pvc, log); err != nil {
1✔
UNCOV
459
                        return err
×
UNCOV
460
                }
×
461
                log.V(1).Info("Updated PVC", "pvc.anno.Phase", anno[cc.AnnPodPhase], "pvc.anno.Restarts", anno[cc.AnnPodRestarts])
1✔
462
        }
463

464
        if cc.IsPVCComplete(pvc) || podModificationsNeeded {
2✔
465
                if !podModificationsNeeded {
2✔
466
                        r.recorder.Event(pvc, corev1.EventTypeNormal, ImportSucceededPVC, "Import Successful")
1✔
467
                        log.V(1).Info("Import completed successfully")
1✔
468
                }
1✔
469
                if cc.ShouldDeletePod(pvc) {
2✔
470
                        log.V(1).Info("Deleting pod", "pod.Name", pod.Name)
1✔
471
                        if err := r.cleanup(pvc, pod, log); err != nil {
1✔
UNCOV
472
                                return err
×
UNCOV
473
                        }
×
474
                }
475
        }
476
        return nil
1✔
477
}
478

479
func (r *ImportReconciler) cleanup(pvc *corev1.PersistentVolumeClaim, pod *corev1.Pod, log logr.Logger) error {
1✔
480
        if err := r.client.Delete(context.TODO(), pod); cc.IgnoreNotFound(err) != nil {
1✔
UNCOV
481
                return err
×
UNCOV
482
        }
×
483
        if cc.HasFinalizer(pvc, importPodImageStreamFinalizer) {
1✔
484
                cc.RemoveFinalizer(pvc, importPodImageStreamFinalizer)
×
485
                if err := r.updatePVC(pvc, log); err != nil {
×
486
                        return err
×
487
                }
×
488
        }
489
        return nil
1✔
490
}
491

492
func (r *ImportReconciler) updatePVC(pvc *corev1.PersistentVolumeClaim, log logr.Logger) error {
1✔
493
        if err := r.client.Update(context.TODO(), pvc); err != nil {
1✔
UNCOV
494
                return err
×
UNCOV
495
        }
×
496
        return nil
1✔
497
}
498

499
func (r *ImportReconciler) createImporterPod(pvc *corev1.PersistentVolumeClaim) error {
1✔
500
        r.log.V(1).Info("Creating importer POD for PVC", "pvc.Name", pvc.Name)
1✔
501
        var scratchPvcName *string
1✔
502
        var vddkImageName *string
1✔
503
        var vddkExtraArgs *string
1✔
504
        var err error
1✔
505

1✔
506
        requiresScratch := r.requiresScratchSpace(pvc)
1✔
507
        if requiresScratch {
1✔
UNCOV
508
                name := createScratchNameFromPvc(pvc)
×
UNCOV
509
                scratchPvcName = &name
×
510
        }
×
511

512
        if cc.GetSource(pvc) == cc.SourceVDDK {
2✔
513
                r.log.V(1).Info("Pod requires VDDK sidecar for VMware transfer")
1✔
514
                anno := pvc.GetAnnotations()
1✔
515
                if imageName, ok := anno[cc.AnnVddkInitImageURL]; ok {
2✔
516
                        vddkImageName = &imageName
1✔
517
                } else {
2✔
518
                        if vddkImageName, err = r.getVddkImageName(); err != nil {
2✔
519
                                r.log.V(1).Error(err, "failed to get VDDK image name from configmap")
1✔
520
                        }
1✔
521
                }
522
                if vddkImageName == nil {
2✔
523
                        message := fmt.Sprintf("waiting for %s configmap or %s annotation for VDDK image", common.VddkConfigMap, cc.AnnVddkInitImageURL)
1✔
524
                        anno[cc.AnnBoundCondition] = "false"
1✔
525
                        anno[cc.AnnBoundConditionMessage] = message
1✔
526
                        anno[cc.AnnBoundConditionReason] = common.AwaitingVDDK
1✔
527
                        if err := r.updatePVC(pvc, r.log); err != nil {
1✔
UNCOV
528
                                return err
×
UNCOV
529
                        }
×
530
                        return errors.New(message)
1✔
531
                }
532

533
                if extraArgs, ok := anno[cc.AnnVddkExtraArgs]; ok && extraArgs != "" {
2✔
534
                        r.log.V(1).Info("Mounting extra VDDK args ConfigMap to importer pod", "ConfigMap", extraArgs)
1✔
535
                        vddkExtraArgs = &extraArgs
1✔
536
                }
1✔
537
        }
538

539
        podEnvVar, err := r.createImportEnvVar(pvc)
1✔
540
        if err != nil {
1✔
UNCOV
541
                return err
×
UNCOV
542
        }
×
543
        // all checks passed, let's create the importer pod!
544
        podArgs := &importerPodArgs{
1✔
545
                image:             r.image,
1✔
546
                verbose:           r.verbose,
1✔
547
                pullPolicy:        r.pullPolicy,
1✔
548
                podEnvVar:         podEnvVar,
1✔
549
                pvc:               pvc,
1✔
550
                scratchPvcName:    scratchPvcName,
1✔
551
                vddkImageName:     vddkImageName,
1✔
552
                vddkExtraArgs:     vddkExtraArgs,
1✔
553
                priorityClassName: cc.GetPriorityClass(pvc),
1✔
554
        }
1✔
555

1✔
556
        pod, err := createImporterPod(context.TODO(), r.log, r.client, podArgs, r.installerLabels)
1✔
557
        // Check if pod has failed and, in that case, record an event with the error
1✔
558
        if podErr := cc.HandleFailedPod(err, pvc.Annotations[cc.AnnImportPod], pvc, r.recorder, r.client); podErr != nil {
1✔
UNCOV
559
                return podErr
×
UNCOV
560
        }
×
561

562
        r.log.V(1).Info("Created POD", "pod.Name", pod.Name)
1✔
563

1✔
564
        // If importing from image stream, add finalizer. Note we don't watch the importer pod in this case,
1✔
565
        // so to prevent a deadlock we add finalizer only if the pod is not retained after completion.
1✔
566
        if cc.IsImageStream(pvc) && pvc.GetAnnotations()[cc.AnnPodRetainAfterCompletion] != "true" {
1✔
UNCOV
567
                cc.AddFinalizer(pvc, importPodImageStreamFinalizer)
×
UNCOV
568
                if err := r.updatePVC(pvc, r.log); err != nil {
×
UNCOV
569
                        return err
×
570
                }
×
571
        }
572

573
        if requiresScratch {
1✔
UNCOV
574
                r.log.V(1).Info("Pod requires scratch space")
×
UNCOV
575
                return r.createScratchPvcForPod(pvc, pod)
×
UNCOV
576
        }
×
577

578
        return nil
1✔
579
}
580

UNCOV
581
func createScratchNameFromPvc(pvc *v1.PersistentVolumeClaim) string {
×
UNCOV
582
        return naming.GetResourceName(pvc.Name, common.ScratchNameSuffix)
×
UNCOV
583
}
×
584

585
func (r *ImportReconciler) createImportEnvVar(pvc *corev1.PersistentVolumeClaim) (*importPodEnvVar, error) {
1✔
586
        podEnvVar := &importPodEnvVar{}
1✔
587
        podEnvVar.source = cc.GetSource(pvc)
1✔
588
        podEnvVar.contentType = string(cc.GetPVCContentType(pvc))
1✔
589

1✔
590
        var err error
1✔
591
        if podEnvVar.source != cc.SourceNone {
2✔
592
                podEnvVar.ep, err = cc.GetEndpoint(pvc)
1✔
593
                if err != nil {
1✔
UNCOV
594
                        return nil, err
×
UNCOV
595
                }
×
596
                podEnvVar.secretName = r.getSecretName(pvc)
1✔
597
                if podEnvVar.secretName == "" {
2✔
598
                        r.log.V(2).Info("no secret will be supplied to endpoint", "endPoint", podEnvVar.ep)
1✔
599
                }
1✔
600
                //get the CDIConfig to extract the proxy configuration to be used to import an image
601
                cdiConfig := &cdiv1.CDIConfig{}
1✔
602
                err = r.client.Get(context.TODO(), types.NamespacedName{Name: common.ConfigName}, cdiConfig)
1✔
603
                if err != nil {
1✔
604
                        return nil, err
×
605
                }
×
606
                podEnvVar.certConfigMap, err = r.getCertConfigMap(pvc)
1✔
607
                if err != nil {
1✔
UNCOV
608
                        return nil, err
×
UNCOV
609
                }
×
610
                podEnvVar.insecureTLS, err = r.isInsecureTLS(pvc, cdiConfig)
1✔
611
                if err != nil {
1✔
612
                        return nil, err
×
UNCOV
613
                }
×
614
                podEnvVar.diskID = getValueFromAnnotation(pvc, cc.AnnDiskID)
1✔
615
                podEnvVar.backingFile = getValueFromAnnotation(pvc, cc.AnnBackingFile)
1✔
616
                podEnvVar.uuid = getValueFromAnnotation(pvc, cc.AnnUUID)
1✔
617
                podEnvVar.thumbprint = getValueFromAnnotation(pvc, cc.AnnThumbprint)
1✔
618
                podEnvVar.previousCheckpoint = getValueFromAnnotation(pvc, cc.AnnPreviousCheckpoint)
1✔
619
                podEnvVar.currentCheckpoint = getValueFromAnnotation(pvc, cc.AnnCurrentCheckpoint)
1✔
620
                podEnvVar.finalCheckpoint = getValueFromAnnotation(pvc, cc.AnnFinalCheckpoint)
1✔
621
                podEnvVar.registryImageArchitecture = getValueFromAnnotation(pvc, cc.AnnRegistryImageArchitecture)
1✔
622
                podEnvVar.checksum = getValueFromAnnotation(pvc, cc.AnnChecksum)
1✔
623

1✔
624
                for annotation, value := range pvc.Annotations {
2✔
625
                        if strings.HasPrefix(annotation, cc.AnnExtraHeaders) {
1✔
UNCOV
626
                                podEnvVar.extraHeaders = append(podEnvVar.extraHeaders, value)
×
UNCOV
627
                        }
×
628
                        if strings.HasPrefix(annotation, cc.AnnSecretExtraHeaders) {
1✔
UNCOV
629
                                podEnvVar.secretExtraHeaders = append(podEnvVar.secretExtraHeaders, value)
×
UNCOV
630
                        }
×
631
                }
632

633
                var field string
1✔
634
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyHTTP); err != nil {
2✔
635
                        r.log.V(3).Info("no proxy http url will be supplied:", "error", err.Error())
1✔
636
                }
1✔
637
                podEnvVar.httpProxy = field
1✔
638
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyHTTPS); err != nil {
2✔
639
                        r.log.V(3).Info("no proxy https url will be supplied:", "error", err.Error())
1✔
640
                }
1✔
641
                podEnvVar.httpsProxy = field
1✔
642
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyNoProxy); err != nil {
2✔
643
                        r.log.V(3).Info("the noProxy field will not be supplied:", "error", err.Error())
1✔
644
                }
1✔
645
                podEnvVar.noProxy = field
1✔
646
                if field, err = GetImportProxyConfig(cdiConfig, common.ImportProxyConfigMapName); err != nil {
2✔
647
                        r.log.V(3).Info("no proxy CA certiticate will be supplied:", "error", err.Error())
1✔
648
                }
1✔
649
                podEnvVar.certConfigMapProxy = field
1✔
650
        }
651

652
        fsOverhead, err := GetFilesystemOverhead(context.TODO(), r.client, pvc)
1✔
653
        if err != nil {
1✔
UNCOV
654
                return nil, err
×
655
        }
×
656
        podEnvVar.filesystemOverhead = string(fsOverhead)
1✔
657

1✔
658
        if preallocation, err := strconv.ParseBool(getValueFromAnnotation(pvc, cc.AnnPreallocationRequested)); err == nil {
1✔
659
                podEnvVar.preallocation = preallocation
×
UNCOV
660
        } // else use the default "false"
×
661

662
        //get the requested image size.
663
        podEnvVar.imageSize, err = cc.GetRequestedImageSize(pvc)
1✔
664
        if err != nil {
1✔
UNCOV
665
                return nil, err
×
UNCOV
666
        }
×
667

668
        if v, ok := pvc.Annotations[cc.AnnRequiresDirectIO]; ok && v == "true" {
2✔
669
                podEnvVar.cacheMode = common.CacheModeTryNone
1✔
670
        }
1✔
671

672
        return podEnvVar, nil
1✔
673
}
674

675
func (r *ImportReconciler) isInsecureTLS(pvc *corev1.PersistentVolumeClaim, cdiConfig *cdiv1.CDIConfig) (bool, error) {
1✔
676
        // Check if insecureSkipVerify annotation is set (only applicable for ImageIO sources)
1✔
677
        source, sourceOk := pvc.Annotations[cc.AnnSource]
1✔
678
        if sourceOk && source == cc.SourceImageio {
1✔
UNCOV
679
                if insecureSkipVerify, ok := pvc.Annotations[cc.AnnInsecureSkipVerify]; ok && insecureSkipVerify == "true" {
×
UNCOV
680
                        return true, nil
×
UNCOV
681
                }
×
682
        }
683

684
        ep, ok := pvc.Annotations[cc.AnnEndpoint]
1✔
685
        if !ok || ep == "" {
2✔
686
                return false, nil
1✔
687
        }
1✔
688
        return IsInsecureTLS(ep, cdiConfig, r.log)
1✔
689
}
690

691
// IsInsecureTLS checks if TLS security is disabled for the given endpoint
692
func IsInsecureTLS(ep string, cdiConfig *cdiv1.CDIConfig, log logr.Logger) (bool, error) {
1✔
693
        url, err := url.Parse(ep)
1✔
694
        if err != nil {
1✔
695
                return false, err
×
UNCOV
696
        }
×
697

698
        if url.Scheme != "docker" {
2✔
699
                return false, nil
1✔
700
        }
1✔
701

702
        for _, value := range cdiConfig.Spec.InsecureRegistries {
2✔
703
                log.V(1).Info("Checking host against value", "host", url.Host, "value", value)
1✔
704
                if value == url.Host {
2✔
705
                        return true, nil
1✔
706
                }
1✔
707
        }
708
        return false, nil
1✔
709
}
710

711
func (r *ImportReconciler) getCertConfigMap(pvc *corev1.PersistentVolumeClaim) (string, error) {
1✔
712
        value, ok := pvc.Annotations[cc.AnnCertConfigMap]
1✔
713
        if !ok || value == "" {
2✔
714
                return "", nil
1✔
715
        }
1✔
716

717
        configMap := &corev1.ConfigMap{}
1✔
718
        if err := r.uncachedClient.Get(context.TODO(), types.NamespacedName{Name: value, Namespace: pvc.Namespace}, configMap); err != nil {
2✔
719
                if k8serrors.IsNotFound(err) {
2✔
720
                        r.log.V(1).Info("Configmap does not exist, pod will not start until it does", "configMapName", value)
1✔
721
                        return value, nil
1✔
722
                }
1✔
723

724
                return "", err
×
725
        }
726

727
        return value, nil
1✔
728
}
729

730
// returns the name of the secret containing endpoint credentials consumed by the importer pod.
731
// A value of "" implies there are no credentials for the endpoint being used. A returned error
732
// causes processNextItem() to stop.
733
func (r *ImportReconciler) getSecretName(pvc *corev1.PersistentVolumeClaim) string {
1✔
734
        ns := pvc.Namespace
1✔
735
        name, found := pvc.Annotations[cc.AnnSecret]
1✔
736
        if !found || name == "" {
2✔
737
                msg := "getEndpointSecret: "
1✔
738
                if !found {
2✔
739
                        msg += fmt.Sprintf("annotation %q is missing in pvc \"%s/%s\"", cc.AnnSecret, ns, pvc.Name)
1✔
740
                } else {
1✔
UNCOV
741
                        msg += fmt.Sprintf("secret name is missing from annotation %q in pvc \"%s/%s\"", cc.AnnSecret, ns, pvc.Name)
×
UNCOV
742
                }
×
743
                r.log.V(2).Info(msg)
1✔
744
                return "" // importer pod will not contain secret credentials
1✔
745
        }
746
        return name
1✔
747
}
748

749
func (r *ImportReconciler) requiresScratchSpace(pvc *corev1.PersistentVolumeClaim) bool {
1✔
750
        scratchRequired := false
1✔
751
        contentType := cc.GetPVCContentType(pvc)
1✔
752
        // All archive requires scratch space.
1✔
753
        if contentType == cdiv1.DataVolumeArchive {
1✔
UNCOV
754
                scratchRequired = true
×
755
        } else {
1✔
756
                switch cc.GetSource(pvc) {
1✔
UNCOV
757
                case cc.SourceGlance:
×
UNCOV
758
                        scratchRequired = true
×
UNCOV
759
                case cc.SourceImageio:
×
UNCOV
760
                        if val, ok := pvc.Annotations[cc.AnnCurrentCheckpoint]; ok {
×
UNCOV
761
                                scratchRequired = val != ""
×
UNCOV
762
                        }
×
763
                case cc.SourceRegistry:
1✔
764
                        scratchRequired = pvc.Annotations[cc.AnnRegistryImportMethod] != string(cdiv1.RegistryPullNode)
1✔
765
                }
766
        }
767
        value, ok := pvc.Annotations[cc.AnnRequiresScratch]
1✔
768
        if ok {
2✔
769
                boolVal, _ := strconv.ParseBool(value)
1✔
770
                scratchRequired = scratchRequired || boolVal
1✔
771
        }
1✔
772
        return scratchRequired
1✔
773
}
774

775
func (r *ImportReconciler) createScratchPvcForPod(pvc *corev1.PersistentVolumeClaim, pod *corev1.Pod) error {
1✔
776
        scratchPvc := &corev1.PersistentVolumeClaim{}
1✔
777
        scratchPVCName, exists := getScratchNameFromPod(pod)
1✔
778
        if !exists {
1✔
UNCOV
779
                return errors.New("Scratch Volume not configured for pod")
×
UNCOV
780
        }
×
781
        anno := pvc.GetAnnotations()
1✔
782
        err := r.client.Get(context.TODO(), types.NamespacedName{Namespace: pvc.GetNamespace(), Name: scratchPVCName}, scratchPvc)
1✔
783
        if cc.IgnoreNotFound(err) != nil {
1✔
UNCOV
784
                return err
×
UNCOV
785
        }
×
786
        if k8serrors.IsNotFound(err) {
2✔
787
                r.log.V(1).Info("Creating scratch space for POD and PVC", "pod.Name", pod.Name, "pvc.Name", pvc.Name)
1✔
788

1✔
789
                storageClassName := GetScratchPvcStorageClass(r.client, pvc)
1✔
790
                // Scratch PVC doesn't exist yet, create it. Determine which storage class to use.
1✔
791
                _, err = createScratchPersistentVolumeClaim(r.client, pvc, pod, scratchPVCName, storageClassName, r.installerLabels, r.recorder)
1✔
792
                if err != nil {
1✔
UNCOV
793
                        return err
×
UNCOV
794
                }
×
795
                anno[cc.AnnBoundCondition] = "false"
1✔
796
                anno[cc.AnnBoundConditionMessage] = "Creating scratch space"
1✔
797
                anno[cc.AnnBoundConditionReason] = creatingScratch
1✔
UNCOV
798
        } else {
×
UNCOV
799
                if scratchPvc.DeletionTimestamp != nil {
×
UNCOV
800
                        // Delete the pod since we are in a deadlock situation now. The scratch PVC from the previous import is not gone
×
UNCOV
801
                        // yet but terminating, and the new pod is still being created and the scratch PVC now has a finalizer on it.
×
UNCOV
802
                        // Only way to break it, is to delete the importer pod, and give the pvc a chance to disappear.
×
UNCOV
803
                        err = r.client.Delete(context.TODO(), pod)
×
UNCOV
804
                        if err != nil {
×
UNCOV
805
                                return err
×
UNCOV
806
                        }
×
UNCOV
807
                        return fmt.Errorf("terminating scratch space found, deleting pod %s", pod.Name)
×
808
                }
809
        }
810
        anno[cc.AnnRequiresScratch] = "false"
1✔
811
        return nil
1✔
812
}
813

814
// Get path to VDDK image from 'v2v-vmware' ConfigMap
815
func (r *ImportReconciler) getVddkImageName() (*string, error) {
1✔
816
        namespace := util.GetNamespace()
1✔
817

1✔
818
        cm := &corev1.ConfigMap{}
1✔
819
        err := r.uncachedClient.Get(context.TODO(), types.NamespacedName{Name: common.VddkConfigMap, Namespace: namespace}, cm)
1✔
820
        if k8serrors.IsNotFound(err) {
2✔
821
                return nil, errors.Errorf("No %s ConfigMap present in namespace %s", common.VddkConfigMap, namespace)
1✔
822
        }
1✔
823

824
        image, found := cm.Data[common.VddkConfigDataKey]
1✔
825
        if found {
2✔
826
                msg := fmt.Sprintf("Found %s ConfigMap in namespace %s, VDDK image path is: ", common.VddkConfigMap, namespace)
1✔
827
                r.log.V(1).Info(msg, common.VddkConfigDataKey, image)
1✔
828
                return &image, nil
1✔
829
        }
1✔
830

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

834
// returns the import image part of the endpoint string
835
func getRegistryImportImage(pvc *corev1.PersistentVolumeClaim) (string, error) {
1✔
836
        ep, err := cc.GetEndpoint(pvc)
1✔
837
        if err != nil {
1✔
UNCOV
838
                return "", nil
×
UNCOV
839
        }
×
840
        if cc.IsImageStream(pvc) {
1✔
UNCOV
841
                return ep, nil
×
UNCOV
842
        }
×
843
        url, err := url.Parse(ep)
1✔
844
        if err != nil {
1✔
UNCOV
845
                return "", errors.Errorf("illegal registry endpoint %s", ep)
×
UNCOV
846
        }
×
847
        return url.Host + url.Path, nil
1✔
848
}
849

850
// getValueFromAnnotation returns the value of an annotation
851
func getValueFromAnnotation(pvc *corev1.PersistentVolumeClaim, annotation string) string {
1✔
852
        return pvc.Annotations[annotation]
1✔
853
}
1✔
854

855
// If this pod is going to transfer one checkpoint in a multi-stage import, attach the checkpoint name to the pod name so
856
// that each checkpoint gets a unique pod. That way each pod can be inspected using the retainAfterCompletion annotation.
857
func podNameWithCheckpoint(pvc *corev1.PersistentVolumeClaim) string {
1✔
858
        if checkpoint := pvc.Annotations[cc.AnnCurrentCheckpoint]; checkpoint != "" {
2✔
859
                return pvc.Name + "-checkpoint-" + checkpoint
1✔
860
        }
1✔
861
        return pvc.Name
1✔
862
}
863

864
func getImportPodNameFromPvc(pvc *corev1.PersistentVolumeClaim) string {
1✔
865
        podName, ok := pvc.Annotations[cc.AnnImportPod]
1✔
866
        if ok {
2✔
867
                return podName
1✔
868
        }
1✔
869
        // fallback to legacy naming, in fact the following function is fully compatible with legacy
870
        // name concatenation "importer-{pvc.Name}" if the name length is under the size limits,
871
        return naming.GetResourceName(common.ImporterPodName, podNameWithCheckpoint(pvc))
1✔
872
}
873

874
func createImportPodNameFromPvc(pvc *corev1.PersistentVolumeClaim) string {
1✔
875
        return naming.GetResourceName(common.ImporterPodName, podNameWithCheckpoint(pvc))
1✔
876
}
1✔
877

878
// createImporterPod creates and returns a pointer to a pod which is created based on the passed-in endpoint, secret
879
// name, and pvc. A nil secret means the endpoint credentials are not passed to the
880
// importer pod.
881
func createImporterPod(ctx context.Context, log logr.Logger, client client.Client, args *importerPodArgs, installerLabels map[string]string) (*corev1.Pod, error) {
1✔
882
        var err error
1✔
883
        args.podResourceRequirements, err = cc.GetDefaultPodResourceRequirements(client)
1✔
884
        if err != nil {
1✔
UNCOV
885
                return nil, err
×
UNCOV
886
        }
×
887

888
        args.imagePullSecrets, err = cc.GetImagePullSecrets(client)
1✔
889
        if err != nil {
1✔
UNCOV
890
                return nil, err
×
UNCOV
891
        }
×
892

893
        args.workloadNodePlacement, err = cc.GetWorkloadNodePlacement(ctx, client)
1✔
894
        if err != nil {
1✔
UNCOV
895
                return nil, err
×
UNCOV
896
        }
×
897

898
        if isRegistryNodeImport(args) {
2✔
899
                args.importImage, err = getRegistryImportImage(args.pvc)
1✔
900
                if err != nil {
1✔
UNCOV
901
                        return nil, err
×
UNCOV
902
                }
×
903
                setRegistryNodeImportEnvVars(args)
1✔
904
                if args.podEnvVar.registryImageArchitecture != "" {
1✔
UNCOV
905
                        setRegistryNodeImportNodeSelector(args)
×
UNCOV
906
                }
×
907
        }
908

909
        pod := makeImporterPodSpec(args)
1✔
910

1✔
911
        util.SetRecommendedLabels(pod, installerLabels, "cdi-controller")
1✔
912

1✔
913
        // add any labels from pvc to the importer pod
1✔
914
        util.MergeLabels(args.pvc.Labels, pod.Labels)
1✔
915

1✔
916
        if err = client.Create(context.TODO(), pod); err != nil {
1✔
UNCOV
917
                return nil, err
×
UNCOV
918
        }
×
919

920
        log.V(3).Info("importer pod created\n", "pod.Name", pod.Name, "pod.Namespace", pod.Namespace, "image name", args.image)
1✔
921
        return pod, nil
1✔
922
}
923

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

1✔
929
        pod := &corev1.Pod{
1✔
930
                TypeMeta: metav1.TypeMeta{
1✔
931
                        Kind:       "Pod",
1✔
932
                        APIVersion: "v1",
1✔
933
                },
1✔
934
                ObjectMeta: metav1.ObjectMeta{
1✔
935
                        Name:      podName,
1✔
936
                        Namespace: args.pvc.Namespace,
1✔
937
                        Annotations: map[string]string{
1✔
938
                                cc.AnnCreatedBy: "yes",
1✔
939
                        },
1✔
940
                        Labels: map[string]string{
1✔
941
                                common.CDILabelKey:        common.CDILabelValue,
1✔
942
                                common.CDIComponentLabel:  common.ImporterPodName,
1✔
943
                                common.PrometheusLabelKey: common.PrometheusLabelValue,
1✔
944
                        },
1✔
945
                        OwnerReferences: []metav1.OwnerReference{
1✔
946
                                {
1✔
947
                                        APIVersion:         "v1",
1✔
948
                                        Kind:               "PersistentVolumeClaim",
1✔
949
                                        Name:               args.pvc.Name,
1✔
950
                                        UID:                args.pvc.GetUID(),
1✔
951
                                        BlockOwnerDeletion: ptr.To[bool](true),
1✔
952
                                        Controller:         ptr.To[bool](true),
1✔
953
                                },
1✔
954
                        },
1✔
955
                },
1✔
956
                Spec: corev1.PodSpec{
1✔
957
                        Containers:        makeImporterContainerSpec(args),
1✔
958
                        InitContainers:    makeImporterInitContainersSpec(args),
1✔
959
                        Volumes:           makeImporterVolumeSpec(args),
1✔
960
                        RestartPolicy:     corev1.RestartPolicyOnFailure,
1✔
961
                        NodeSelector:      args.workloadNodePlacement.NodeSelector,
1✔
962
                        Tolerations:       args.workloadNodePlacement.Tolerations,
1✔
963
                        Affinity:          args.workloadNodePlacement.Affinity,
1✔
964
                        PriorityClassName: args.priorityClassName,
1✔
965
                        ImagePullSecrets:  args.imagePullSecrets,
1✔
966
                },
1✔
967
        }
1✔
968

1✔
969
        /**
1✔
970
        FIXME: When registry source is ImageStream, if we set importer pod OwnerReference (to its pvc, like all other cases),
1✔
971
        for some reason (OCP issue?) we get the following error:
1✔
972
                Failed to pull image "imagestream-name": rpc error: code = Unknown
1✔
973
                desc = Error reading manifest latest in docker.io/library/imagestream-name: errors:
1✔
974
                denied: requested access to the resource is denied
1✔
975
                unauthorized: authentication required
1✔
976
        When we don't set pod OwnerReferences, all works well.
1✔
977
        */
1✔
978
        if isRegistryNodeImport(args) && cc.IsImageStream(args.pvc) {
1✔
UNCOV
979
                pod.OwnerReferences = nil
×
UNCOV
980
                pod.Annotations[cc.AnnOpenShiftImageLookup] = "*"
×
UNCOV
981
        }
×
982

983
        cc.CopyAllowedAnnotations(args.pvc, pod)
1✔
984
        cc.SetRestrictedSecurityContext(&pod.Spec)
1✔
985
        // We explicitly define a NodeName for dynamically provisioned PVCs
1✔
986
        // when the PVC is being handled by a populator (PVC')
1✔
987
        cc.SetNodeNameIfPopulator(args.pvc, &pod.Spec)
1✔
988

1✔
989
        return pod
1✔
990
}
991

992
func makeImporterContainerSpec(args *importerPodArgs) []corev1.Container {
1✔
993
        containers := []corev1.Container{
1✔
994
                {
1✔
995
                        Name:            common.ImporterPodName,
1✔
996
                        Image:           args.image,
1✔
997
                        ImagePullPolicy: corev1.PullPolicy(args.pullPolicy),
1✔
998
                        Args:            []string{"-v=" + args.verbose},
1✔
999
                        Env:             makeImportEnv(args.podEnvVar, getOwnerUID(args)),
1✔
1000
                        Ports: []corev1.ContainerPort{
1✔
1001
                                {
1✔
1002
                                        Name:          "metrics",
1✔
1003
                                        ContainerPort: 8443,
1✔
1004
                                        Protocol:      corev1.ProtocolTCP,
1✔
1005
                                },
1✔
1006
                        },
1✔
1007
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1008
                },
1✔
1009
        }
1✔
1010
        if cc.GetVolumeMode(args.pvc) == corev1.PersistentVolumeBlock {
2✔
1011
                containers[0].VolumeDevices = cc.AddVolumeDevices()
1✔
1012
        } else {
2✔
1013
                containers[0].VolumeMounts = cc.AddImportVolumeMounts()
1✔
1014
        }
1✔
1015
        if isRegistryNodeImport(args) {
2✔
1016
                containers = append(containers, corev1.Container{
1✔
1017
                        Name:            "server",
1✔
1018
                        Image:           args.importImage,
1✔
1019
                        ImagePullPolicy: corev1.PullPolicy(args.pullPolicy),
1✔
1020
                        Command:         []string{"/shared/server", "-p", "8100", "-image-dir", "/disk", "-ready-file", "/shared/ready", "-done-file", "/shared/done"},
1✔
1021
                        VolumeMounts: []corev1.VolumeMount{
1✔
1022
                                {
1✔
1023
                                        MountPath: "/shared",
1✔
1024
                                        Name:      "shared-volume",
1✔
1025
                                },
1✔
1026
                        },
1✔
1027
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1028
                })
1✔
1029
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1030
                        MountPath: "/shared",
1✔
1031
                        Name:      "shared-volume",
1✔
1032
                })
1✔
1033
        }
1✔
1034
        if args.scratchPvcName != nil {
2✔
1035
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1036
                        Name:      cc.ScratchVolName,
1✔
1037
                        MountPath: common.ScratchDataDir,
1✔
1038
                })
1✔
1039
        }
1✔
1040
        if args.vddkImageName != nil {
2✔
1041
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1042
                        Name:      "vddk-vol-mount",
1✔
1043
                        MountPath: "/opt",
1✔
1044
                })
1✔
1045
        }
1✔
1046
        if args.vddkExtraArgs != nil {
2✔
1047
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
1✔
1048
                        Name:      common.VddkArgsVolName,
1✔
1049
                        MountPath: common.VddkArgsDir,
1✔
1050
                })
1✔
1051
        }
1✔
1052
        if args.podEnvVar.certConfigMap != "" {
1✔
UNCOV
1053
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
UNCOV
1054
                        Name:      CertVolName,
×
UNCOV
1055
                        MountPath: common.ImporterCertDir,
×
UNCOV
1056
                })
×
UNCOV
1057
        }
×
1058
        if args.podEnvVar.certConfigMapProxy != "" {
1✔
UNCOV
1059
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
UNCOV
1060
                        Name:      ProxyCertVolName,
×
UNCOV
1061
                        MountPath: common.ImporterProxyCertDir,
×
UNCOV
1062
                })
×
UNCOV
1063
        }
×
1064
        if args.podEnvVar.source == cc.SourceGCS && args.podEnvVar.secretName != "" {
1✔
UNCOV
1065
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
UNCOV
1066
                        Name:      SecretVolName,
×
UNCOV
1067
                        MountPath: common.ImporterGoogleCredentialDir,
×
UNCOV
1068
                })
×
UNCOV
1069
        }
×
1070
        for index := range args.podEnvVar.secretExtraHeaders {
1✔
UNCOV
1071
                containers[0].VolumeMounts = append(containers[0].VolumeMounts, corev1.VolumeMount{
×
UNCOV
1072
                        Name:      fmt.Sprintf(secretExtraHeadersVolumeName, index),
×
UNCOV
1073
                        MountPath: path.Join(common.ImporterSecretExtraHeadersDir, fmt.Sprint(index)),
×
UNCOV
1074
                })
×
UNCOV
1075
        }
×
1076
        if args.podResourceRequirements != nil {
1✔
UNCOV
1077
                for i := range containers {
×
UNCOV
1078
                        containers[i].Resources = *args.podResourceRequirements
×
UNCOV
1079
                }
×
1080
        }
1081
        return containers
1✔
1082
}
1083

1084
func makeImporterVolumeSpec(args *importerPodArgs) []corev1.Volume {
1✔
1085
        volumes := []corev1.Volume{
1✔
1086
                {
1✔
1087
                        Name: cc.DataVolName,
1✔
1088
                        VolumeSource: corev1.VolumeSource{
1✔
1089
                                PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
1✔
1090
                                        ClaimName: args.pvc.Name,
1✔
1091
                                        ReadOnly:  false,
1✔
1092
                                },
1✔
1093
                        },
1✔
1094
                },
1✔
1095
        }
1✔
1096
        if isRegistryNodeImport(args) {
2✔
1097
                volumes = append(volumes, corev1.Volume{
1✔
1098
                        Name: "shared-volume",
1✔
1099
                        VolumeSource: corev1.VolumeSource{
1✔
1100
                                EmptyDir: &corev1.EmptyDirVolumeSource{},
1✔
1101
                        },
1✔
1102
                })
1✔
1103
        }
1✔
1104
        if args.scratchPvcName != nil {
2✔
1105
                volumes = append(volumes, corev1.Volume{
1✔
1106
                        Name: cc.ScratchVolName,
1✔
1107
                        VolumeSource: corev1.VolumeSource{
1✔
1108
                                PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
1✔
1109
                                        ClaimName: *args.scratchPvcName,
1✔
1110
                                        ReadOnly:  false,
1✔
1111
                                },
1✔
1112
                        },
1✔
1113
                })
1✔
1114
        }
1✔
1115
        if args.vddkImageName != nil {
2✔
1116
                volumes = append(volumes, corev1.Volume{
1✔
1117
                        Name: "vddk-vol-mount",
1✔
1118
                        VolumeSource: corev1.VolumeSource{
1✔
1119
                                EmptyDir: &corev1.EmptyDirVolumeSource{},
1✔
1120
                        },
1✔
1121
                })
1✔
1122
        }
1✔
1123
        if args.vddkExtraArgs != nil {
2✔
1124
                volumes = append(volumes, corev1.Volume{
1✔
1125
                        Name: common.VddkArgsVolName,
1✔
1126
                        VolumeSource: corev1.VolumeSource{
1✔
1127
                                ConfigMap: &v1.ConfigMapVolumeSource{
1✔
1128
                                        LocalObjectReference: v1.LocalObjectReference{
1✔
1129
                                                Name: *args.vddkExtraArgs,
1✔
1130
                                        },
1✔
1131
                                },
1✔
1132
                        },
1✔
1133
                })
1✔
1134
        }
1✔
1135
        if args.podEnvVar.certConfigMap != "" {
1✔
UNCOV
1136
                volumes = append(volumes, createConfigMapVolume(CertVolName, args.podEnvVar.certConfigMap))
×
UNCOV
1137
        }
×
1138
        if args.podEnvVar.certConfigMapProxy != "" {
1✔
UNCOV
1139
                volumes = append(volumes, createConfigMapVolume(ProxyCertVolName, GetImportProxyConfigMapName(args.pvc.Name)))
×
UNCOV
1140
        }
×
1141
        if args.podEnvVar.source == cc.SourceGCS && args.podEnvVar.secretName != "" {
1✔
UNCOV
1142
                volumes = append(volumes, createSecretVolume(SecretVolName, args.podEnvVar.secretName))
×
UNCOV
1143
        }
×
1144
        for index, header := range args.podEnvVar.secretExtraHeaders {
1✔
UNCOV
1145
                volumes = append(volumes, corev1.Volume{
×
UNCOV
1146
                        Name: fmt.Sprintf(secretExtraHeadersVolumeName, index),
×
UNCOV
1147
                        VolumeSource: corev1.VolumeSource{
×
UNCOV
1148
                                Secret: &corev1.SecretVolumeSource{
×
UNCOV
1149
                                        SecretName: header,
×
UNCOV
1150
                                },
×
UNCOV
1151
                        },
×
UNCOV
1152
                })
×
UNCOV
1153
        }
×
1154
        return volumes
1✔
1155
}
1156

1157
func makeImporterInitContainersSpec(args *importerPodArgs) []corev1.Container {
1✔
1158
        var initContainers []corev1.Container
1✔
1159
        if isRegistryNodeImport(args) {
2✔
1160
                initContainers = append(initContainers, corev1.Container{
1✔
1161
                        Name:            "init",
1✔
1162
                        Image:           args.image,
1✔
1163
                        ImagePullPolicy: corev1.PullPolicy(args.pullPolicy),
1✔
1164
                        Command:         []string{"sh", "-c", "cp /usr/bin/cdi-containerimage-server /shared/server"},
1✔
1165
                        VolumeMounts: []corev1.VolumeMount{
1✔
1166
                                {
1✔
1167
                                        MountPath: "/shared",
1✔
1168
                                        Name:      "shared-volume",
1✔
1169
                                },
1✔
1170
                        },
1✔
1171
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1172
                })
1✔
1173
        }
1✔
1174
        if args.vddkImageName != nil {
2✔
1175
                initContainers = append(initContainers, corev1.Container{
1✔
1176
                        Name:  "vddk-side-car",
1✔
1177
                        Image: *args.vddkImageName,
1✔
1178
                        VolumeMounts: []corev1.VolumeMount{
1✔
1179
                                {
1✔
1180
                                        Name:      "vddk-vol-mount",
1✔
1181
                                        MountPath: "/opt",
1✔
1182
                                },
1✔
1183
                        },
1✔
1184
                        TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
1✔
1185
                })
1✔
1186
        }
1✔
1187
        if args.podResourceRequirements != nil {
1✔
UNCOV
1188
                for i := range initContainers {
×
UNCOV
1189
                        initContainers[i].Resources = *args.podResourceRequirements
×
UNCOV
1190
                }
×
1191
        }
1192
        return initContainers
1✔
1193
}
1194

1195
func isRegistryNodeImport(args *importerPodArgs) bool {
1✔
1196
        return cc.GetSource(args.pvc) == cc.SourceRegistry &&
1✔
1197
                args.pvc.Annotations[cc.AnnRegistryImportMethod] == string(cdiv1.RegistryPullNode)
1✔
1198
}
1✔
1199

1200
func getOwnerUID(args *importerPodArgs) types.UID {
1✔
1201
        if len(args.pvc.OwnerReferences) == 1 {
1✔
UNCOV
1202
                return args.pvc.OwnerReferences[0].UID
×
UNCOV
1203
        }
×
1204
        return args.pvc.UID
1✔
1205
}
1206

1207
func setRegistryNodeImportEnvVars(args *importerPodArgs) {
1✔
1208
        args.podEnvVar.source = cc.SourceHTTP
1✔
1209
        args.podEnvVar.ep = "http://localhost:8100/disk.img"
1✔
1210
        args.podEnvVar.pullMethod = string(cdiv1.RegistryPullNode)
1✔
1211
        args.podEnvVar.readyFile = "/shared/ready"
1✔
1212
        args.podEnvVar.doneFile = "/shared/done"
1✔
1213
}
1✔
1214

UNCOV
1215
func setRegistryNodeImportNodeSelector(args *importerPodArgs) {
×
UNCOV
1216
        if args.workloadNodePlacement.NodeSelector == nil {
×
1217
                args.workloadNodePlacement.NodeSelector = make(map[string]string, 0)
×
1218
        }
×
1219
        args.workloadNodePlacement.NodeSelector[v1.LabelArchStable] = args.podEnvVar.registryImageArchitecture
×
1220
}
1221

1222
func createConfigMapVolume(certVolName, objRef string) corev1.Volume {
1✔
1223
        return corev1.Volume{
1✔
1224
                Name: certVolName,
1✔
1225
                VolumeSource: corev1.VolumeSource{
1✔
1226
                        ConfigMap: &corev1.ConfigMapVolumeSource{
1✔
1227
                                LocalObjectReference: corev1.LocalObjectReference{
1✔
1228
                                        Name: objRef,
1✔
1229
                                },
1✔
1230
                        },
1✔
1231
                },
1✔
1232
        }
1✔
1233
}
1✔
1234

UNCOV
1235
func createSecretVolume(thisVolName, objRef string) corev1.Volume {
×
UNCOV
1236
        return corev1.Volume{
×
UNCOV
1237
                Name: thisVolName,
×
UNCOV
1238
                VolumeSource: corev1.VolumeSource{
×
UNCOV
1239
                        Secret: &corev1.SecretVolumeSource{
×
UNCOV
1240
                                SecretName: objRef,
×
UNCOV
1241
                        },
×
UNCOV
1242
                },
×
UNCOV
1243
        }
×
1244
}
×
1245

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

1396
func isOOMKilled(status v1.ContainerStatus) bool {
1✔
1397
        if terminated := status.State.Terminated; terminated != nil {
2✔
1398
                if terminated.Reason == cc.OOMKilledReason {
2✔
1399
                        return true
1✔
1400
                }
1✔
1401
        }
1402
        if terminated := status.LastTerminationState.Terminated; terminated != nil {
2✔
1403
                if terminated.Reason == cc.OOMKilledReason {
1✔
UNCOV
1404
                        return true
×
1405
                }
×
1406
        }
1407

1408
        return false
1✔
1409
}
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