Skip to content
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
58 changes: 56 additions & 2 deletions internal/tools/uvmboot/lcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@ import (

"github.com/containerd/console"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"

"github.com/Microsoft/hcsshim/internal/cmd"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/logfields"
"github.com/Microsoft/hcsshim/internal/memory"
"github.com/Microsoft/hcsshim/internal/oci"
"github.com/Microsoft/hcsshim/internal/uvm"
)

const (
annotationsArgName = "annotation"
bootFilesPathArgName = "boot-files-path"
consolePipeArgName = "console-pipe"
kernelDirectArgName = "kernel-direct"
Expand Down Expand Up @@ -48,6 +52,11 @@ var lcowCommand = cli.Command{
Name: "lcow",
Usage: "Boot an LCOW UVM",
Flags: []cli.Flag{
cli.StringSliceFlag{
Name: annotationsArgName,
Usage: "Annotations in the form of `key=value` to apply to the uVM. Use repeat instances to add multiple. " +
"Annotations will be applied to uVM settings BEFORE all other settings.",
},
cli.StringFlag{
Name: kernelArgsArgName,
Value: "",
Expand Down Expand Up @@ -113,7 +122,7 @@ var lcowCommand = cli.Command{
},
cli.StringFlag{
Name: consolePipeArgName,
Usage: "Named pipe for serial console output (which will be enabled)",
Usage: "Named `pipe` for serial console output (which will be enabled)",
},
cli.BoolFlag{
Name: "tty,t",
Expand Down Expand Up @@ -159,7 +168,21 @@ func init() {
}

func createLCOWOptions(ctx context.Context, c *cli.Context, id string) (*uvm.OptionsLCOW, error) {
options := uvm.NewDefaultOptionsLCOW(id, "")
opt, err := oci.SpecToUVMCreateOpts(ctx,
&specs.Spec{
Linux: &specs.Linux{},
Annotations: parseAnnotations(ctx, c, annotationsArgName),
},
id, "",
)
if err != nil {
return nil, err
}
options, ok := opt.(*uvm.OptionsLCOW)
if !ok {
return nil, fmt.Errorf("unexpect uVM create options type: %T", opt)
}

setGlobalOptions(c, options.Options)

// boot
Expand Down Expand Up @@ -259,6 +282,37 @@ func createLCOWOptions(ctx context.Context, c *cli.Context, id string) (*uvm.Opt
return options, nil
}

// parseAnnotations parses the annotations from the [cli.StringSliceFlag] specified by `name`.
func parseAnnotations(ctx context.Context, c *cli.Context, name string) map[string]string {
ss := c.StringSlice(name)
annots := map[string]string{}

for _, s := range ss {
entry := log.G(ctx).WithField("flag-value", s)
k, v, found := strings.Cut(s, "=")

if !found {
entry.WithField(logrus.ErrorKey, "missing `=` in annotation").Warnf("invald %s flag value", name)
} else if k == "" || v == "" {
entry.WithField(logrus.ErrorKey, "empty annotation key or value").Warnf("invald %s flag value", name)
} else {
entry = entry.WithFields(logrus.Fields{
logfields.Key: k,
logfields.Value: v,
})
entry.Debugf("parsed %s flag", name)

if vv, ok := annots[k]; ok {
entry.WithField(logfields.Value+"-existing", vv).Warn("overriding existing annotation")
}

annots[k] = v
}
}

return annots
}

func runLCOW(ctx context.Context, options *uvm.OptionsLCOW, c *cli.Context) error {
vm, err := uvm.CreateLCOW(ctx, options)
if err != nil {
Expand Down
14 changes: 8 additions & 6 deletions internal/tools/uvmboot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package main

import (
"fmt"
"log"
"os"
"sync"
"time"
Expand Down Expand Up @@ -76,7 +75,7 @@ func main() {
},
cli.BoolFlag{
Name: "debug",
Usage: "Enable debug information",
Usage: "Increase logging verbosity",
Destination: &debug,
},
cli.BoolFlag{
Expand All @@ -98,14 +97,15 @@ func main() {

app.Before = func(c *cli.Context) error {
if !winapi.IsElevated() {
log.Fatal(c.App.Name + " must be run in an elevated context")
return fmt.Errorf(c.App.Name + " must be run in an elevated context")
}

lvl := logrus.WarnLevel
if debug {
logrus.SetLevel(logrus.DebugLevel)
} else {
logrus.SetLevel(logrus.WarnLevel)
// as a debugging tool, opt for more logs over less
lvl = logrus.TraceLevel
}
logrus.SetLevel(lvl)

return nil
}
Expand All @@ -116,6 +116,7 @@ func main() {
}

func setGlobalOptions(c *cli.Context, options *uvm.Options) {
// TODO: create appropriate spec for (conf) WCOW and handle annotations here
if c.GlobalIsSet(cpusArgName) {
options.ProcessorCount = int32(c.GlobalUint64(cpusArgName))
}
Expand All @@ -138,6 +139,7 @@ func setGlobalOptions(c *cli.Context, options *uvm.Options) {
}
options.ResourcePartitionID = &rpID
}
// TODO: create common arg for console pipe and set `uvmConsolePipe` as the default value
// Always set the console pipe in uvmboot, it helps with testing/debugging
options.ConsolePipe = uvmConsolePipe
}
Expand Down
31 changes: 17 additions & 14 deletions internal/tools/uvmboot/mounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,23 +87,26 @@ type mount struct {
writable bool
}

// parseMounts parses the mounts stored under the cli StringSlice argument, `n`.
func parseMounts(ctx context.Context, c *cli.Context, n string) []mount {
if c.IsSet(n) {
ss := c.StringSlice(n)
ms := make([]mount, 0, len(ss))
for _, s := range ss {
log.G(ctx).Debugf("parsing %q", s)

if m, err := mountFromString(s); err == nil {
ms = append(ms, m)
}
}
// parseMounts parses the mounts from the [cli.StringSliceFlag] specified by `name`.
func parseMounts(ctx context.Context, c *cli.Context, name string) []mount {
if !c.IsSet(name) {
return nil
}

return ms
ss := c.StringSlice(name)
ms := make([]mount, 0, len(ss))
for _, s := range ss {
entry := log.G(ctx).WithField("flag-value", s)

if m, err := mountFromString(s); err != nil {
entry.WithError(err).Warnf("invald %s flag value", name)
} else {
entry.Debugf("parsed %s flag", name)
ms = append(ms, m)
}
}

return nil
return ms
}

func mountFromString(s string) (m mount, _ error) {
Expand Down