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

kubevirt / kubevirt / e19f0223-9daf-4e5e-8fbd-3faa1908a371

20 May 2026 09:57PM UTC coverage: 71.877% (-0.004%) from 71.881%
e19f0223-9daf-4e5e-8fbd-3faa1908a371

push

prow

web-flow
Merge pull request #17405 from iholder101/beta-features-on-by-default

[Meta-VEP 229]: Enable Beta features by default

24 of 27 new or added lines in 6 files covered. (88.89%)

57 existing lines in 6 files now uncovered.

78021 of 108548 relevant lines covered (71.88%)

595.54 hits per line

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

27.24
/pkg/virt-handler/container-disk/mount.go
1
/*
2
 * This file is part of the KubeVirt project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 *
16
 * Copyright The KubeVirt Authors.
17
 *
18
 */
19

20
package container_disk
21

22
import (
23
        "errors"
24
        "fmt"
25
        "hash/crc32"
26
        "io"
27
        "os"
28
        "path/filepath"
29
        "strings"
30
        "sync"
31
        "time"
32

33
        "kubevirt.io/client-go/log"
34

35
        "kubevirt.io/kubevirt/pkg/checkpoint"
36
        containerdisk "kubevirt.io/kubevirt/pkg/container-disk"
37
        diskutils "kubevirt.io/kubevirt/pkg/ephemeral-disk-utils"
38
        "kubevirt.io/kubevirt/pkg/safepath"
39
        "kubevirt.io/kubevirt/pkg/unsafepath"
40
        "kubevirt.io/kubevirt/pkg/util"
41
        virtconfig "kubevirt.io/kubevirt/pkg/virt-config"
42
        cmdclient "kubevirt.io/kubevirt/pkg/virt-handler/cmd-client"
43
        "kubevirt.io/kubevirt/pkg/virt-handler/isolation"
44
        virt_chroot "kubevirt.io/kubevirt/pkg/virt-handler/virt-chroot"
45

46
        "k8s.io/apimachinery/pkg/api/equality"
47
        "k8s.io/apimachinery/pkg/types"
48

49
        v1 "kubevirt.io/api/core/v1"
50
)
51

52
const (
53
        failedCheckMountPointFmt = "failed to check mount point for containerDisk %v: %v"
54
        failedUnmountFmt         = "failed to unmount containerDisk %v: %v : %v"
55
)
56

57
var (
58
        ErrWaitingForDisks   = errors.New("waiting for containerdisks")
59
        ErrDiskContainerGone = errors.New("disk container is gone")
60
)
61

62
//go:generate mockgen -source $GOFILE -package=$GOPACKAGE -destination=generated_mock_$GOFILE
63

64
type mounter struct {
65
        podIsolationDetector       isolation.PodIsolationDetector
66
        checkpointManager          checkpoint.CheckpointManager
67
        mountRecords               map[types.UID]*vmiMountTargetRecord
68
        mountRecordsLock           sync.Mutex
69
        suppressWarningTimeout     time.Duration
70
        needsBindMountFunc         needsBindMountFunc
71
        socketPathGetter           containerdisk.SocketPathGetter
72
        kernelBootSocketPathGetter containerdisk.KernelBootSocketPathGetter
73
        clusterConfig              *virtconfig.ClusterConfig
74
        nodeIsolationResult        isolation.IsolationResult
75
}
76

77
type Mounter interface {
78
        ContainerDisksReady(vmi *v1.VirtualMachineInstance, notInitializedSince time.Time) (bool, error)
79
        MountAndVerify(vmi *v1.VirtualMachineInstance) error
80
        Unmount(vmi *v1.VirtualMachineInstance) error
81
        // ComputeChecksums method, along with the code added in this commit, can be removed after the 1.7 release.
82
        // By then, we can be sure that during upgrades older versions of virt-handler no longer expect the checksum
83
        // in the VMI status.
84
        // Therefore, it will no longer be necessary to include this information in the VMI status.
85
        ComputeChecksums(vmi *v1.VirtualMachineInstance) (*DiskChecksums, error)
86
}
87

88
type vmiMountTargetEntry struct {
89
        TargetFile string `json:"targetFile"`
90
        SocketFile string `json:"socketFile"`
91
}
92

93
type vmiMountTargetRecord struct {
94
        MountTargetEntries []vmiMountTargetEntry `json:"mountTargetEntries"`
95
        UsesSafePaths      bool                  `json:"usesSafePaths"`
96
}
97

98
type kernelArtifacts struct {
99
        kernel *safepath.Path
100
        initrd *safepath.Path
101
}
102

