diff --git a/go.mod b/go.mod index 3f7c826b9..16588d5f0 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 b813a310f..91623a12a 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 +}