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" }