From ef910311e8142b7fda83377030692376266aeba6 Mon Sep 17 00:00:00 2001 From: "Justin Terry (VM)" Date: Thu, 6 Sep 2018 11:05:45 -0700 Subject: [PATCH] Add a Windows section for Linux oci on LCOW When creating a default OCI spec on Windows that is targeting the LCOW platform it needs to contain a Windows section as well. This adds the Windows section by default. It also protects against this case for all OCI creation that doesnt use the OCI package in the runhcs-shim. Signed-off-by: Justin Terry (VM) --- oci/spec.go | 31 ++++++++++++++++++----------- oci/spec_opts.go | 16 ++------------- oci/spec_test.go | 38 ++++++++++++++++++++++++++++++++++++ runtime/v2/runhcs/service.go | 8 ++++++++ 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/oci/spec.go b/oci/spec.go index f46dc1458..6fb31e454 100644 --- a/oci/spec.go +++ b/oci/spec.go @@ -19,6 +19,7 @@ package oci import ( "context" "path/filepath" + "runtime" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/platforms" @@ -51,24 +52,32 @@ func GenerateSpec(ctx context.Context, client Client, c *containers.Container, o // GenerateSpecWithPlatform will generate a default spec from the provided image // for use as a containerd container in the platform requested. func GenerateSpecWithPlatform(ctx context.Context, client Client, platform string, c *containers.Container, opts ...SpecOpts) (*Spec, error) { - plat, err := platforms.Parse(platform) - if err != nil { - return nil, err - } - var s Spec - if plat.OS == "windows" { - err = populateDefaultWindowsSpec(ctx, &s, c.ID) - } else { - err = populateDefaultUnixSpec(ctx, &s, c.ID) - } - if err != nil { + if err := generateDefaultSpecWithPlatform(ctx, platform, c.ID, &s); err != nil { return nil, err } return &s, ApplyOpts(ctx, client, c, &s, opts...) } +func generateDefaultSpecWithPlatform(ctx context.Context, platform, id string, s *Spec) error { + plat, err := platforms.Parse(platform) + if err != nil { + return err + } + + if plat.OS == "windows" { + err = populateDefaultWindowsSpec(ctx, s, id) + } else { + err = populateDefaultUnixSpec(ctx, s, id) + if err == nil && runtime.GOOS == "windows" { + // To run LCOW we have a Linux and Windows section. Add an empty one now. + s.Windows = &specs.Windows{} + } + } + return err +} + // ApplyOpts applys the options to the given spec, injecting data from the // context, client and container instance. func ApplyOpts(ctx context.Context, client Client, c *containers.Container, s *Spec, opts ...SpecOpts) error { diff --git a/oci/spec_opts.go b/oci/spec_opts.go index 5feb476bc..47ffd8c9f 100644 --- a/oci/spec_opts.go +++ b/oci/spec_opts.go @@ -23,7 +23,6 @@ import ( "io/ioutil" "os" "path/filepath" - "runtime" "strconv" "strings" @@ -91,10 +90,7 @@ func setCapabilities(s *Spec) { // Use as the first option to clear the spec, then apply options afterwards. func WithDefaultSpec() SpecOpts { return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { - if runtime.GOOS == "windows" { - return populateDefaultWindowsSpec(ctx, s, c.ID) - } - return populateDefaultUnixSpec(ctx, s, c.ID) + return generateDefaultSpecWithPlatform(ctx, platforms.DefaultString(), c.ID, s) } } @@ -104,15 +100,7 @@ func WithDefaultSpec() SpecOpts { // Use as the first option to clear the spec, then apply options afterwards. func WithDefaultSpecForPlatform(platform string) SpecOpts { return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { - plat, err := platforms.Parse(platform) - if err != nil { - return err - } - - if plat.OS == "windows" { - return populateDefaultWindowsSpec(ctx, s, c.ID) - } - return populateDefaultUnixSpec(ctx, s, c.ID) + return generateDefaultSpecWithPlatform(ctx, platform, c.ID, s) } } diff --git a/oci/spec_test.go b/oci/spec_test.go index 09fe12d9c..7e04b960e 100644 --- a/oci/spec_test.go +++ b/oci/spec_test.go @@ -73,6 +73,44 @@ func TestGenerateSpec(t *testing.T) { } } +func TestGenerateSpecWithPlatform(t *testing.T) { + t.Parallel() + + ctx := namespaces.WithNamespace(context.Background(), "testing") + platforms := []string{"windows/amd64", "linux/amd64"} + for _, p := range platforms { + t.Logf("Testing platform: %s", p) + s, err := GenerateSpecWithPlatform(ctx, nil, p, &containers.Container{ID: t.Name()}) + if err != nil { + t.Fatalf("failed to generate spec: %v", err) + } + + if s.Root == nil { + t.Fatal("expected non nil Root section.") + } + if s.Process == nil { + t.Fatal("expected non nil Process section.") + } + if p == "windows/amd64" { + if s.Linux != nil { + t.Fatal("expected nil Linux section") + } + if s.Windows == nil { + t.Fatal("expected non nil Windows section") + } + } else { + if s.Linux == nil { + t.Fatal("expected non nil Linux section") + } + if runtime.GOOS == "windows" && s.Windows == nil { + t.Fatal("expected non nil Windows section for LCOW") + } else if runtime.GOOS != "windows" && s.Windows != nil { + t.Fatal("expected nil Windows section") + } + } + } +} + func TestSpecWithTTY(t *testing.T) { t.Parallel() diff --git a/runtime/v2/runhcs/service.go b/runtime/v2/runhcs/service.go index 0e0b0c5c7..66010f25f 100644 --- a/runtime/v2/runhcs/service.go +++ b/runtime/v2/runhcs/service.go @@ -314,6 +314,14 @@ func writeMountsToConfig(bundle string, mounts []*containerd_types.Mount) error return errors.Wrap(err, "failed to seek to 0 in config.json") } + // If we are creating LCOW make sure that spec.Windows is filled out before + // appending layer folders. + if m.Type == "lcow-layer" && spec.Windows == nil { + spec.Windows = &oci.Windows{ + HyperV: &oci.WindowsHyperV{}, + } + } + // Append the parents spec.Windows.LayerFolders = append(spec.Windows.LayerFolders, parentLayerPaths...) // Append the scratch