103
type DiskChecksums struct {
104
        KernelBootChecksum     KernelBootChecksum
105
        ContainerDiskChecksums map[string]uint32
106
}
107

108
type KernelBootChecksum struct {
109
        Initrd *uint32
110
        Kernel *uint32
111
}
112

113
func NewMounter(isoDetector isolation.PodIsolationDetector, mountStateDir string, clusterConfig *virtconfig.ClusterConfig) Mounter {
141✔
114
        return &mounter{
141✔
115
                mountRecords:               make(map[types.UID]*vmiMountTargetRecord),
141✔
116
                podIsolationDetector:       isoDetector,
141✔
117
                checkpointManager:          checkpoint.NewSimpleCheckpointManager(mountStateDir),
141✔
118
                suppressWarningTimeout:     1 * time.Minute,
141✔
119
                needsBindMountFunc:         newNeedsBindMountFunc(""),
141✔
120
                socketPathGetter:           containerdisk.NewSocketPathGetter(""),
141✔
121
                kernelBootSocketPathGetter: containerdisk.NewKernelBootSocketPathGetter(""),
141✔
122
                clusterConfig:              clusterConfig,
141✔
123
                nodeIsolationResult:        isolation.NodeIsolationResult(),
141✔
124
        }
141✔
125
}
141✔
126

127
func (m *mounter) deleteMountTargetRecord(vmi *v1.VirtualMachineInstance) error {
2✔
128
        if string(vmi.UID) == "" {
2✔
129
                return fmt.Errorf("unable to find container disk mounted directories for vmi without uid")
×
130
        }
×
131

132
        record := vmiMountTargetRecord{}
2✔
133
        err := m.checkpointManager.Get(string(vmi.UID), &record)
2✔
134
        if err != nil && !errors.Is(err, os.ErrNotExist) {
2✔
135
                return fmt.Errorf("failed to get a checkpoint %s, %w", vmi.UID, err)
×
136
        }
×
137
        if !errors.Is(err, os.ErrNotExist) {
3✔
138
                for _, target := range record.MountTargetEntries {
2✔
139
                        os.Remove(target.TargetFile)
1✔
140
                        os.Remove(target.SocketFile)
1✔
141
                }
1✔
142

143
                if err := m.checkpointManager.Delete(string(vmi.UID)); err != nil {
1✔
144
                        return fmt.Errorf("failed to delete checkpoint %s, %w", vmi.UID, err)
×
145
                }
×
146
        }
147

148
        m.mountRecordsLock.Lock()
2✔
149
        defer m.mountRecordsLock.Unlock()
2✔
150
        delete(m.mountRecords, vmi.UID)
2✔
151

2✔
152
        return nil
2✔
153
}
154

155
func (m *mounter) getMountTargetRecord(vmi *v1.VirtualMachineInstance) (*vmiMountTargetRecord, error) {
13✔
156
        var ok bool
13✔
157
        var existingRecord *vmiMountTargetRecord
13✔
158

13✔
159
        if string(vmi.UID) == "" {
13✔
160
                return nil, fmt.Errorf("unable to find container disk mounted directories for vmi without uid")
×
161
        }
×
162

163
        m.mountRecordsLock.Lock()
13✔
164
        defer m.mountRecordsLock.Unlock()
13✔
165
        existingRecord, ok = m.mountRecords[vmi.UID]
13✔
166

13✔
167
        // first check memory cache
13✔
168
        if ok {
14✔
169
                return existingRecord, nil
1✔
170
        }
1✔
171

172
        // if not there, see if record is on disk, this can happen if virt-handler restarts
173
        record := vmiMountTargetRecord{}
12✔
174
        err := m.checkpointManager.Get(string(vmi.UID), &record)
12✔
175
        if err != nil && !errors.Is(err, os.ErrNotExist) {
12✔
176
                return nil, fmt.Errorf("failed to get checkpoint %s, %w", vmi.UID, err)
×
177
        }
×
178

179
        if err == nil {
13✔
180
                // XXX: backward compatibility for old unresolved paths, can be removed in July 2023
1✔
181
                // After a one-time convert and persist, old records are safe too.
1✔
182
                if !record.UsesSafePaths {
1✔
183
                        record.UsesSafePaths = true
×
184
                        for i, entry := range record.MountTargetEntries {
×
185
                                safePath, err := safepath.JoinAndResolveWithRelativeRoot("/", entry.TargetFile)
×
186
                                if err != nil {
×
187
                                        return nil, fmt.Errorf("failed converting legacy path to safepath: %v", err)
×
188
                                }
×
189
                                record.MountTargetEntries[i].TargetFile = unsafepath.UnsafeAbsolute(safePath.Raw())
×
190
                        }
191
                }
192

193
                m.mountRecords[vmi.UID] = &record
1✔
194
                return &record, nil
1✔
195
        }
196

197
        // not found
198
        return nil, nil
11✔
199
}
200

