 de3d9993f5
			
		
	
	de3d9993f5
	
	
	
		
			
			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)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 |