|  | @@ -1,10 +1,14 @@
 | 
	
		
			
				|  |  |  package moby
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import (
 | 
	
		
			
				|  |  | +	"bufio"
 | 
	
		
			
				|  |  |  	"context"
 | 
	
		
			
				|  |  |  	"io"
 | 
	
		
			
				|  |  | +	"strconv"
 | 
	
		
			
				|  |  |  	"time"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	"github.com/docker/go-connections/nat"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	"github.com/docker/api/context/cloud"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	"github.com/docker/docker/api/types"
 | 
	
	
		
			
				|  | @@ -72,7 +76,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 +84,50 @@ 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)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			// Read the whole body, otherwise the pulling stops
 | 
	
		
			
				|  |  | +			for scanner.Scan() {
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			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
 | 
	
		
			
				|  |  | +}
 |