201
func (m *mounter) addMountTargetRecord(vmi *v1.VirtualMachineInstance, record *vmiMountTargetRecord) error {
×
202
        return m.setAddMountTargetRecordHelper(vmi, record, true)
×
203
}
×
204

205
func (m *mounter) setMountTargetRecord(vmi *v1.VirtualMachineInstance, record *vmiMountTargetRecord) error {
1✔
206
        return m.setAddMountTargetRecordHelper(vmi, record, false)
1✔
207
}
1✔
208

209
func (m *mounter) setAddMountTargetRecordHelper(vmi *v1.VirtualMachineInstance, record *vmiMountTargetRecord, addPreviousRules bool) error {
1✔
210
        if string(vmi.UID) == "" {
1✔
211
                return fmt.Errorf("unable to set container disk mounted directories for vmi without uid")
×
212
        }
×
213
        // XXX: backward compatibility for old unresolved paths, can be removed in July 2023
214
        // After a one-time convert and persist, old records are safe too.
215
        record.UsesSafePaths = true
1✔
216

1✔
217
        err := m.checkpointManager.Get(string(vmi.UID), &vmiMountTargetRecord{})
1✔
218
        if err != nil && !errors.Is(err, os.ErrNotExist) {
1✔
219
                return fmt.Errorf("failed to get checkpoint %s, %w", vmi.UID, err)
×
220
        }
×
221

222
        m.mountRecordsLock.Lock()
1✔
223
        defer m.mountRecordsLock.Unlock()
1✔
224

1✔
225
        existingRecord, ok := m.mountRecords[vmi.UID]
1✔
226
        if ok && !errors.Is(err, os.ErrNotExist) && equality.Semantic.DeepEqual(existingRecord, record) {
1✔
227
                // already done
×
228
                return nil
×
229
        }
×
230

231
        if addPreviousRules && existingRecord != nil && len(existingRecord.MountTargetEntries) > 0 {
1✔
232
                record.MountTargetEntries = append(record.MountTargetEntries, existingRecord.MountTargetEntries...)
×
233
        }
×
234

235
        if err := m.checkpointManager.Store(string(vmi.UID), record); err != nil {
1✔
236
                return fmt.Errorf("failed to checkpoint %s, %w", vmi.UID, err)
×
237
        }
×
238

239
        m.mountRecords[vmi.UID] = record
1✔
240

1✔
241
        return nil
1✔
242
}
243

