Adds support for Windows container images built by Docker that contain the ArgsEscaped boolean in the ImageConfig. This is a non-OCI entry that tells the runtime that the Entrypoint and/or Cmd are a single element array with the args pre-escaped into a single CommandLine that should be passed directly to Windows rather than passed as an args array which will be additionally escaped. Signed-off-by: Justin Terry <jlterry@amazon.com>
		
			
				
	
	
		
			549 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			549 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   Copyright The containerd Authors.
 | 
						|
 | 
						|
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
   you may not use this file except in compliance with the License.
 | 
						|
   You may obtain a copy of the License at
 | 
						|
 | 
						|
       http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
   Unless required by applicable law or agreed to in writing, software
 | 
						|
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
   See the License for the specific language governing permissions and
 | 
						|
   limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package oci
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/json"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/containerd/containerd/containers"
 | 
						|
	"github.com/containerd/containerd/namespaces"
 | 
						|
 | 
						|
	"github.com/opencontainers/go-digest"
 | 
						|
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | 
						|
	"github.com/opencontainers/runtime-spec/specs-go"
 | 
						|
)
 | 
						|
 | 
						|
func TestWithCPUCount(t *testing.T) {
 | 
						|
	var (
 | 
						|
		ctx = namespaces.WithNamespace(context.Background(), "testing")
 | 
						|
		c   = containers.Container{ID: t.Name()}
 | 
						|
		cpu = uint64(8)
 | 
						|
		o   = WithWindowsCPUCount(cpu)
 | 
						|
	)
 | 
						|
	// Test with all three supported scenarios
 | 
						|
	platforms := []string{"", "linux/amd64", "windows/amd64"}
 | 
						|
	for _, p := range platforms {
 | 
						|
		var spec *Spec
 | 
						|
		var err error
 | 
						|
		if p == "" {
 | 
						|
			t.Log("Testing GenerateSpec default platform")
 | 
						|
			spec, err = GenerateSpec(ctx, nil, &c, o)
 | 
						|
		} else {
 | 
						|
			t.Logf("Testing GenerateSpecWithPlatform with platform: '%s'", p)
 | 
						|
			spec, err = GenerateSpecWithPlatform(ctx, nil, p, &c, o)
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			t.Fatalf("failed to generate spec with: %v", err)
 | 
						|
		}
 | 
						|
		if *spec.Windows.Resources.CPU.Count != cpu {
 | 
						|
			t.Fatalf("spec.Windows.Resources.CPU.Count expected: %v, got: %v", cpu, *spec.Windows.Resources.CPU.Count)
 | 
						|
		}
 | 
						|
		if spec.Linux != nil && spec.Linux.Resources != nil && spec.Linux.Resources.CPU != nil {
 | 
						|
			t.Fatalf("spec.Linux.Resources.CPU section should not be set on GOOS=windows")
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestWithWindowsIgnoreFlushesDuringBoot(t *testing.T) {
 | 
						|
	var (
 | 
						|
		ctx = namespaces.WithNamespace(context.Background(), "testing")
 | 
						|
		c   = containers.Container{ID: t.Name()}
 | 
						|
		o   = WithWindowsIgnoreFlushesDuringBoot()
 | 
						|
	)
 | 
						|
	// Test with all supported scenarios
 | 
						|
	platforms := []string{"", "windows/amd64"}
 | 
						|
	for _, p := range platforms {
 | 
						|
		var spec *Spec
 | 
						|
		var err error
 | 
						|
		if p == "" {
 | 
						|
			t.Log("Testing GenerateSpec default platform")
 | 
						|
			spec, err = GenerateSpec(ctx, nil, &c, o)
 | 
						|
		} else {
 | 
						|
			t.Logf("Testing GenerateSpecWithPlatform with platform: '%s'", p)
 | 
						|
			spec, err = GenerateSpecWithPlatform(ctx, nil, p, &c, o)
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			t.Fatalf("failed to generate spec with: %v", err)
 | 
						|
		}
 | 
						|
		if spec.Windows.IgnoreFlushesDuringBoot != true {
 | 
						|
			t.Fatalf("spec.Windows.IgnoreFlushesDuringBoot expected: true")
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestWithWindowNetworksAllowUnqualifiedDNSQuery(t *testing.T) {
 | 
						|
	var (
 | 
						|
		ctx = namespaces.WithNamespace(context.Background(), "testing")
 | 
						|
		c   = containers.Container{ID: t.Name()}
 | 
						|
		o   = WithWindowNetworksAllowUnqualifiedDNSQuery()
 | 
						|
	)
 | 
						|
	// Test with all supported scenarios
 | 
						|
	platforms := []string{"", "windows/amd64"}
 | 
						|
	for _, p := range platforms {
 | 
						|
		var spec *Spec
 | 
						|
		var err error
 | 
						|
		if p == "" {
 | 
						|
			t.Log("Testing GenerateSpec default platform")
 | 
						|
			spec, err = GenerateSpec(ctx, nil, &c, o)
 | 
						|
		} else {
 | 
						|
			t.Logf("Testing GenerateSpecWithPlatform with platform: '%s'", p)
 | 
						|
			spec, err = GenerateSpecWithPlatform(ctx, nil, p, &c, o)
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			t.Fatalf("failed to generate spec with: %v", err)
 | 
						|
		}
 | 
						|
		if spec.Windows.Network.AllowUnqualifiedDNSQuery != true {
 | 
						|
			t.Fatalf("spec.Windows.Network.AllowUnqualifiedDNSQuery expected: true")
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func newFakeArgsEscapedImage(config ocispec.ImageConfig) (Image, error) {
 | 
						|
	type imageExtended struct {
 | 
						|
		Config struct {
 | 
						|
			ocispec.ImageConfig
 | 
						|
			ArgsEscaped bool `json:"ArgsEscaped,omitempty"`
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Copy to extended format.
 | 
						|
	configExtended := imageExtended{}
 | 
						|
	configExtended.Config.ImageConfig = config
 | 
						|
	configExtended.Config.ArgsEscaped = true
 | 
						|
 | 
						|
	configBlob, err := json.Marshal(configExtended)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	configDescriptor := ocispec.Descriptor{
 | 
						|
		MediaType: ocispec.MediaTypeImageConfig,
 | 
						|
		Digest:    digest.NewDigestFromBytes(digest.SHA256, configBlob),
 | 
						|
	}
 | 
						|
 | 
						|
	return fakeImage{
 | 
						|
		config: configDescriptor,
 | 
						|
		blobs: map[string]blob{
 | 
						|
			configDescriptor.Digest.String(): configBlob,
 | 
						|
		},
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// TestWithProcessArgsOverwritesWithImage verifies that when calling
 | 
						|
// WithImageConfig followed by WithProcessArgs when `ArgsEscaped==false` that
 | 
						|
// the process args overwrite the image args.
 | 
						|
func TestWithProcessArgsOverwritesWithImage(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	img, err := newFakeImage(ocispec.Image{
 | 
						|
		Config: ocispec.ImageConfig{
 | 
						|
			Entrypoint: []string{"powershell.exe", "-Command", "Write-Host Hello"},
 | 
						|
			Cmd:        []string{"cmd.exe", "/S", "/C", "echo Hello"},
 | 
						|
		},
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	s := Spec{
 | 
						|
		Version: specs.Version,
 | 
						|
		Root:    &specs.Root{},
 | 
						|
		Windows: &specs.Windows{},
 | 
						|
	}
 | 
						|
 | 
						|
	args := []string{"cmd.exe", "echo", "should be set"}
 | 
						|
	opts := []SpecOpts{
 | 
						|
		WithImageConfig(img),
 | 
						|
		WithProcessArgs(args...),
 | 
						|
	}
 | 
						|
 | 
						|
	for _, opt := range opts {
 | 
						|
		if err := opt(nil, nil, nil, &s); err != nil {
 | 
						|
			t.Fatal(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if err := assertEqualsStringArrays(args, s.Process.Args); err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	if s.Process.CommandLine != "" {
 | 
						|
		t.Fatalf("Expected empty CommandLine, got: '%s'", s.Process.CommandLine)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TestWithProcessArgsOverwritesWithImageArgsEscaped verifies that when calling
 | 
						|
// WithImageConfig followed by WithProcessArgs when `ArgsEscaped==true` that the
 | 
						|
// process args overwrite the image args.
 | 
						|
func TestWithProcessArgsOverwritesWithImageArgsEscaped(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	img, err := newFakeArgsEscapedImage(ocispec.ImageConfig{
 | 
						|
		Entrypoint: []string{`powershell.exe -Command "C:\My Data\MyExe.exe" -arg1 "-arg2 value2"`},
 | 
						|
		Cmd:        []string{`cmd.exe /S /C "C:\test path\test.exe"`},
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	s := Spec{
 | 
						|
		Version: specs.Version,
 | 
						|
		Root:    &specs.Root{},
 | 
						|
		Windows: &specs.Windows{},
 | 
						|
	}
 | 
						|
 | 
						|
	args := []string{"cmd.exe", "echo", "should be set"}
 | 
						|
	opts := []SpecOpts{
 | 
						|
		WithImageConfig(img),
 | 
						|
		WithProcessArgs(args...),
 | 
						|
	}
 | 
						|
 | 
						|
	for _, opt := range opts {
 | 
						|
		if err := opt(nil, nil, nil, &s); err != nil {
 | 
						|
			t.Fatal(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if err := assertEqualsStringArrays(args, s.Process.Args); err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	if s.Process.CommandLine != "" {
 | 
						|
		t.Fatalf("Expected empty CommandLine, got: '%s'", s.Process.CommandLine)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TestWithImageOverwritesWithProcessArgs verifies that when calling
 | 
						|
// WithProcessArgs followed by WithImageConfig `ArgsEscaped==false` that the
 | 
						|
// image args overwrites process args.
 | 
						|
func TestWithImageOverwritesWithProcessArgs(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	img, err := newFakeImage(ocispec.Image{
 | 
						|
		Config: ocispec.ImageConfig{
 | 
						|
			Entrypoint: []string{"powershell.exe", "-Command"},
 | 
						|
			Cmd:        []string{"Write-Host", "echo Hello"},
 | 
						|
		},
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	s := Spec{
 | 
						|
		Version: specs.Version,
 | 
						|
		Root:    &specs.Root{},
 | 
						|
		Windows: &specs.Windows{},
 | 
						|
	}
 | 
						|
 | 
						|
	opts := []SpecOpts{
 | 
						|
		WithProcessArgs("cmd.exe", "echo", "should not be set"),
 | 
						|
		WithImageConfig(img),
 | 
						|
	}
 | 
						|
 | 
						|
	for _, opt := range opts {
 | 
						|
		if err := opt(nil, nil, nil, &s); err != nil {
 | 
						|
			t.Fatal(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	expectedArgs := []string{"powershell.exe", "-Command", "Write-Host", "echo Hello"}
 | 
						|
	if err := assertEqualsStringArrays(expectedArgs, s.Process.Args); err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	if s.Process.CommandLine != "" {
 | 
						|
		t.Fatalf("Expected empty CommandLine, got: '%s'", s.Process.CommandLine)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TestWithImageOverwritesWithProcessArgs verifies that when calling
 | 
						|
// WithProcessArgs followed by WithImageConfig `ArgsEscaped==true` that the
 | 
						|
// image args overwrites process args.
 | 
						|
func TestWithImageArgsEscapedOverwritesWithProcessArgs(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	img, err := newFakeArgsEscapedImage(ocispec.ImageConfig{
 | 
						|
		Entrypoint: []string{`powershell.exe -Command "C:\My Data\MyExe.exe" -arg1 "-arg2 value2"`},
 | 
						|
		Cmd:        []string{`cmd.exe /S /C "C:\test path\test.exe"`},
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	s := Spec{
 | 
						|
		Version: specs.Version,
 | 
						|
		Root:    &specs.Root{},
 | 
						|
		Windows: &specs.Windows{},
 | 
						|
	}
 | 
						|
 | 
						|
	opts := []SpecOpts{
 | 
						|
		WithProcessArgs("cmd.exe", "echo", "should not be set"),
 | 
						|
		WithImageConfig(img),
 | 
						|
	}
 | 
						|
 | 
						|
	expectedCommandLine := `powershell.exe -Command "C:\My Data\MyExe.exe" -arg1 "-arg2 value2" "cmd.exe /S /C \"C:\test path\test.exe\""`
 | 
						|
 | 
						|
	for _, opt := range opts {
 | 
						|
		if err := opt(nil, nil, nil, &s); err != nil {
 | 
						|
			t.Fatal(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if s.Process.Args != nil {
 | 
						|
		t.Fatalf("Expected empty Process.Args, got: '%v'", s.Process.Args)
 | 
						|
	}
 | 
						|
	if expectedCommandLine != s.Process.CommandLine {
 | 
						|
		t.Fatalf("Expected CommandLine '%s', got: '%s'", expectedCommandLine, s.Process.CommandLine)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestWithImageConfigArgsWindows(t *testing.T) {
 | 
						|
	testcases := []struct {
 | 
						|
		name       string
 | 
						|
		entrypoint []string
 | 
						|
		cmd        []string
 | 
						|
		args       []string
 | 
						|
 | 
						|
		expectError bool
 | 
						|
		// When ArgsEscaped==false we always expect args and CommandLine==""
 | 
						|
		expectedArgs []string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			// This is not really a valid test case since Docker would have made
 | 
						|
			// the default cmd to be the shell. So just verify it hits the error
 | 
						|
			// case we expect.
 | 
						|
			name:        "EmptyEntrypoint_EmptyCmd_EmptyArgs",
 | 
						|
			entrypoint:  nil,
 | 
						|
			cmd:         nil,
 | 
						|
			args:        nil,
 | 
						|
			expectError: true,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:         "EmptyEntrypoint_EmptyCmd_Args",
 | 
						|
			entrypoint:   nil,
 | 
						|
			cmd:          nil,
 | 
						|
			args:         []string{"additional", "args"},
 | 
						|
			expectedArgs: []string{"additional", "args"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:         "EmptyEntrypoint_Cmd_EmptyArgs",
 | 
						|
			entrypoint:   nil,
 | 
						|
			cmd:          []string{"cmd", "args"},
 | 
						|
			args:         nil,
 | 
						|
			expectedArgs: []string{"cmd", "args"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:         "EmptyEntrypoint_Cmd_Args",
 | 
						|
			entrypoint:   nil,
 | 
						|
			cmd:          []string{"cmd", "args"},
 | 
						|
			args:         []string{"additional", "args"},
 | 
						|
			expectedArgs: []string{"additional", "args"}, // Args overwrite Cmd
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:         "Entrypoint_EmptyCmd_EmptyArgs",
 | 
						|
			entrypoint:   []string{"entrypoint", "args"},
 | 
						|
			cmd:          nil,
 | 
						|
			args:         nil,
 | 
						|
			expectedArgs: []string{"entrypoint", "args"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:         "Entrypoint_EmptyCmd_Args",
 | 
						|
			entrypoint:   []string{"entrypoint", "args"},
 | 
						|
			cmd:          nil,
 | 
						|
			args:         []string{"additional", "args"},
 | 
						|
			expectedArgs: []string{"entrypoint", "args", "additional", "args"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:         "Entrypoint_Cmd_EmptyArgs",
 | 
						|
			entrypoint:   []string{"entrypoint", "args"},
 | 
						|
			cmd:          []string{"cmd", "args"},
 | 
						|
			args:         nil,
 | 
						|
			expectedArgs: []string{"entrypoint", "args", "cmd", "args"},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:         "Entrypoint_Cmd_Args",
 | 
						|
			entrypoint:   []string{"entrypoint", "args"},
 | 
						|
			cmd:          []string{"cmd", "args"},
 | 
						|
			args:         []string{"additional", "args"}, // Args overwrites Cmd
 | 
						|
			expectedArgs: []string{"entrypoint", "args", "additional", "args"},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tc := range testcases {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			img, err := newFakeImage(ocispec.Image{
 | 
						|
				Config: ocispec.ImageConfig{
 | 
						|
					Entrypoint: tc.entrypoint,
 | 
						|
					Cmd:        tc.cmd,
 | 
						|
				},
 | 
						|
			})
 | 
						|
			if err != nil {
 | 
						|
				t.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			s := Spec{
 | 
						|
				Version: specs.Version,
 | 
						|
				Root:    &specs.Root{},
 | 
						|
				Windows: &specs.Windows{},
 | 
						|
			}
 | 
						|
 | 
						|
			opts := []SpecOpts{
 | 
						|
				WithImageConfigArgs(img, tc.args),
 | 
						|
			}
 | 
						|
 | 
						|
			for _, opt := range opts {
 | 
						|
				if err := opt(nil, nil, nil, &s); err != nil {
 | 
						|
					if tc.expectError {
 | 
						|
						continue
 | 
						|
					}
 | 
						|
					t.Fatal(err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if err := assertEqualsStringArrays(tc.expectedArgs, s.Process.Args); err != nil {
 | 
						|
				t.Fatal(err)
 | 
						|
			}
 | 
						|
			if s.Process.CommandLine != "" {
 | 
						|
				t.Fatalf("Expected empty CommandLine, got: '%s'", s.Process.CommandLine)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestWithImageConfigArgsEscapedWindows(t *testing.T) {
 | 
						|
	testcases := []struct {
 | 
						|
		name       string
 | 
						|
		entrypoint []string
 | 
						|
		cmd        []string
 | 
						|
		args       []string
 | 
						|
 | 
						|
		expectError         bool
 | 
						|
		expectedArgs        []string
 | 
						|
		expectedCommandLine string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			// This is not really a valid test case since Docker would have made
 | 
						|
			// the default cmd to be the shell. So just verify it hits the error
 | 
						|
			// case we expect.
 | 
						|
			name:                "EmptyEntrypoint_EmptyCmd_EmptyArgs",
 | 
						|
			entrypoint:          nil,
 | 
						|
			cmd:                 nil,
 | 
						|
			args:                nil,
 | 
						|
			expectError:         true,
 | 
						|
			expectedArgs:        nil,
 | 
						|
			expectedCommandLine: "",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// This case is special for ArgsEscaped, since there is no Image
 | 
						|
			// Default Args should be passed as ProcessArgs not as Cmdline
 | 
						|
			name:                "EmptyEntrypoint_EmptyCmd_Args",
 | 
						|
			entrypoint:          nil,
 | 
						|
			cmd:                 nil,
 | 
						|
			args:                []string{"additional", "-args", "hello world"},
 | 
						|
			expectedArgs:        []string{"additional", "-args", "hello world"},
 | 
						|
			expectedCommandLine: "",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:                "EmptyEntrypoint_Cmd_EmptyArgs",
 | 
						|
			entrypoint:          nil,
 | 
						|
			cmd:                 []string{`cmd -args "hello world"`},
 | 
						|
			args:                nil,
 | 
						|
			expectedCommandLine: `cmd -args "hello world"`,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// This case is a second special case for ArgsEscaped, since Args
 | 
						|
			// overwrite Cmd the args are not from the image, so ArgsEscaped
 | 
						|
			// should be ignored, and passed as Args not CommandLine.
 | 
						|
			name:                "EmptyEntrypoint_Cmd_Args",
 | 
						|
			entrypoint:          nil,
 | 
						|
			cmd:                 []string{`cmd -args "hello world"`},
 | 
						|
			args:                []string{"additional", "args"},
 | 
						|
			expectedArgs:        []string{"additional", "args"}, // Args overwrite Cmd
 | 
						|
			expectedCommandLine: "",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:                "Entrypoint_EmptyCmd_EmptyArgs",
 | 
						|
			entrypoint:          []string{`"C:\My Folder\MyProcess.exe" -arg1 "test value"`},
 | 
						|
			cmd:                 nil,
 | 
						|
			args:                nil,
 | 
						|
			expectedCommandLine: `"C:\My Folder\MyProcess.exe" -arg1 "test value"`,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:                "Entrypoint_EmptyCmd_Args",
 | 
						|
			entrypoint:          []string{`"C:\My Folder\MyProcess.exe" -arg1 "test value"`},
 | 
						|
			cmd:                 nil,
 | 
						|
			args:                []string{"additional", "args with spaces"},
 | 
						|
			expectedCommandLine: `"C:\My Folder\MyProcess.exe" -arg1 "test value" additional "args with spaces"`,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			// This case will not work in Docker today so adding the test to
 | 
						|
			// confirm we fail in the same way. Although the appending of
 | 
						|
			// Entrypoint + " " + Cmd here works, Cmd is double escaped and the
 | 
						|
			// container would not launch. This is because when Docker built
 | 
						|
			// such an image it escaped both Entrypoint and Cmd. However the
 | 
						|
			// docs say that CMD should always be appened to entrypoint if not
 | 
						|
			// overwritten so this results in an incorrect cmdline.
 | 
						|
			name:                "Entrypoint_Cmd_EmptyArgs",
 | 
						|
			entrypoint:          []string{`"C:\My Folder\MyProcess.exe" -arg1 "test value"`},
 | 
						|
			cmd:                 []string{`cmd -args "hello world"`},
 | 
						|
			args:                nil,
 | 
						|
			expectedCommandLine: `"C:\My Folder\MyProcess.exe" -arg1 "test value" "cmd -args \"hello world\""`,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:                "Entrypoint_Cmd_Args",
 | 
						|
			entrypoint:          []string{`"C:\My Folder\MyProcess.exe" -arg1 "test value"`},
 | 
						|
			cmd:                 []string{`cmd -args "hello world"`},
 | 
						|
			args:                []string{"additional", "args with spaces"}, // Args overwrites Cmd
 | 
						|
			expectedCommandLine: `"C:\My Folder\MyProcess.exe" -arg1 "test value" additional "args with spaces"`,
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tc := range testcases {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			img, err := newFakeArgsEscapedImage(ocispec.ImageConfig{
 | 
						|
				Entrypoint: tc.entrypoint,
 | 
						|
				Cmd:        tc.cmd,
 | 
						|
			})
 | 
						|
			if err != nil {
 | 
						|
				t.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			s := Spec{
 | 
						|
				Version: specs.Version,
 | 
						|
				Root:    &specs.Root{},
 | 
						|
				Windows: &specs.Windows{},
 | 
						|
			}
 | 
						|
 | 
						|
			opts := []SpecOpts{
 | 
						|
				WithImageConfigArgs(img, tc.args),
 | 
						|
			}
 | 
						|
 | 
						|
			for _, opt := range opts {
 | 
						|
				if err := opt(nil, nil, nil, &s); err != nil {
 | 
						|
					if tc.expectError {
 | 
						|
						continue
 | 
						|
					}
 | 
						|
					t.Fatal(err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if err := assertEqualsStringArrays(tc.expectedArgs, s.Process.Args); err != nil {
 | 
						|
				t.Fatal(err)
 | 
						|
			}
 | 
						|
			if tc.expectedCommandLine != s.Process.CommandLine {
 | 
						|
				t.Fatalf("Expected CommandLine: '%s', got: '%s'", tc.expectedCommandLine, s.Process.CommandLine)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 |