github.com/justincormack/cli@v0.0.0-20201215022714-831ebeae9675/cli/command/container/create_test.go (about)

     1  package container
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"runtime"
    10  	"sort"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/docker/cli/cli/config/configfile"
    15  	"github.com/docker/cli/internal/test"
    16  	"github.com/docker/cli/internal/test/notary"
    17  	"github.com/docker/docker/api/types"
    18  	"github.com/docker/docker/api/types/container"
    19  	"github.com/docker/docker/api/types/network"
    20  	"github.com/google/go-cmp/cmp"
    21  	specs "github.com/opencontainers/image-spec/specs-go/v1"
    22  	"gotest.tools/v3/assert"
    23  	is "gotest.tools/v3/assert/cmp"
    24  	"gotest.tools/v3/fs"
    25  	"gotest.tools/v3/golden"
    26  )
    27  
    28  func TestCIDFileNoOPWithNoFilename(t *testing.T) {
    29  	file, err := newCIDFile("")
    30  	assert.NilError(t, err)
    31  	assert.DeepEqual(t, &cidFile{}, file, cmp.AllowUnexported(cidFile{}))
    32  
    33  	assert.NilError(t, file.Write("id"))
    34  	assert.NilError(t, file.Close())
    35  }
    36  
    37  func TestNewCIDFileWhenFileAlreadyExists(t *testing.T) {
    38  	tempfile := fs.NewFile(t, "test-cid-file")
    39  	defer tempfile.Remove()
    40  
    41  	_, err := newCIDFile(tempfile.Path())
    42  	assert.ErrorContains(t, err, "Container ID file found")
    43  }
    44  
    45  func TestCIDFileCloseWithNoWrite(t *testing.T) {
    46  	tempdir := fs.NewDir(t, "test-cid-file")
    47  	defer tempdir.Remove()
    48  
    49  	path := tempdir.Join("cidfile")
    50  	file, err := newCIDFile(path)
    51  	assert.NilError(t, err)
    52  	assert.Check(t, is.Equal(file.path, path))
    53  
    54  	assert.NilError(t, file.Close())
    55  	_, err = os.Stat(path)
    56  	assert.Check(t, os.IsNotExist(err))
    57  }
    58  
    59  func TestCIDFileCloseWithWrite(t *testing.T) {
    60  	tempdir := fs.NewDir(t, "test-cid-file")
    61  	defer tempdir.Remove()
    62  
    63  	path := tempdir.Join("cidfile")
    64  	file, err := newCIDFile(path)
    65  	assert.NilError(t, err)
    66  
    67  	content := "id"
    68  	assert.NilError(t, file.Write(content))
    69  
    70  	actual, err := ioutil.ReadFile(path)
    71  	assert.NilError(t, err)
    72  	assert.Check(t, is.Equal(content, string(actual)))
    73  
    74  	assert.NilError(t, file.Close())
    75  	_, err = os.Stat(path)
    76  	assert.NilError(t, err)
    77  }
    78  
    79  func TestCreateContainerImagePullPolicy(t *testing.T) {
    80  	imageName := "does-not-exist-locally"
    81  	containerID := "abcdef"
    82  	config := &containerConfig{
    83  		Config: &container.Config{
    84  			Image: imageName,
    85  		},
    86  		HostConfig: &container.HostConfig{},
    87  	}
    88  
    89  	cases := []struct {
    90  		PullPolicy      string
    91  		ExpectedPulls   int
    92  		ExpectedBody    container.ContainerCreateCreatedBody
    93  		ExpectedErrMsg  string
    94  		ResponseCounter int
    95  	}{
    96  		{
    97  			PullPolicy:    PullImageMissing,
    98  			ExpectedPulls: 1,
    99  			ExpectedBody:  container.ContainerCreateCreatedBody{ID: containerID},
   100  		}, {
   101  			PullPolicy:      PullImageAlways,
   102  			ExpectedPulls:   1,
   103  			ExpectedBody:    container.ContainerCreateCreatedBody{ID: containerID},
   104  			ResponseCounter: 1, // This lets us return a container on the first pull
   105  		}, {
   106  			PullPolicy:     PullImageNever,
   107  			ExpectedPulls:  0,
   108  			ExpectedErrMsg: "error fake not found",
   109  		},
   110  	}
   111  	for _, c := range cases {
   112  		c := c
   113  		pullCounter := 0
   114  
   115  		client := &fakeClient{
   116  			createContainerFunc: func(
   117  				config *container.Config,
   118  				hostConfig *container.HostConfig,
   119  				networkingConfig *network.NetworkingConfig,
   120  				platform *specs.Platform,
   121  				containerName string,
   122  			) (container.ContainerCreateCreatedBody, error) {
   123  				defer func() { c.ResponseCounter++ }()
   124  				switch c.ResponseCounter {
   125  				case 0:
   126  					return container.ContainerCreateCreatedBody{}, fakeNotFound{}
   127  				default:
   128  					return container.ContainerCreateCreatedBody{ID: containerID}, nil
   129  				}
   130  			},
   131  			imageCreateFunc: func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
   132  				defer func() { pullCounter++ }()
   133  				return ioutil.NopCloser(strings.NewReader("")), nil
   134  			},
   135  			infoFunc: func() (types.Info, error) {
   136  				return types.Info{IndexServerAddress: "http://indexserver"}, nil
   137  			},
   138  		}
   139  		cli := test.NewFakeCli(client)
   140  		body, err := createContainer(context.Background(), cli, config, &createOptions{
   141  			name:      "name",
   142  			platform:  runtime.GOOS,
   143  			untrusted: true,
   144  			pull:      c.PullPolicy,
   145  		})
   146  
   147  		if c.ExpectedErrMsg != "" {
   148  			assert.ErrorContains(t, err, c.ExpectedErrMsg)
   149  		} else {
   150  			assert.NilError(t, err)
   151  			assert.Check(t, is.DeepEqual(c.ExpectedBody, *body))
   152  		}
   153  
   154  		assert.Check(t, is.Equal(c.ExpectedPulls, pullCounter))
   155  	}
   156  }
   157  func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
   158  	testCases := []struct {
   159  		name          string
   160  		args          []string
   161  		expectedError string
   162  		notaryFunc    test.NotaryClientFuncType
   163  	}{
   164  		{
   165  			name:          "offline-notary-server",
   166  			notaryFunc:    notary.GetOfflineNotaryRepository,
   167  			expectedError: "client is offline",
   168  			args:          []string{"image:tag"},
   169  		},
   170  		{
   171  			name:          "uninitialized-notary-server",
   172  			notaryFunc:    notary.GetUninitializedNotaryRepository,
   173  			expectedError: "remote trust data does not exist",
   174  			args:          []string{"image:tag"},
   175  		},
   176  		{
   177  			name:          "empty-notary-server",
   178  			notaryFunc:    notary.GetEmptyTargetsNotaryRepository,
   179  			expectedError: "No valid trust data for tag",
   180  			args:          []string{"image:tag"},
   181  		},
   182  	}
   183  	for _, tc := range testCases {
   184  		tc := tc
   185  		cli := test.NewFakeCli(&fakeClient{
   186  			createContainerFunc: func(config *container.Config,
   187  				hostConfig *container.HostConfig,
   188  				networkingConfig *network.NetworkingConfig,
   189  				platform *specs.Platform,
   190  				containerName string,
   191  			) (container.ContainerCreateCreatedBody, error) {
   192  				return container.ContainerCreateCreatedBody{}, fmt.Errorf("shouldn't try to pull image")
   193  			},
   194  		}, test.EnableContentTrust)
   195  		cli.SetNotaryClient(tc.notaryFunc)
   196  		cmd := NewCreateCommand(cli)
   197  		cmd.SetOut(ioutil.Discard)
   198  		cmd.SetArgs(tc.args)
   199  		err := cmd.Execute()
   200  		assert.ErrorContains(t, err, tc.expectedError)
   201  	}
   202  }
   203  
   204  func TestNewCreateCommandWithWarnings(t *testing.T) {
   205  	testCases := []struct {
   206  		name    string
   207  		args    []string
   208  		warning bool
   209  	}{
   210  		{
   211  			name: "container-create-without-oom-kill-disable",
   212  			args: []string{"image:tag"},
   213  		},
   214  		{
   215  			name: "container-create-oom-kill-disable-false",
   216  			args: []string{"--oom-kill-disable=false", "image:tag"},
   217  		},
   218  		{
   219  			name:    "container-create-oom-kill-without-memory-limit",
   220  			args:    []string{"--oom-kill-disable", "image:tag"},
   221  			warning: true,
   222  		},
   223  		{
   224  			name:    "container-create-oom-kill-true-without-memory-limit",
   225  			args:    []string{"--oom-kill-disable=true", "image:tag"},
   226  			warning: true,
   227  		},
   228  		{
   229  			name: "container-create-oom-kill-true-with-memory-limit",
   230  			args: []string{"--oom-kill-disable=true", "--memory=100M", "image:tag"},
   231  		},
   232  		{
   233  			name:    "container-create-localhost-dns",
   234  			args:    []string{"--dns=127.0.0.11", "image:tag"},
   235  			warning: true,
   236  		},
   237  		{
   238  			name:    "container-create-localhost-dns-ipv6",
   239  			args:    []string{"--dns=::1", "image:tag"},
   240  			warning: true,
   241  		},
   242  	}
   243  	for _, tc := range testCases {
   244  		tc := tc
   245  		t.Run(tc.name, func(t *testing.T) {
   246  			cli := test.NewFakeCli(&fakeClient{
   247  				createContainerFunc: func(config *container.Config,
   248  					hostConfig *container.HostConfig,
   249  					networkingConfig *network.NetworkingConfig,
   250  					platform *specs.Platform,
   251  					containerName string,
   252  				) (container.ContainerCreateCreatedBody, error) {
   253  					return container.ContainerCreateCreatedBody{}, nil
   254  				},
   255  			})
   256  			cmd := NewCreateCommand(cli)
   257  			cmd.SetOut(ioutil.Discard)
   258  			cmd.SetArgs(tc.args)
   259  			err := cmd.Execute()
   260  			assert.NilError(t, err)
   261  			if tc.warning {
   262  				golden.Assert(t, cli.ErrBuffer().String(), tc.name+".golden")
   263  			} else {
   264  				assert.Equal(t, cli.ErrBuffer().String(), "")
   265  			}
   266  		})
   267  	}
   268  }
   269  
   270  func TestCreateContainerWithProxyConfig(t *testing.T) {
   271  	expected := []string{
   272  		"HTTP_PROXY=httpProxy",
   273  		"http_proxy=httpProxy",
   274  		"HTTPS_PROXY=httpsProxy",
   275  		"https_proxy=httpsProxy",
   276  		"NO_PROXY=noProxy",
   277  		"no_proxy=noProxy",
   278  		"FTP_PROXY=ftpProxy",
   279  		"ftp_proxy=ftpProxy",
   280  	}
   281  	sort.Strings(expected)
   282  
   283  	cli := test.NewFakeCli(&fakeClient{
   284  		createContainerFunc: func(config *container.Config,
   285  			hostConfig *container.HostConfig,
   286  			networkingConfig *network.NetworkingConfig,
   287  			platform *specs.Platform,
   288  			containerName string,
   289  		) (container.ContainerCreateCreatedBody, error) {
   290  			sort.Strings(config.Env)
   291  			assert.DeepEqual(t, config.Env, expected)
   292  			return container.ContainerCreateCreatedBody{}, nil
   293  		},
   294  	})
   295  	cli.SetConfigFile(&configfile.ConfigFile{
   296  		Proxies: map[string]configfile.ProxyConfig{
   297  			"default": {
   298  				HTTPProxy:  "httpProxy",
   299  				HTTPSProxy: "httpsProxy",
   300  				NoProxy:    "noProxy",
   301  				FTPProxy:   "ftpProxy",
   302  			},
   303  		},
   304  	})
   305  	cmd := NewCreateCommand(cli)
   306  	cmd.SetOut(ioutil.Discard)
   307  	cmd.SetArgs([]string{"image:tag"})
   308  	err := cmd.Execute()
   309  	assert.NilError(t, err)
   310  }
   311  
   312  type fakeNotFound struct{}
   313  
   314  func (f fakeNotFound) NotFound() bool { return true }
   315  func (f fakeNotFound) Error() string  { return "error fake not found" }