package client // import "github.com/docker/docker/client" import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/container" "github.com/docker/docker/errdefs" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestContainerCreateError(t *testing.T) { client := &Client{ client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing") assert.Check(t, is.ErrorType(err, errdefs.IsSystem)) // 404 doesn't automatically means an unknown image client = &Client{ client: newMockClient(errorMock(http.StatusNotFound, "Server error")), } _, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing") assert.Check(t, is.ErrorType(err, errdefs.IsNotFound)) } func TestContainerCreateImageNotFound(t *testing.T) { client := &Client{ client: newMockClient(errorMock(http.StatusNotFound, "No such image")), } _, err := client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, nil, "unknown") assert.Check(t, is.ErrorType(err, errdefs.IsNotFound)) } func TestContainerCreateWithName(t *testing.T) { expectedURL := "/containers/create" client := &Client{ client: newMockClient(func(req *http.Request) (*http.Response, error) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } name := req.URL.Query().Get("name") if name != "container_name" { return nil, fmt.Errorf("container name not set in URL query properly. Expected `container_name`, got %s", name) } b, err := json.Marshal(container.CreateResponse{ ID: "container_id", }) if err != nil { return nil, err } return &http.Response{ StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } r, err := client.ContainerCreate(context.Background(), nil, nil, nil, nil, "container_name") if err != nil { t.Fatal(err) } if r.ID != "container_id" { t.Fatalf("expected `container_id`, got %s", r.ID) } } // TestContainerCreateAutoRemove validates that a client using API 1.24 always disables AutoRemove. When using API 1.25 // or up, AutoRemove should not be disabled. func TestContainerCreateAutoRemove(t *testing.T) { autoRemoveValidator := func(expectedValue bool) func(req *http.Request) (*http.Response, error) { return func(req *http.Request) (*http.Response, error) { var config configWrapper if err := json.NewDecoder(req.Body).Decode(&config); err != nil { return nil, err } if config.HostConfig.AutoRemove != expectedValue { return nil, fmt.Errorf("expected AutoRemove to be %v, got %v", expectedValue, config.HostConfig.AutoRemove) } b, err := json.Marshal(container.CreateResponse{ ID: "container_id", }) if err != nil { return nil, err } return &http.Response{ StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewReader(b)), }, nil } } client := &Client{ client: newMockClient(autoRemoveValidator(false)), version: "1.24", } if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, nil, ""); err != nil { t.Fatal(err) } client = &Client{ client: newMockClient(autoRemoveValidator(true)), version: "1.25", } if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, nil, ""); err != nil { t.Fatal(err) } } // TestContainerCreateConnection verifies that connection errors occurring // during API-version negotiation are not shadowed by API-version errors. // // Regression test for https://github.com/docker/cli/issues/4890 func TestContainerCreateConnectionError(t *testing.T) { client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid")) assert.NilError(t, err) _, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "") assert.Check(t, is.ErrorType(err, IsErrConnectionFailed)) }