244
// Mount takes a vmi and mounts all container disks of the VMI, so that they are visible for the qemu process.
245
// Additionally qcow2 images are validated if "verify" is true. The validation happens with rlimits set, to avoid DOS.
246
func (m *mounter) MountAndVerify(vmi *v1.VirtualMachineInstance) error {
6✔
247
        if m.clusterConfig.ImageVolumeEnabled() {
12✔
248
                bindMountNeeded, err := m.needsBindMountFunc(vmi)
6✔
249
                if err != nil {
6✔
250
                        return fmt.Errorf("fail to detect if bind mount needed for vmi: %s in namespace: %v. err: %v", vmi.Name, vmi.Namespace, err)
×
251
                }
×
252
                if !bindMountNeeded {
12✔
253
                        return nil
6✔
254
                }
6✔
255
        }
256

UNCOV
257
        record := vmiMountTargetRecord{}
×
UNCOV
258
        for i, volume := range vmi.Spec.Volumes {
×
UNCOV
259
                if volume.ContainerDisk != nil {
×
260
                        diskTargetDir, err := containerdisk.GetDiskTargetDirFromHostView(vmi)
×
261
                        if err != nil {
×
262
                                return err
×
263
                        }
×
264
                        diskName := containerdisk.GetDiskTargetName(i)
×
265
                        // If diskName is a symlink it will fail if the target exists.
×
266
                        if err := safepath.TouchAtNoFollow(diskTargetDir, diskName, os.ModePerm); err != nil {
×
267
                                if !os.IsExist(err) {
×
268
                                        return fmt.Errorf("failed to create mount point target: %v", err)
×
269
                                }
×
270
                        }
271
                        targetFile, err := safepath.JoinNoFollow(diskTargetDir, diskName)
×
272
                        if err != nil {
×
273
                                return err
×
274
                        }
×
275

276
                        sock, err := m.socketPathGetter(vmi, i)
×
277
                        if err != nil {
×
278
                                return err
×
279
                        }
×
280

281
                        record.MountTargetEntries = append(record.MountTargetEntries, vmiMountTargetEntry{
×
282
                                TargetFile: unsafepath.UnsafeAbsolute(targetFile.Raw()),
×
283
                                SocketFile: sock,
×
284
                        })
×
285
                }
286
        }
287

UNCOV
288
        if len(record.MountTargetEntries) > 0 {
×
289
                err := m.setMountTargetRecord(vmi, &record)
×
290
                if err != nil {
×
291
                        return err
×
292
                }
×
293
        }
294

UNCOV
295
        for i, volume := range vmi.Spec.Volumes {
×
UNCOV
296
                if volume.ContainerDisk != nil {
×
297
                        diskTargetDir, err := containerdisk.GetDiskTargetDirFromHostView(vmi)
×
298
                        if err != nil {
×
299
                                return err
×
300
                        }
×
301
                        diskName := containerdisk.GetDiskTargetName(i)
×
302
                        targetFile, err := safepath.JoinNoFollow(diskTargetDir, diskName)
×
303
                        if err != nil {
×
304
                                return err
×
305
                        }
×
306

307
                        if isMounted, err := isolation.IsMounted(targetFile); err != nil {
×
308
                                return fmt.Errorf("failed to determine if %s is already mounted: %v", targetFile, err)
×
309
                        } else if !isMounted {
×
310

×
311
                                sourceFile, err := m.getContainerDiskPath(vmi, &volume, i)
×
312
                                if err != nil {
×
313
                                        return fmt.Errorf("failed to find a sourceFile in containerDisk %v: %v", volume.Name, err)
×
314
                                }
×
315

316
                                log.DefaultLogger().Object(vmi).Infof("Bind mounting container disk at %s to %s", sourceFile, targetFile)
×
317
                                out, err := virt_chroot.MountChroot(sourceFile, targetFile, true).CombinedOutput()
×
318
                                if err != nil {
×
319
                                        return fmt.Errorf("failed to bindmount containerDisk %v: %v : %v", volume.Name, string(out), err)
×
320
                                }
×
321
                        }
322
                }
323
        }
UNCOV
324
        err := m.mountKernelArtifacts(vmi, true)
×
UNCOV
325
        if err != nil {
×
326
                return fmt.Errorf("error mounting kernel artifacts: %v", err)
×
327
        }
×
328

UNCOV
329
        return nil
×
330
}
331

332
// Unmount unmounts all container disks of a given VMI.
333
func (m *mounter) Unmount(vmi *v1.VirtualMachineInstance) error {
9✔
334
        if vmi.UID == "" {
9✔
335
                return nil
×
336
        }
×
337

338
        err := m.unmountKernelArtifacts(vmi)
9✔
339
        if err != nil {
9✔
340
                return fmt.Errorf("error unmounting kernel artifacts: %v", err)
×
341
        }
×
342

343
        record, err := m.getMountTargetRecord(vmi)
9✔
344
        if err != nil {
9✔
345
                return err
×
346
        } else if record == nil {
18✔
347
                // no entries to unmount
9✔
348

9✔
349
                log.DefaultLogger().Object(vmi).Infof("No container disk mount entries found to unmount")
9✔
350
                return nil
9✔
351
        }
9✔
352

353
        log.DefaultLogger().Object(vmi).Infof("Found container disk mount entries")
×
354
        for _, entry := range record.MountTargetEntries {
×
355
                log.DefaultLogger().Object(vmi).Infof("Looking to see if containerdisk is mounted at path %s", entry.TargetFile)
×
356
                file, err := safepath.NewFileNoFollow(entry.TargetFile)
×
357
                if err != nil {
×
358
                        if errors.Is(err, os.ErrNotExist) {
×
359
                                continue
×
360
                        }
361
                        return fmt.Errorf(failedCheckMountPointFmt, entry.TargetFile, err)
×
362
                }
363
                _ = file.Close()
×
364
                if mounted, err := isolation.IsMounted(file.Path()); err != nil {
×
365
                        return fmt.Errorf(failedCheckMountPointFmt, file, err)
×
366
                } else if mounted {
×
367
                        log.DefaultLogger().Object(vmi).Infof("unmounting container disk at path %s", file)
×
368
                        // #nosec No risk for attacker injection. Parameters are predefined strings
×
369
                        out, err := virt_chroot.UmountChroot(file.Path()).CombinedOutput()
×
370
                        if err != nil {
×
371
                                return fmt.Errorf(failedUnmountFmt, file, string(out), err)
×
372
                        }
×
373
                }
374
        }
375
        err = m.deleteMountTargetRecord(vmi)
×
376
        if err != nil {
×
377
                return err
×
378
        }
×
379

380
        return nil
×
381
}
382

