From a0d8d80250af0fc6673f8428920faa45c2ce50cf Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Thu, 31 Aug 2017 17:07:16 -0400 Subject: [PATCH] Fix crash in containe run after pulling an image. Signed-off-by: Daniel Nephin --- cli/command/container/client_test.go | 40 +++++++++++++++++-- cli/command/container/create.go | 5 ++- cli/command/container/create_test.go | 60 +++++++++++++++++++++++++++- cli/command/trust.go | 4 ++ 4 files changed, 102 insertions(+), 7 deletions(-) diff --git a/cli/command/container/client_test.go b/cli/command/container/client_test.go index 32f9a28fbf..875fee7049 100644 --- a/cli/command/container/client_test.go +++ b/cli/command/container/client_test.go @@ -1,16 +1,23 @@ package container import ( + "io" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "golang.org/x/net/context" ) type fakeClient struct { client.Client - inspectFunc func(string) (types.ContainerJSON, error) - execInspectFunc func(execID string) (types.ContainerExecInspect, error) - execCreateFunc func(container string, config types.ExecConfig) (types.IDResponse, error) + inspectFunc func(string) (types.ContainerJSON, error) + execInspectFunc func(execID string) (types.ContainerExecInspect, error) + execCreateFunc func(container string, config types.ExecConfig) (types.IDResponse, error) + createContainerFunc func(config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error) + imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) + infoFunc func() (types.Info, error) } func (f *fakeClient) ContainerInspect(_ context.Context, containerID string) (types.ContainerJSON, error) { @@ -37,3 +44,30 @@ func (f *fakeClient) ContainerExecInspect(_ context.Context, execID string) (typ func (f *fakeClient) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error { return nil } + +func (f *fakeClient) ContainerCreate( + _ context.Context, + config *container.Config, + hostConfig *container.HostConfig, + networkingConfig *network.NetworkingConfig, + containerName string, +) (container.ContainerCreateCreatedBody, error) { + if f.createContainerFunc != nil { + return f.createContainerFunc(config, hostConfig, networkingConfig, containerName) + } + return container.ContainerCreateCreatedBody{}, nil +} + +func (f *fakeClient) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) { + if f.imageCreateFunc != nil { + return f.imageCreateFunc(parentReference, options) + } + return nil, nil +} + +func (f *fakeClient) Info(_ context.Context) (types.Info, error) { + if f.infoFunc != nil { + return f.infoFunc() + } + return types.Info{}, nil +} diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 97bc1863f0..e87e3fb28a 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -198,7 +198,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef)) // we don't want to write to stdout anything apart from container.ID - if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil { + if err := pullImage(ctx, dockerCli, config.Image, stderr); err != nil { return nil, err } if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil { @@ -212,8 +212,9 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig if retryErr != nil { return nil, retryErr } + } else { + return nil, err } - return nil, err } for _, warning := range response.Warnings { diff --git a/cli/command/container/create_test.go b/cli/command/container/create_test.go index 467cdb3a6e..21354e093a 100644 --- a/cli/command/container/create_test.go +++ b/cli/command/container/create_test.go @@ -1,13 +1,20 @@ package container import ( + "context" + "io" + "io/ioutil" "os" + "strings" "testing" - "io/ioutil" - + "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/testutil" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" "github.com/gotestyourself/gotestyourself/fs" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -62,3 +69,52 @@ func TestCIDFileCloseWithWrite(t *testing.T) { _, err = os.Stat(path) require.NoError(t, err) } + +func TestCreateContainerPullsImageIfMissing(t *testing.T) { + imageName := "does-not-exist-locally" + responseCounter := 0 + containerID := "abcdef" + + client := &fakeClient{ + createContainerFunc: func( + config *container.Config, + hostConfig *container.HostConfig, + networkingConfig *network.NetworkingConfig, + containerName string, + ) (container.ContainerCreateCreatedBody, error) { + defer func() { responseCounter++ }() + switch responseCounter { + case 0: + return container.ContainerCreateCreatedBody{}, fakeNotFound{} + case 1: + return container.ContainerCreateCreatedBody{ID: containerID}, nil + default: + return container.ContainerCreateCreatedBody{}, errors.New("unexpected") + } + }, + imageCreateFunc: func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) { + return ioutil.NopCloser(strings.NewReader("")), nil + }, + infoFunc: func() (types.Info, error) { + return types.Info{IndexServerAddress: "http://indexserver"}, nil + }, + } + cli := test.NewFakeCli(client) + config := &containerConfig{ + Config: &container.Config{ + Image: imageName, + }, + HostConfig: &container.HostConfig{}, + } + body, err := createContainer(context.Background(), cli, config, "name") + require.NoError(t, err) + expected := container.ContainerCreateCreatedBody{ID: containerID} + assert.Equal(t, expected, *body) + stderr := cli.ErrBuffer().String() + assert.Contains(t, stderr, "Unable to find image 'does-not-exist-locally:latest' locally") +} + +type fakeNotFound struct{} + +func (f fakeNotFound) NotFound() bool { return true } +func (f fakeNotFound) Error() string { return "error fake not found" } diff --git a/cli/command/trust.go b/cli/command/trust.go index c0742bc5b2..51f760ac5b 100644 --- a/cli/command/trust.go +++ b/cli/command/trust.go @@ -12,6 +12,10 @@ var ( untrusted bool ) +func init() { + untrusted = !getDefaultTrustState() +} + // AddTrustVerificationFlags adds content trust flags to the provided flagset func AddTrustVerificationFlags(fs *pflag.FlagSet) { trusted := getDefaultTrustState()