Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standardize LCOW uVM bootfiles update #1861

Merged
merged 1 commit into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions internal/oci/uvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,23 @@ func parseAnnotationsPreferredRootFSType(ctx context.Context, a map[string]strin
return def
}

// handleAnnotationKernelDirectBoot handles parsing annotationKernelDirectBoot and setting
// implied annotations from the result.
// handleAnnotationBootFilesPath handles parsing annotations.BootFilesRootPath and setting
// implied options from the result.
func handleAnnotationBootFilesPath(ctx context.Context, a map[string]string, lopts *uvm.OptionsLCOW) {
lopts.UpdateBootFilesPath(ctx, parseAnnotationsString(a, annotations.BootFilesRootPath, lopts.BootFilesPath))
}

// handleAnnotationKernelDirectBoot handles parsing annotations.KernelDirectBoot and setting
// implied options from the result.
func handleAnnotationKernelDirectBoot(ctx context.Context, a map[string]string, lopts *uvm.OptionsLCOW) {
lopts.KernelDirect = ParseAnnotationsBool(ctx, a, annotations.KernelDirectBoot, lopts.KernelDirect)
if !lopts.KernelDirect {
lopts.KernelFile = uvm.KernelFile
}
}

// handleAnnotationPreferredRootFSType handles parsing annotationPreferredRootFSType and setting
// implied annotations from the result
// handleAnnotationPreferredRootFSType handles parsing annotations.PreferredRootFSType and setting
// implied options from the result
func handleAnnotationPreferredRootFSType(ctx context.Context, a map[string]string, lopts *uvm.OptionsLCOW) {
lopts.PreferredRootFSType = parseAnnotationsPreferredRootFSType(ctx, a, annotations.PreferredRootFSType, lopts.PreferredRootFSType)
switch lopts.PreferredRootFSType {
Expand All @@ -161,8 +167,8 @@ func handleAnnotationPreferredRootFSType(ctx context.Context, a map[string]strin
}
}

// handleAnnotationFullyPhysicallyBacked handles parsing annotationFullyPhysicallyBacked and setting
// implied annotations from the result. For both LCOW and WCOW options.
// handleAnnotationFullyPhysicallyBacked handles parsing annotations.FullyPhysicallyBacked and setting
// implied options from the result. For both LCOW and WCOW options.
func handleAnnotationFullyPhysicallyBacked(ctx context.Context, a map[string]string, opts interface{}) {
switch options := opts.(type) {
case *uvm.OptionsLCOW:
Expand Down Expand Up @@ -244,7 +250,7 @@ func SpecToUVMCreateOpts(ctx context.Context, s *specs.Spec, id, owner string) (
WARNING!!!!!!!!!!

When adding an option here which must match some security policy by default, make sure that the correct default (ie matches
a default security policy) is applied in handleSecurityPolicy. Inadvertantly adding an "option" which defaults to false but MUST be
a default security policy) is applied in handleSecurityPolicy. Inadvertently adding an "option" which defaults to false but MUST be
true for a default security policy to work will force the annotation to have be set by the team that owns the box. That will
be practically difficult and we might not find out until a little late in the process.
*/
Expand All @@ -254,7 +260,7 @@ func SpecToUVMCreateOpts(ctx context.Context, s *specs.Spec, id, owner string) (
lopts.VPMemSizeBytes = parseAnnotationsUint64(ctx, s.Annotations, annotations.VPMemSize, lopts.VPMemSizeBytes)
lopts.VPMemNoMultiMapping = ParseAnnotationsBool(ctx, s.Annotations, annotations.VPMemNoMultiMapping, lopts.VPMemNoMultiMapping)
lopts.VPCIEnabled = ParseAnnotationsBool(ctx, s.Annotations, annotations.VPCIEnabled, lopts.VPCIEnabled)
lopts.BootFilesPath = parseAnnotationsString(s.Annotations, annotations.BootFilesRootPath, lopts.BootFilesPath)
handleAnnotationBootFilesPath(ctx, s.Annotations, lopts)
katiewasnothere marked this conversation as resolved.
Show resolved Hide resolved
lopts.EnableScratchEncryption = ParseAnnotationsBool(ctx, s.Annotations, annotations.EncryptedScratchDisk, lopts.EnableScratchEncryption)
lopts.SecurityPolicy = parseAnnotationsString(s.Annotations, annotations.SecurityPolicy, lopts.SecurityPolicy)
lopts.SecurityPolicyEnforcer = parseAnnotationsString(s.Annotations, annotations.SecurityPolicyEnforcer, lopts.SecurityPolicyEnforcer)
Expand Down
4 changes: 2 additions & 2 deletions internal/tools/uvmboot/lcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ func init() {
`.\uvmboot.exe -gcs lcow -boot-files-path "C:\ContainerPlat\LinuxBootFiles" -root-fs-type vhd -t -exec "/bin/bash"`
}

func createLCOWOptions(_ context.Context, c *cli.Context, id string) (*uvm.OptionsLCOW, error) {
func createLCOWOptions(ctx context.Context, c *cli.Context, id string) (*uvm.OptionsLCOW, error) {
options := uvm.NewDefaultOptionsLCOW(id, "")
setGlobalOptions(c, options.Options)

// boot
if c.IsSet(bootFilesPathArgName) {
options.BootFilesPath = c.String(bootFilesPathArgName)
options.UpdateBootFilesPath(ctx, bootFilesPathArgName)
}

// kernel
Expand Down
49 changes: 44 additions & 5 deletions internal/uvm/create_lcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ type OptionsLCOW struct {
*Options
*ConfidentialOptions

BootFilesPath string // Folder in which kernel and root file system reside. Defaults to \Program Files\Linux Containers
// Folder in which kernel and root file system reside. Defaults to \Program Files\Linux Containers.
//
// It is preferred to use [UpdateBootFilesPath] to change this value and update associated fields.
BootFilesPath string
KernelFile string // Filename under `BootFilesPath` for the kernel. Defaults to `kernel`
KernelDirect bool // Skip UEFI and boot directly to `kernel`
RootFSFile string // Filename under `BootFilesPath` for the UVMs root file system. Defaults to `InitrdFile`
Expand Down Expand Up @@ -144,7 +147,6 @@ func NewDefaultOptionsLCOW(id, owner string) *OptionsLCOW {
kernelDirectSupported := osversion.Build() >= 18286
opts := &OptionsLCOW{
Options: newDefaultOptions(id, owner),
BootFilesPath: defaultLCOWOSBootFilesPath(),
KernelFile: KernelFile,
KernelDirect: kernelDirectSupported,
RootFSFile: InitrdFile,
Expand All @@ -170,22 +172,59 @@ func NewDefaultOptionsLCOW(id, owner string) *OptionsLCOW {
},
}

opts.UpdateBootFilesPath(context.TODO(), defaultLCOWOSBootFilesPath())

return opts
}

// UpdateBootFilesPath updates the LCOW BootFilesPath field and associated settings.
// Specifically, if [VhdFile] is found in path, RootFS is updated, and, if KernelDirect is set,
// KernelFile is also updated if [UncompressedKernelFile] is found in path.
//
// This is a nop if the current BootFilesPath is equal to path (case-insensitive).
func (opts *OptionsLCOW) UpdateBootFilesPath(ctx context.Context, path string) {
if p, err := filepath.Abs(path); err == nil {
path = p
} else {
// if its a filesystem issue, we'll error out elsewhere when we try to access the boot files
// otherwise, it might be transient, or a Go issue, so log and move on
log.G(ctx).WithFields(logrus.Fields{
logfields.Path: p,
logrus.ErrorKey: err,
}).Warning("could not make boot files path absolute")
}

if strings.EqualFold(opts.BootFilesPath, path) { // Windows is case-insensitive, so compare paths that way too
return
}

opts.BootFilesPath = path

if _, err := os.Stat(filepath.Join(opts.BootFilesPath, VhdFile)); err == nil {
// We have a rootfs.vhd in the boot files path. Use it over an initrd.img
opts.RootFSFile = VhdFile
opts.PreferredRootFSType = PreferredRootFSTypeVHD

log.G(ctx).WithFields(logrus.Fields{
logfields.UVMID: opts.ID,
VhdFile: filepath.Join(opts.BootFilesPath, VhdFile),
}).Debug("updated LCOW root filesystem to " + VhdFile)
}

if kernelDirectSupported {
if opts.KernelDirect {
// KernelDirect supports uncompressed kernel if the kernel is present.
// Default to uncompressed if on box. NOTE: If `kernel` is already
// uncompressed and simply named 'kernel' it will still be used
// uncompressed automatically.
if _, err := os.Stat(filepath.Join(opts.BootFilesPath, UncompressedKernelFile)); err == nil {
opts.KernelFile = UncompressedKernelFile

log.G(ctx).WithFields(logrus.Fields{
logfields.UVMID: opts.ID,
UncompressedKernelFile: filepath.Join(opts.BootFilesPath, UncompressedKernelFile),
}).Debug("updated LCOW kernel file to " + UncompressedKernelFile)
katiewasnothere marked this conversation as resolved.
Show resolved Hide resolved
}
}
return opts
}

// Get an acceptable number of processors given option and actual constraints.
Expand Down Expand Up @@ -759,7 +798,7 @@ func CreateLCOW(ctx context.Context, opts *OptionsLCOW) (_ *UtilityVM, err error
}

span.AddAttributes(trace.StringAttribute(logfields.UVMID, opts.ID))
log.G(ctx).WithField("options", fmt.Sprintf("%+v", opts)).Debug("uvm::CreateLCOW options")
log.G(ctx).WithField("options", log.Format(ctx, opts)).Debug("uvm::CreateLCOW options")

// We don't serialize OutputHandlerCreator so if it is missing we need to put it back to the default.
if opts.OutputHandlerCreator == nil {
Expand Down
5 changes: 3 additions & 2 deletions internal/uvm/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import (
// Unit tests for negative testing of input to uvm.Create()

func TestCreateBadBootFilesPath(t *testing.T) {
ctx := context.Background()
opts := NewDefaultOptionsLCOW(t.Name(), "")
opts.BootFilesPath = `c:\does\not\exist\I\hope`
opts.UpdateBootFilesPath(ctx, `c:\does\not\exist\I\hope`)

_, err := CreateLCOW(context.Background(), opts)
_, err := CreateLCOW(ctx, opts)
if err == nil || err.Error() != `kernel: 'c:\does\not\exist\I\hope\kernel' not found` {
t.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions test/functional/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ func requireFeatures(tb testing.TB, features ...string) {

func defaultLCOWOptions(tb testing.TB) *uvm.OptionsLCOW {
tb.Helper()
opts := testuvm.DefaultLCOWOptions(tb, util.CleanName(tb.Name()), hcsOwner)
opts := testuvm.DefaultLCOWOptions(context.TODO(), tb, util.CleanName(tb.Name()), hcsOwner)
if p := *flagLinuxBootFilesPath; p != "" {
opts.BootFilesPath = p
opts.UpdateBootFilesPath(context.TODO(), p)
}
return opts
}
Expand Down
6 changes: 3 additions & 3 deletions test/pkg/uvm/lcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ func init() {
// path.
//
// See [uvm.NewDefaultOptionsLCOW] for more information.
func DefaultLCOWOptions(tb testing.TB, id, owner string) *uvm.OptionsLCOW {
func DefaultLCOWOptions(ctx context.Context, tb testing.TB, id, owner string) *uvm.OptionsLCOW {
tb.Helper()
opts := uvm.NewDefaultOptionsLCOW(id, owner)
if lcowOSBootFiles != "" {
opts.BootFilesPath = lcowOSBootFiles
opts.UpdateBootFilesPath(ctx, lcowOSBootFiles)
}
return opts
}
Expand All @@ -55,7 +55,7 @@ func DefaultLCOWOptions(tb testing.TB, id, owner string) *uvm.OptionsLCOW {
// See [CreateAndStartLCOWFromOpts].
func CreateAndStartLCOW(ctx context.Context, tb testing.TB, id string) *uvm.UtilityVM {
tb.Helper()
return CreateAndStartLCOWFromOpts(ctx, tb, DefaultLCOWOptions(tb, id, ""))
return CreateAndStartLCOWFromOpts(ctx, tb, DefaultLCOWOptions(ctx, tb, id, ""))
}

// CreateAndStartLCOWFromOpts creates an LCOW utility VM with the specified options.
Expand Down
Loading