383
func (m *mounter) ContainerDisksReady(vmi *v1.VirtualMachineInstance, notInitializedSince time.Time) (bool, error) {
20✔
384
        if m.clusterConfig.ImageVolumeEnabled() {
28✔
385
                bindMountNeeded, err := m.needsBindMountFunc(vmi)
8✔
386
                if err != nil {
9✔
387
                        return false, fmt.Errorf("fail to detect if bind mount needed for vmi: %s in namespace: %v. err: %v", vmi.Name, vmi.Namespace, err)
1✔
388
                }
1✔
389
                if !bindMountNeeded {
14✔
390
                        return true, nil
7✔
391
                }
7✔
392
        }
393
        for i, volume := range vmi.Spec.Volumes {
18✔
394
                if volume.ContainerDisk != nil {
12✔
395
                        sock, err := m.socketPathGetter(vmi, i)
6✔
396
                        if err == nil {
10✔
397
                                _, err = m.podIsolationDetector.DetectForSocket(sock)
4✔
398
                        }
4✔
399

400
                        if err != nil {
10✔
401
                                log.DefaultLogger().Object(vmi).Reason(err).Infof("containerdisk %s not yet ready", volume.Name)
4✔
402
                                if time.Now().After(notInitializedSince.Add(m.suppressWarningTimeout)) {
6✔
403
                                        return false, fmt.Errorf("containerdisk %s still not ready after one minute", volume.Name)
2✔
404
                                }
2✔
405
                                return false, nil
2✔
406
                        }
407

408
                }
409
        }
410

411
        if util.HasKernelBootContainerImage(vmi) {
14✔
412
                sock, err := m.kernelBootSocketPathGetter(vmi)
6✔
413
                if err == nil {
10✔
414
                        _, err = m.podIsolationDetector.DetectForSocket(sock)
4✔
415
                }
4✔
416
                if err != nil {
10✔
417
                        log.DefaultLogger().Object(vmi).Reason(err).Info("kernelboot container not yet ready")
4✔
418
                        if time.Now().After(notInitializedSince.Add(m.suppressWarningTimeout)) {
6✔
419
                                return false, fmt.Errorf("kernelboot container still not ready after one minute")
2✔
420
                        }
2✔
421
                        return false, nil
2✔
422
                }
423
        }
424

425
        log.DefaultLogger().Object(vmi).V(4).Info("all containerdisks are ready")
4✔
426
        return true, nil
4✔
427
}
428

