From 2dd98e8a74393fdfd32c3b6431d48079c9503f73 Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Fri, 22 May 2020 16:15:57 +0200 Subject: [PATCH 1/2] Add ports to the moby backend --- go.mod | 2 +- moby/backend.go | 83 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 3f7c826b..16588d5f 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/compose-spec/compose-go v0.0.0-20200423124427-63dcf8c22cae github.com/containerd/console v1.0.0 github.com/containerd/containerd v1.3.4 // indirect - github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible github.com/docker/go-connections v0.4.0 github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect diff --git a/moby/backend.go b/moby/backend.go index b813a310..91623a12 100644 --- a/moby/backend.go +++ b/moby/backend.go @@ -1,10 +1,15 @@ package moby import ( + "bufio" "context" + "fmt" "io" + "strconv" "time" + "github.com/docker/go-connections/nat" + "github.com/docker/api/context/cloud" "github.com/docker/docker/api/types" @@ -72,7 +77,7 @@ func (ms *mobyService) List(ctx context.Context, all bool) ([]containers.Contain // statuses. We also need to add a `Created` property on the gRPC side. Status: container.Status, Command: container.Command, - Ports: getPorts(container.Ports), + Ports: toPorts(container.Ports), }) } @@ -80,15 +85,49 @@ func (ms *mobyService) List(ctx context.Context, all bool) ([]containers.Contain } func (ms *mobyService) Run(ctx context.Context, r containers.ContainerConfig) error { - create, err := ms.apiClient.ContainerCreate(ctx, &container.Config{ - Image: r.Image, - Labels: r.Labels, - }, nil, nil, r.ID) + exposedPorts, hostBindings, err := fromPorts(r.Ports) if err != nil { return err } - return ms.apiClient.ContainerStart(ctx, create.ID, types.ContainerStartOptions{}) + containerConfig := &container.Config{ + Image: r.Image, + Labels: r.Labels, + ExposedPorts: exposedPorts, + } + hostConfig := &container.HostConfig{ + PortBindings: hostBindings, + } + + created, err := ms.apiClient.ContainerCreate(ctx, containerConfig, hostConfig, nil, r.ID) + + if err != nil { + if client.IsErrNotFound(err) { + io, err := ms.apiClient.ImagePull(ctx, r.Image, types.ImagePullOptions{}) + if err != nil { + return err + } + scanner := bufio.NewScanner(io) + for scanner.Scan() { + fmt.Println(string(scanner.Bytes())) + } + + if err = scanner.Err(); err != nil { + return err + } + if err = io.Close(); err != nil { + return err + } + created, err = ms.apiClient.ContainerCreate(ctx, containerConfig, hostConfig, nil, r.ID) + if err != nil { + return err + } + } else { + return err + } + } + + return ms.apiClient.ContainerStart(ctx, created.ID, types.ContainerStartOptions{}) } func (ms *mobyService) Stop(ctx context.Context, containerID string, timeout *uint32) error { @@ -162,7 +201,7 @@ func (ms *mobyService) Delete(ctx context.Context, containerID string, force boo return err } -func getPorts(ports []types.Port) []containers.Port { +func toPorts(ports []types.Port) []containers.Port { result := []containers.Port{} for _, port := range ports { result = append(result, containers.Port{ @@ -175,3 +214,33 @@ func getPorts(ports []types.Port) []containers.Port { return result } + +func fromPorts(ports []containers.Port) (map[nat.Port]struct{}, map[nat.Port][]nat.PortBinding, error) { + var ( + exposedPorts = make(map[nat.Port]struct{}, len(ports)) + bindings = make(map[nat.Port][]nat.PortBinding) + ) + + for _, port := range ports { + p, err := nat.NewPort(port.Protocol, strconv.Itoa(int(port.ContainerPort))) + if err != nil { + return nil, nil, err + } + + if _, exists := exposedPorts[p]; !exists { + exposedPorts[p] = struct{}{} + } + + portBinding := nat.PortBinding{ + HostIP: port.HostIP, + HostPort: strconv.Itoa(int(port.HostPort)), + } + bslice, exists := bindings[p] + if !exists { + bslice = []nat.PortBinding{} + } + bindings[p] = append(bslice, portBinding) + } + + return exposedPorts, bindings, nil +} From 1c3673c42160fbdc59aa601cdd5ed2113dae4da6 Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Fri, 22 May 2020 17:05:46 +0200 Subject: [PATCH 2/2] Add moby backend e2e tests --- Makefile | 2 +- moby/backend.go | 4 +-- moby/e2e/backend_test.go | 56 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 moby/e2e/backend_test.go diff --git a/Makefile b/Makefile index f2988dfd..32d3d894 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ cli: ## Compile the cli --target cli e2e-local: ## Run End to end local tests - go test -v ./tests/e2e + go test -v ./tests/e2e ./moby/e2e e2e-aci: ## Run End to end ACI tests (requires azure login) go test -v ./tests/aci-e2e diff --git a/moby/backend.go b/moby/backend.go index 91623a12..5c6616a7 100644 --- a/moby/backend.go +++ b/moby/backend.go @@ -3,7 +3,6 @@ package moby import ( "bufio" "context" - "fmt" "io" "strconv" "time" @@ -108,8 +107,9 @@ func (ms *mobyService) Run(ctx context.Context, r containers.ContainerConfig) er return err } scanner := bufio.NewScanner(io) + + // Read the whole body, otherwise the pulling stops for scanner.Scan() { - fmt.Println(string(scanner.Bytes())) } if err = scanner.Err(); err != nil { diff --git a/moby/e2e/backend_test.go b/moby/e2e/backend_test.go new file mode 100644 index 00000000..90b72c86 --- /dev/null +++ b/moby/e2e/backend_test.go @@ -0,0 +1,56 @@ +package e2e + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/docker/api/tests/framework" +) + +type MobyBackendTestSuite struct { + framework.Suite +} + +func (m *MobyBackendTestSuite) BeforeTest(suiteName string, testName string) { + m.NewDockerCommand("context", "create", "test-context", "moby").ExecOrDie() + m.NewDockerCommand("context", "use", "test-context").ExecOrDie() +} + +func (m *MobyBackendTestSuite) AfterTest(suiteName string, testName string) { + m.NewDockerCommand("context", "rm", "test-context").ExecOrDie() + m.NewDockerCommand("context", "use", "default").ExecOrDie() +} + +func (m *MobyBackendTestSuite) TestPs() { + out := m.NewDockerCommand("ps").ExecOrDie() + require.Equal(m.T(), "CONTAINER ID IMAGE COMMAND STATUS PORTS\n", out) +} + +func (m *MobyBackendTestSuite) TestRun() { + _, err := m.NewDockerCommand("run", "--name", "nginx", "nginx").Exec() + require.Nil(m.T(), err) + out := m.NewDockerCommand("ps").ExecOrDie() + defer func() { + m.NewDockerCommand("rm", "-f", "nginx").ExecOrDie() + }() + lines := strings.Split(out, "\n") + assert.Equal(m.T(), 3, len(lines)) +} + +func (m *MobyBackendTestSuite) TestRunWithPorts() { + _, err := m.NewDockerCommand("run", "--name", "nginx", "-p", "8080:80", "nginx").Exec() + require.Nil(m.T(), err) + out := m.NewDockerCommand("ps").ExecOrDie() + defer func() { + m.NewDockerCommand("rm", "-f", "nginx").ExecOrDie() + }() + assert.Contains(m.T(), out, "8080") +} + +func TestMobyBackendTestSuite(t *testing.T) { + suite.Run(t, new(MobyBackendTestSuite)) +}