Skip to content

Commit 3b9a4af

Browse files
committed
adds lcow builder to generate HCS document from annotations and options
This commit adds a lcow builder for `containerd-shim-lcow-v1` shim. This package encapsulates the business logic to parse the annotations + devices + runhcs options into the final HCS document which will be used by the shim to create UVMs via HCS. Signed-off-by: Harsh Rawat <harshrawat@microsoft.com>
1 parent 4a86103 commit 3b9a4af

11 files changed

Lines changed: 3595 additions & 16 deletions

File tree

internal/builder/vm/lcow/boot.go

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
//go:build windows
2+
3+
package lcow
4+
5+
import (
6+
"context"
7+
"fmt"
8+
"os"
9+
"path/filepath"
10+
11+
runhcsoptions "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
12+
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
13+
"github.com/Microsoft/hcsshim/internal/log"
14+
"github.com/Microsoft/hcsshim/internal/logfields"
15+
"github.com/Microsoft/hcsshim/internal/oci"
16+
"github.com/Microsoft/hcsshim/internal/vm/vmutils"
17+
"github.com/Microsoft/hcsshim/osversion"
18+
shimannotations "github.com/Microsoft/hcsshim/pkg/annotations"
19+
20+
"github.com/sirupsen/logrus"
21+
)
22+
23+
// resolveBootFilesPath resolves and validates the boot files root path.
24+
func resolveBootFilesPath(ctx context.Context, opts *runhcsoptions.Options, annotations map[string]string) (string, error) {
25+
log.G(ctx).Debug("resolveBootFilesPath: starting boot files path resolution")
26+
27+
// If the customer provides the boot files path then it is given preference over the default path.
28+
// Similarly, based on the existing behavior in old shim, the annotation provided boot files path
29+
// is given preference over those in runhcs options.
30+
bootFilesRootPath := oci.ParseAnnotationsString(annotations, shimannotations.BootFilesRootPath, opts.BootFilesRootPath)
31+
if bootFilesRootPath == "" {
32+
bootFilesRootPath = vmutils.DefaultLCOWOSBootFilesPath()
33+
}
34+
35+
if p, err := filepath.Abs(bootFilesRootPath); err == nil {
36+
bootFilesRootPath = p
37+
} else {
38+
log.G(ctx).WithFields(logrus.Fields{
39+
logfields.Path: bootFilesRootPath,
40+
logrus.ErrorKey: err,
41+
}).Warning("could not make boot files path absolute")
42+
}
43+
44+
if _, err := os.Stat(bootFilesRootPath); err != nil {
45+
return "", fmt.Errorf("boot_files_root_path %q not found: %w", bootFilesRootPath, err)
46+
}
47+
48+
log.G(ctx).WithField(logfields.Path, bootFilesRootPath).Debug("resolveBootFilesPath completed successfully")
49+
return bootFilesRootPath, nil
50+
}
51+
52+
// parseBootOptions parses LCOW boot options from annotations and options.
53+
// Returns the HCS Chipset config and the full rootfs path.
54+
func parseBootOptions(ctx context.Context, opts *runhcsoptions.Options, annotations map[string]string) (*hcsschema.Chipset, string, error) {
55+
log.G(ctx).Debug("parseBootOptions: starting boot options parsing")
56+
57+
// Resolve and validate boot files path.
58+
bootFilesPath, err := resolveBootFilesPath(ctx, opts, annotations)
59+
if err != nil {
60+
return nil, "", err
61+
}
62+
63+
log.G(ctx).WithField(logfields.Path, bootFilesPath).Debug("using boot files path")
64+
65+
chipset := &hcsschema.Chipset{}
66+
67+
// Set the default rootfs to initrd.
68+
rootFsFile := vmutils.InitrdFile
69+
70+
// Helper to check file existence in boot files path.
71+
fileExists := func(filename string) bool {
72+
_, err := os.Stat(filepath.Join(bootFilesPath, filename))
73+
return err == nil
74+
}
75+
76+
// Reset the default values based on the presence of files in the boot files path.
77+
// We have a rootfs.vhd in the boot files path. Use it over an initrd.img
78+
if fileExists(vmutils.VhdFile) {
79+
rootFsFile = vmutils.VhdFile
80+
log.G(ctx).WithField(
81+
vmutils.VhdFile, filepath.Join(bootFilesPath, vmutils.VhdFile),
82+
).Debug("updated LCOW root filesystem to " + vmutils.VhdFile)
83+
}
84+
85+
// KernelDirect supports uncompressed kernel if the kernel is present.
86+
// Default to uncompressed if on box. NOTE: If `kernel` is already
87+
// uncompressed and simply named 'kernel' it will still be used
88+
// uncompressed automatically.
89+
kernelDirectBootSupported := osversion.Build() >= 18286
90+
useKernelDirect := oci.ParseAnnotationsBool(ctx, annotations, shimannotations.KernelDirectBoot, kernelDirectBootSupported)
91+
92+
log.G(ctx).WithFields(logrus.Fields{
93+
"kernelDirectSupported": kernelDirectBootSupported,
94+
"useKernelDirect": useKernelDirect,
95+
}).Debug("determined boot mode")
96+
97+
// If customer specifies kernel direct boot but the build does not support it, return an error.
98+
if useKernelDirect && !kernelDirectBootSupported {
99+
return nil, "", fmt.Errorf("KernelDirectBoot is not supported on builds older than 18286")
100+
}
101+
102+
// Determine kernel file based on boot mode
103+
var kernelFileName string
104+
if useKernelDirect {
105+
// KernelDirect supports uncompressed kernel if present.
106+
if fileExists(vmutils.UncompressedKernelFile) {
107+
kernelFileName = vmutils.UncompressedKernelFile
108+
log.G(ctx).WithField(vmutils.UncompressedKernelFile, filepath.Join(bootFilesPath, vmutils.UncompressedKernelFile)).Debug("updated LCOW kernel file to " + vmutils.UncompressedKernelFile)
109+
} else if fileExists(vmutils.KernelFile) {
110+
kernelFileName = vmutils.KernelFile
111+
} else {
112+
return nil, "", fmt.Errorf("kernel file not found in boot files path for kernel direct boot")
113+
}
114+
} else {
115+
kernelFileName = vmutils.KernelFile
116+
if !fileExists(vmutils.KernelFile) {
117+
return nil, "", fmt.Errorf("kernel file %q not found in boot files path: %w", vmutils.KernelFile, os.ErrNotExist)
118+
}
119+
}
120+
121+
log.G(ctx).WithField("kernelFile", kernelFileName).Debug("selected kernel file")
122+
123+
// Parse preferred rootfs type annotation. This overrides the default set above based on file presence.
124+
if preferredRootfsType := oci.ParseAnnotationsString(annotations, shimannotations.PreferredRootFSType, ""); preferredRootfsType != "" {
125+
log.G(ctx).WithField("preferredRootFSType", preferredRootfsType).Debug("applying preferred rootfs type override")
126+
switch preferredRootfsType {
127+
case "initrd":
128+
rootFsFile = vmutils.InitrdFile
129+
case "vhd":
130+
rootFsFile = vmutils.VhdFile
131+
default:
132+
return nil, "", fmt.Errorf("invalid PreferredRootFSType: %s", preferredRootfsType)
133+
}
134+
if !fileExists(rootFsFile) {
135+
return nil, "", fmt.Errorf("%q not found in boot files path", rootFsFile)
136+
}
137+
}
138+
139+
log.G(ctx).WithField("rootFsFile", rootFsFile).Debug("selected rootfs file")
140+
141+
// Set up boot configuration based on boot mode
142+
if useKernelDirect {
143+
log.G(ctx).Debug("configuring kernel direct boot")
144+
chipset.LinuxKernelDirect = &hcsschema.LinuxKernelDirect{
145+
KernelFilePath: filepath.Join(bootFilesPath, kernelFileName),
146+
// KernelCmdLine will be populated later by buildKernelArgs
147+
}
148+
if rootFsFile == vmutils.InitrdFile {
149+
chipset.LinuxKernelDirect.InitRdPath = filepath.Join(bootFilesPath, rootFsFile)
150+
log.G(ctx).WithField("initrdPath", chipset.LinuxKernelDirect.InitRdPath).Debug("configured initrd for kernel direct boot")
151+
}
152+
} else {
153+
// UEFI boot
154+
log.G(ctx).Debug("configuring UEFI boot")
155+
chipset.Uefi = &hcsschema.Uefi{
156+
BootThis: &hcsschema.UefiBootEntry{
157+
DevicePath: `\` + kernelFileName,
158+
DeviceType: "VmbFs",
159+
VmbFsRootPath: bootFilesPath,
160+
},
161+
}
162+
}
163+
164+
rootFsFullPath := filepath.Join(bootFilesPath, rootFsFile)
165+
log.G(ctx).WithFields(logrus.Fields{
166+
"rootFsFullPath": rootFsFullPath,
167+
"kernelFilePath": filepath.Join(bootFilesPath, kernelFileName),
168+
"useKernelDirect": useKernelDirect,
169+
}).Debug("parseBootOptions completed successfully")
170+
171+
return chipset, rootFsFullPath, nil
172+
}

0 commit comments

Comments
 (0)