429
// MountKernelArtifacts mounts artifacts defined by KernelBootName in VMI.
430
// This function is assumed to run after MountAndVerify.
UNCOV
431
func (m *mounter) mountKernelArtifacts(vmi *v1.VirtualMachineInstance, verify bool) error {
×
UNCOV
432
        const kernelBootName = containerdisk.KernelBootName
×
UNCOV
433

×
UNCOV
434
        log.Log.Object(vmi).Infof("mounting kernel artifacts")
×
UNCOV
435

×
UNCOV
436
        if !util.HasKernelBootContainerImage(vmi) {
×
UNCOV
437
                log.Log.Object(vmi).Infof("kernel boot not defined - nothing to mount")
×
UNCOV
438
                return nil
×
UNCOV
439
        }
×
440

441
        kb := vmi.Spec.Domain.Firmware.KernelBoot.Container
×
442

×
443
        targetDir, err := containerdisk.GetDiskTargetDirFromHostView(vmi)
×
444
        if err != nil {
×
445
                return fmt.Errorf("failed to get disk target dir: %v", err)
×
446
        }
×
447
        if err := safepath.MkdirAtNoFollow(targetDir, containerdisk.KernelBootName, 0755); err != nil {
×
448
                if !os.IsExist(err) {
×
449
                        return err
×
450
                }
×
451
        }
452

453
        targetDir, err = safepath.JoinNoFollow(targetDir, containerdisk.KernelBootName)
×
454
        if err != nil {
×
455
                return err
×
456
        }
×
457
        if err := safepath.ChpermAtNoFollow(targetDir, 0, 0, 0755); err != nil {
×
458
                return err
×
459
        }
×
460

461
        socketFilePath, err := m.kernelBootSocketPathGetter(vmi)
×
462
        if err != nil {
×
463
                return fmt.Errorf("failed to find socket path for kernel artifacts: %v", err)
×
464
        }
×
465

466
        record := vmiMountTargetRecord{
×
467
                MountTargetEntries: []vmiMountTargetEntry{{
×
468
                        TargetFile: unsafepath.UnsafeAbsolute(targetDir.Raw()),
×
469
                        SocketFile: socketFilePath,
×
470
                }},
×
471
        }
×
472

×
473
        err = m.addMountTargetRecord(vmi, &record)
×
474
        if err != nil {
×
475
                return err
×
476
        }
×
477

478
        var targetInitrdPath *safepath.Path
×
479
        var targetKernelPath *safepath.Path
×
480

×
481
        if kb.InitrdPath != "" {
×
482
                if err := safepath.TouchAtNoFollow(targetDir, filepath.Base(kb.InitrdPath), 0655); err != nil && !os.IsExist(err) {
×
483
                        return err
×
484
                }
×
485

486
                targetInitrdPath, err = safepath.JoinNoFollow(targetDir, filepath.Base(kb.InitrdPath))
×
487
                if err != nil {
×
488
                        return err
×
489
                }
×
490
        }
491

492
        if kb.KernelPath != "" {
×
493
                if err := safepath.TouchAtNoFollow(targetDir, filepath.Base(kb.KernelPath), 0655); err != nil && !os.IsExist(err) {
×
494
                        return err
×
495
                }
×
496

497
                targetKernelPath, err = safepath.JoinNoFollow(targetDir, filepath.Base(kb.KernelPath))
×
498
                if err != nil {
×
499
                        return err
×
500
                }
×
501
        }
502

503
        areKernelArtifactsMounted := func(artifactsDir *safepath.Path, artifactFiles ...*safepath.Path) (bool, error) {
×
504
                if _, err = safepath.StatAtNoFollow(artifactsDir); errors.Is(err, os.ErrNotExist) {
×
505
                        return false, nil
×
506
                } else if err != nil {
×
507
                        return false, err
×
508
                }
×
509

510
                for _, mountPoint := range artifactFiles {
×
511
                        if mountPoint != nil {
×
512
                                isMounted, err := isolation.IsMounted(mountPoint)
×
513
                                if !isMounted || err != nil {
×
514
                                        return isMounted, err
×
515
                                }
×
516
                        }
517
                }
518
                return true, nil
×
519
        }
520

521
        if isMounted, err := areKernelArtifactsMounted(targetDir, targetInitrdPath, targetKernelPath); err != nil {
×
522
                return fmt.Errorf("failed to determine if %s is already mounted: %v", targetDir, err)
×
523
        } else if !isMounted {
×
524
                log.Log.Object(vmi).Infof("kernel artifacts are not mounted - mounting...")
×
525

×
526
                kernelArtifacts, err := m.getKernelArtifactPaths(vmi)
×
527
                if err != nil {
×
528
                        return err
×
529
                }
×
530

531
                if kernelArtifacts.kernel != nil {
×
532
                        out, err := virt_chroot.MountChroot(kernelArtifacts.kernel, targetKernelPath, true).CombinedOutput()
×
533
                        if err != nil {
×
534
                                return fmt.Errorf("failed to bindmount %v: %v : %v", kernelBootName, string(out), err)
×
535
                        }
×
536
                }
537

538
                if kernelArtifacts.initrd != nil {
×
539
                        out, err := virt_chroot.MountChroot(kernelArtifacts.initrd, targetInitrdPath, true).CombinedOutput()
×
540
                        if err != nil {
×
541
                                return fmt.Errorf("failed to bindmount %v: %v : %v", kernelBootName, string(out), err)
×
542
                        }
×
543
                }
544

545
        }
546

547
        if verify {
×
548
                mounted, err := areKernelArtifactsMounted(targetDir, targetInitrdPath, targetKernelPath)
×
549
                if err != nil {
×
550
                        return fmt.Errorf("failed to check if kernel artifacts are mounted. error: %v", err)
×
551
                } else if !mounted {
×
552
                        return fmt.Errorf("kernel artifacts verification failed")
×
553
                }
×
554
        }
555

556
        return nil
×
557
}
558

559
func (m *mounter) unmountKernelArtifacts(vmi *v1.VirtualMachineInstance) error {
9✔
560
        if !util.HasKernelBootContainerImage(vmi) {
18✔
561
                return nil
9✔
562
        }
9✔
563

564
        log.DefaultLogger().Object(vmi).Infof("unmounting kernel artifacts")
×
565

×
566
        kb := vmi.Spec.Domain.Firmware.KernelBoot.Container
×
567

×
568
        record, err := m.getMountTargetRecord(vmi)
×
569
        if err != nil {
×
570
                return fmt.Errorf("failed to get mount target record: %v", err)
×
571
        } else if record == nil {
×
572
                log.DefaultLogger().Object(vmi).Warning("Cannot find kernel-boot entries to unmount")
×
573
                return nil
×
574
        }
×
575

576
        unmount := func(targetDir *safepath.Path, artifactPaths ...string) error {
×
577
                for _, artifactPath := range artifactPaths {
×
578
                        if artifactPath == "" {
×
579
                                continue
×
580
                        }
581

582
                        targetPath, err := safepath.JoinNoFollow(targetDir, filepath.Base(artifactPath))
×
583
                        if err != nil {
×
584
                                return fmt.Errorf(failedCheckMountPointFmt, targetPath, err)
×
585
                        }
×
586
                        if mounted, err := isolation.IsMounted(targetPath); err != nil {
×
587
                                return fmt.Errorf(failedCheckMountPointFmt, targetPath, err)
×
588
                        } else if mounted {
×
589
                                log.DefaultLogger().Object(vmi).Infof("unmounting container disk at targetDir %s", targetPath)
×
590

×
591
                                out, err := virt_chroot.UmountChroot(targetPath).CombinedOutput()
×
592
                                if err != nil {
×
593
                                        return fmt.Errorf(failedUnmountFmt, targetPath, string(out), err)
×
594
                                }
×
595
                        }
596
                }
597
                return nil
×
598
        }
599

600
        for idx, entry := range record.MountTargetEntries {
×
601
                if !strings.Contains(entry.TargetFile, containerdisk.KernelBootName) {
×
602
                        continue
×
603
                }
604
                targetDir, err := safepath.NewFileNoFollow(entry.TargetFile)
×
605
                if err != nil {
×
606
                        return fmt.Errorf("failed to obtaining a reference to the target directory %q: %v", targetDir, err)
×
607
                }
×
608
                _ = targetDir.Close()
×
609
                log.DefaultLogger().Object(vmi).Infof("unmounting kernel artifacts in path: %v", targetDir)
×
610

×
611
                if err = unmount(targetDir.Path(), kb.InitrdPath, kb.KernelPath); err != nil {
×
612
                        // Not returning here since even if unmount wasn't successful it's better to keep
×
613
                        // cleaning the mounted files.
×
614
                        log.Log.Object(vmi).Reason(err).Error("unable to unmount kernel artifacts")
×
615
                }
×
616

617
                removeSliceElement := func(s []vmiMountTargetEntry, idxToRemove int) []vmiMountTargetEntry {
×
618
                        // removes slice element efficiently
×
619
                        s[idxToRemove] = s[len(s)-1]
×
620
                        return s[:len(s)-1]
×
621
                }
×
622

623
                record.MountTargetEntries = removeSliceElement(record.MountTargetEntries, idx)
×
624
                return nil
×
625
        }
626

627
        return fmt.Errorf("kernel artifacts record wasn't found")
×
628
}
629

630
func (m *mounter) getContainerDiskPath(vmi *v1.VirtualMachineInstance, volume *v1.Volume, volumeIndex int) (*safepath.Path, error) {
×
631
        sock, err := m.socketPathGetter(vmi, volumeIndex)
×
632
        if err != nil {
×
633
                return nil, ErrDiskContainerGone
×
634
        }
×
635

636
        res, err := m.podIsolationDetector.DetectForSocket(sock)
×
637
        if err != nil {
×
638
                return nil, fmt.Errorf("failed to detect socket for containerDisk %v: %v", volume.Name, err)
×
639
        }
×
640

641
        mountPoint, err := isolation.ParentPathForRootMount(m.nodeIsolationResult, res)
×
642
        if err != nil {
×
643
                return nil, fmt.Errorf("failed to detect root mount point of containerDisk %v on the node: %v", volume.Name, err)
×
644
        }
×
645

646
        return containerdisk.GetImage(mountPoint, volume.ContainerDisk.Path)
×
647
}
648

649
func (m *mounter) getKernelArtifactPaths(vmi *v1.VirtualMachineInstance) (*kernelArtifacts, error) {
×
650
        sock, err := m.kernelBootSocketPathGetter(vmi)
×
651
        if err != nil {
×
652
                return nil, ErrDiskContainerGone
×
653
        }
×
654

655
        res, err := m.podIsolationDetector.DetectForSocket(sock)
×
656
        if err != nil {
×
657
                return nil, fmt.Errorf("failed to detect socket for kernelboot container: %v", err)
×
658
        }
×
659

660
        mountPoint, err := isolation.ParentPathForRootMount(m.nodeIsolationResult, res)
×
661
        if err != nil {
×
662
                return nil, fmt.Errorf("failed to detect root mount point of kernel/initrd container on the node: %v", err)
×
663
        }
×
664

665
        kernelContainer := vmi.Spec.Domain.Firmware.KernelBoot.Container
×
666
        kernelArtifacts := &kernelArtifacts{}
×
667

×
668
        if kernelContainer.KernelPath != "" {
×
669
                kernelPath, err := containerdisk.GetImage(mountPoint, kernelContainer.KernelPath)
×
670
                if err != nil {
×
671
                        return nil, err
×
672
                }
×
673
                kernelArtifacts.kernel = kernelPath
×
674
        }
675
        if kernelContainer.InitrdPath != "" {
×
676
                initrdPath, err := containerdisk.GetImage(mountPoint, kernelContainer.InitrdPath)
×
677
                if err != nil {
×
678
                        return nil, err
×
679
                }
×
680
                kernelArtifacts.initrd = initrdPath
×
681
        }
682

683
        return kernelArtifacts, nil
×
684
}
685

686
func getDigest(imageFile *safepath.Path) (uint32, error) {
×
687
        digest := crc32.NewIEEE()
×
688

×
689
        err := imageFile.ExecuteNoFollow(func(path string) (err error) {
×
690
                f, err := os.Open(path)
×
691
                if err != nil {
×
692
                        return err
×
693
                }
×
694
                defer f.Close()
×
695

×
696
                // 32 MiB chunks
×
697
                chunk := make([]byte, 1024*1024*32)
×
698

×
699
                _, err = io.CopyBuffer(digest, f, chunk)
×
700
                return err
×
701
        })
702

703
        return digest.Sum32(), err
×
704
}
705

706
func (m *mounter) ComputeChecksums(vmi *v1.VirtualMachineInstance) (*DiskChecksums, error) {
×
707

×
708
        diskChecksums := &DiskChecksums{
×
709
                ContainerDiskChecksums: map[string]uint32{},
×
710
        }
×
711

×
712
        // compute for containerdisks
×
713
        for i, volume := range vmi.Spec.Volumes {
×
714
                if volume.VolumeSource.ContainerDisk == nil {
×
715
                        continue
×
716
                }
717

718
                path, err := m.getContainerDiskPath(vmi, &volume, i)
×
719
                if err != nil {
×
720
                        return nil, err
×
721
                }
×
722

723
                checksum, err := getDigest(path)
×
724
                if err != nil {
×
725
                        return nil, err
×
726
                }
×
727

728
                diskChecksums.ContainerDiskChecksums[volume.Name] = checksum
×
729
        }
730

731
        // kernel and initrd
732
        if util.HasKernelBootContainerImage(vmi) {
×
733
                kernelArtifacts, err := m.getKernelArtifactPaths(vmi)
×
734
                if err != nil {
×
735
                        return nil, err
×
736
                }
×
737

738
                if kernelArtifacts.kernel != nil {
×
739
                        checksum, err := getDigest(kernelArtifacts.kernel)
×
740
                        if err != nil {
×
741
                                return nil, err
×
742
                        }
×
743

744
                        diskChecksums.KernelBootChecksum.Kernel = &checksum
×
745
                }
746

747
                if kernelArtifacts.initrd != nil {
×
748
                        checksum, err := getDigest(kernelArtifacts.initrd)
×
749
                        if err != nil {
×
750
                                return nil, err
×
751
                        }
×
752

753
                        diskChecksums.KernelBootChecksum.Initrd = &checksum
×
754
                }
755
        }
756

757
        return diskChecksums, nil
×
758
}
759

760
type needsBindMountFunc func(vmi *v1.VirtualMachineInstance) (bool, error)
761

762
func newNeedsBindMountFunc(baseDir string) needsBindMountFunc {
141✔
763
        return func(vmi *v1.VirtualMachineInstance) (bool, error) {
153✔
764
                for podUID := range vmi.Status.ActivePods {
20✔
765
                        virtLauncherSocketPath := cmdclient.SocketDirectoryOnHost(string(podUID))
8✔
766
                        launcherSocketExists, err := diskutils.FileExists(virtLauncherSocketPath)
8✔
767
                        if err != nil {
8✔
768
                                return false, err
×
769
                        }
×
770
                        basePath := fmt.Sprintf("%s/pods/%s/containers", baseDir, string(podUID))
8✔
771
                        containerDiskPath := filepath.Join(basePath, "container-disk-binary")
8✔
772
                        containerDiskInitContainerExists, err := diskutils.FileExists(containerDiskPath)
8✔
773
                        if err != nil {
8✔
774
                                return false, err
×
775
                        }
×
776
                        // we must check for launcherSocket to make sure this isn't an old launcher that is already completed
777
                        if launcherSocketExists && containerDiskInitContainerExists {
8✔
778
                                return true, nil
×
779
                        }
×
780
                }
781
                return false, nil
12✔
782
        }
783
}
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