mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			330 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package convert
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"time"
 | 
						|
 | 
						|
	composetypes "github.com/aanand/compose-file/types"
 | 
						|
	"github.com/docker/docker/api/types/container"
 | 
						|
	"github.com/docker/docker/api/types/swarm"
 | 
						|
	"github.com/docker/docker/opts"
 | 
						|
	runconfigopts "github.com/docker/docker/runconfig/opts"
 | 
						|
	"github.com/docker/go-connections/nat"
 | 
						|
)
 | 
						|
 | 
						|
// Services from compose-file types to engine API types
 | 
						|
func Services(
 | 
						|
	namespace Namespace,
 | 
						|
	config *composetypes.Config,
 | 
						|
) (map[string]swarm.ServiceSpec, error) {
 | 
						|
	result := make(map[string]swarm.ServiceSpec)
 | 
						|
 | 
						|
	services := config.Services
 | 
						|
	volumes := config.Volumes
 | 
						|
	networks := config.Networks
 | 
						|
 | 
						|
	for _, service := range services {
 | 
						|
		serviceSpec, err := convertService(namespace, service, networks, volumes)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		result[service.Name] = serviceSpec
 | 
						|
	}
 | 
						|
 | 
						|
	return result, nil
 | 
						|
}
 | 
						|
 | 
						|
func convertService(
 | 
						|
	namespace Namespace,
 | 
						|
	service composetypes.ServiceConfig,
 | 
						|
	networkConfigs map[string]composetypes.NetworkConfig,
 | 
						|
	volumes map[string]composetypes.VolumeConfig,
 | 
						|
) (swarm.ServiceSpec, error) {
 | 
						|
	name := namespace.Scope(service.Name)
 | 
						|
 | 
						|
	endpoint, err := convertEndpointSpec(service.Ports)
 | 
						|
	if err != nil {
 | 
						|
		return swarm.ServiceSpec{}, err
 | 
						|
	}
 | 
						|
 | 
						|
	mode, err := convertDeployMode(service.Deploy.Mode, service.Deploy.Replicas)
 | 
						|
	if err != nil {
 | 
						|
		return swarm.ServiceSpec{}, err
 | 
						|
	}
 | 
						|
 | 
						|
	mounts, err := Volumes(service.Volumes, volumes, namespace)
 | 
						|
	if err != nil {
 | 
						|
		// TODO: better error message (include service name)
 | 
						|
		return swarm.ServiceSpec{}, err
 | 
						|
	}
 | 
						|
 | 
						|
	resources, err := convertResources(service.Deploy.Resources)
 | 
						|
	if err != nil {
 | 
						|
		return swarm.ServiceSpec{}, err
 | 
						|
	}
 | 
						|
 | 
						|
	restartPolicy, err := convertRestartPolicy(
 | 
						|
		service.Restart, service.Deploy.RestartPolicy)
 | 
						|
	if err != nil {
 | 
						|
		return swarm.ServiceSpec{}, err
 | 
						|
	}
 | 
						|
 | 
						|
	healthcheck, err := convertHealthcheck(service.HealthCheck)
 | 
						|
	if err != nil {
 | 
						|
		return swarm.ServiceSpec{}, err
 | 
						|
	}
 | 
						|
 | 
						|
	networks, err := convertServiceNetworks(service.Networks, networkConfigs, namespace, service.Name)
 | 
						|
	if err != nil {
 | 
						|
		return swarm.ServiceSpec{}, err
 | 
						|
	}
 | 
						|
 | 
						|
	var logDriver *swarm.Driver
 | 
						|
	if service.Logging != nil {
 | 
						|
		logDriver = &swarm.Driver{
 | 
						|
			Name:    service.Logging.Driver,
 | 
						|
			Options: service.Logging.Options,
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	serviceSpec := swarm.ServiceSpec{
 | 
						|
		Annotations: swarm.Annotations{
 | 
						|
			Name:   name,
 | 
						|
			Labels: AddStackLabel(namespace, service.Deploy.Labels),
 | 
						|
		},
 | 
						|
		TaskTemplate: swarm.TaskSpec{
 | 
						|
			ContainerSpec: swarm.ContainerSpec{
 | 
						|
				Image:           service.Image,
 | 
						|
				Command:         service.Entrypoint,
 | 
						|
				Args:            service.Command,
 | 
						|
				Hostname:        service.Hostname,
 | 
						|
				Hosts:           convertExtraHosts(service.ExtraHosts),
 | 
						|
				Healthcheck:     healthcheck,
 | 
						|
				Env:             convertEnvironment(service.Environment),
 | 
						|
				Labels:          AddStackLabel(namespace, service.Labels),
 | 
						|
				Dir:             service.WorkingDir,
 | 
						|
				User:            service.User,
 | 
						|
				Mounts:          mounts,
 | 
						|
				StopGracePeriod: service.StopGracePeriod,
 | 
						|
				TTY:             service.Tty,
 | 
						|
				OpenStdin:       service.StdinOpen,
 | 
						|
			},
 | 
						|
			LogDriver:     logDriver,
 | 
						|
			Resources:     resources,
 | 
						|
			RestartPolicy: restartPolicy,
 | 
						|
			Placement: &swarm.Placement{
 | 
						|
				Constraints: service.Deploy.Placement.Constraints,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		EndpointSpec: endpoint,
 | 
						|
		Mode:         mode,
 | 
						|
		Networks:     networks,
 | 
						|
		UpdateConfig: convertUpdateConfig(service.Deploy.UpdateConfig),
 | 
						|
	}
 | 
						|
 | 
						|
	return serviceSpec, nil
 | 
						|
}
 | 
						|
 | 
						|
func convertServiceNetworks(
 | 
						|
	networks map[string]*composetypes.ServiceNetworkConfig,
 | 
						|
	networkConfigs networkMap,
 | 
						|
	namespace Namespace,
 | 
						|
	name string,
 | 
						|
) ([]swarm.NetworkAttachmentConfig, error) {
 | 
						|
	if len(networks) == 0 {
 | 
						|
		return []swarm.NetworkAttachmentConfig{
 | 
						|
			{
 | 
						|
				Target:  namespace.Scope("default"),
 | 
						|
				Aliases: []string{name},
 | 
						|
			},
 | 
						|
		}, nil
 | 
						|
	}
 | 
						|
 | 
						|
	nets := []swarm.NetworkAttachmentConfig{}
 | 
						|
	for networkName, network := range networks {
 | 
						|
		networkConfig, ok := networkConfigs[networkName]
 | 
						|
		if !ok {
 | 
						|
			return []swarm.NetworkAttachmentConfig{}, fmt.Errorf(
 | 
						|
				"service %q references network %q, which is not declared", name, networkName)
 | 
						|
		}
 | 
						|
		var aliases []string
 | 
						|
		if network != nil {
 | 
						|
			aliases = network.Aliases
 | 
						|
		}
 | 
						|
		target := namespace.Scope(networkName)
 | 
						|
		if networkConfig.External.External {
 | 
						|
			target = networkConfig.External.Name
 | 
						|
		}
 | 
						|
		nets = append(nets, swarm.NetworkAttachmentConfig{
 | 
						|
			Target:  target,
 | 
						|
			Aliases: append(aliases, name),
 | 
						|
		})
 | 
						|
	}
 | 
						|
	return nets, nil
 | 
						|
}
 | 
						|
 | 
						|
func convertExtraHosts(extraHosts map[string]string) []string {
 | 
						|
	hosts := []string{}
 | 
						|
	for host, ip := range extraHosts {
 | 
						|
		hosts = append(hosts, fmt.Sprintf("%s %s", ip, host))
 | 
						|
	}
 | 
						|
	return hosts
 | 
						|
}
 | 
						|
 | 
						|
func convertHealthcheck(healthcheck *composetypes.HealthCheckConfig) (*container.HealthConfig, error) {
 | 
						|
	if healthcheck == nil {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
	var (
 | 
						|
		err               error
 | 
						|
		timeout, interval time.Duration
 | 
						|
		retries           int
 | 
						|
	)
 | 
						|
	if healthcheck.Disable {
 | 
						|
		if len(healthcheck.Test) != 0 {
 | 
						|
			return nil, fmt.Errorf("test and disable can't be set at the same time")
 | 
						|
		}
 | 
						|
		return &container.HealthConfig{
 | 
						|
			Test: []string{"NONE"},
 | 
						|
		}, nil
 | 
						|
 | 
						|
	}
 | 
						|
	if healthcheck.Timeout != "" {
 | 
						|
		timeout, err = time.ParseDuration(healthcheck.Timeout)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if healthcheck.Interval != "" {
 | 
						|
		interval, err = time.ParseDuration(healthcheck.Interval)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if healthcheck.Retries != nil {
 | 
						|
		retries = int(*healthcheck.Retries)
 | 
						|
	}
 | 
						|
	return &container.HealthConfig{
 | 
						|
		Test:     healthcheck.Test,
 | 
						|
		Timeout:  timeout,
 | 
						|
		Interval: interval,
 | 
						|
		Retries:  retries,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func convertRestartPolicy(restart string, source *composetypes.RestartPolicy) (*swarm.RestartPolicy, error) {
 | 
						|
	// TODO: log if restart is being ignored
 | 
						|
	if source == nil {
 | 
						|
		policy, err := runconfigopts.ParseRestartPolicy(restart)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		switch {
 | 
						|
		case policy.IsNone():
 | 
						|
			return nil, nil
 | 
						|
		case policy.IsAlways(), policy.IsUnlessStopped():
 | 
						|
			return &swarm.RestartPolicy{
 | 
						|
				Condition: swarm.RestartPolicyConditionAny,
 | 
						|
			}, nil
 | 
						|
		case policy.IsOnFailure():
 | 
						|
			attempts := uint64(policy.MaximumRetryCount)
 | 
						|
			return &swarm.RestartPolicy{
 | 
						|
				Condition:   swarm.RestartPolicyConditionOnFailure,
 | 
						|
				MaxAttempts: &attempts,
 | 
						|
			}, nil
 | 
						|
		default:
 | 
						|
			return nil, fmt.Errorf("unknown restart policy: %s", restart)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return &swarm.RestartPolicy{
 | 
						|
		Condition:   swarm.RestartPolicyCondition(source.Condition),
 | 
						|
		Delay:       source.Delay,
 | 
						|
		MaxAttempts: source.MaxAttempts,
 | 
						|
		Window:      source.Window,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func convertUpdateConfig(source *composetypes.UpdateConfig) *swarm.UpdateConfig {
 | 
						|
	if source == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	parallel := uint64(1)
 | 
						|
	if source.Parallelism != nil {
 | 
						|
		parallel = *source.Parallelism
 | 
						|
	}
 | 
						|
	return &swarm.UpdateConfig{
 | 
						|
		Parallelism:     parallel,
 | 
						|
		Delay:           source.Delay,
 | 
						|
		FailureAction:   source.FailureAction,
 | 
						|
		Monitor:         source.Monitor,
 | 
						|
		MaxFailureRatio: source.MaxFailureRatio,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func convertResources(source composetypes.Resources) (*swarm.ResourceRequirements, error) {
 | 
						|
	resources := &swarm.ResourceRequirements{}
 | 
						|
	if source.Limits != nil {
 | 
						|
		cpus, err := opts.ParseCPUs(source.Limits.NanoCPUs)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		resources.Limits = &swarm.Resources{
 | 
						|
			NanoCPUs:    cpus,
 | 
						|
			MemoryBytes: int64(source.Limits.MemoryBytes),
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if source.Reservations != nil {
 | 
						|
		cpus, err := opts.ParseCPUs(source.Reservations.NanoCPUs)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		resources.Reservations = &swarm.Resources{
 | 
						|
			NanoCPUs:    cpus,
 | 
						|
			MemoryBytes: int64(source.Reservations.MemoryBytes),
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return resources, nil
 | 
						|
}
 | 
						|
 | 
						|
func convertEndpointSpec(source []string) (*swarm.EndpointSpec, error) {
 | 
						|
	portConfigs := []swarm.PortConfig{}
 | 
						|
	ports, portBindings, err := nat.ParsePortSpecs(source)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	for port := range ports {
 | 
						|
		portConfigs = append(
 | 
						|
			portConfigs,
 | 
						|
			opts.ConvertPortToPortConfig(port, portBindings)...)
 | 
						|
	}
 | 
						|
 | 
						|
	return &swarm.EndpointSpec{Ports: portConfigs}, nil
 | 
						|
}
 | 
						|
 | 
						|
func convertEnvironment(source map[string]string) []string {
 | 
						|
	var output []string
 | 
						|
 | 
						|
	for name, value := range source {
 | 
						|
		output = append(output, fmt.Sprintf("%s=%s", name, value))
 | 
						|
	}
 | 
						|
 | 
						|
	return output
 | 
						|
}
 | 
						|
 | 
						|
func convertDeployMode(mode string, replicas *uint64) (swarm.ServiceMode, error) {
 | 
						|
	serviceMode := swarm.ServiceMode{}
 | 
						|
 | 
						|
	switch mode {
 | 
						|
	case "global":
 | 
						|
		if replicas != nil {
 | 
						|
			return serviceMode, fmt.Errorf("replicas can only be used with replicated mode")
 | 
						|
		}
 | 
						|
		serviceMode.Global = &swarm.GlobalService{}
 | 
						|
	case "replicated", "":
 | 
						|
		serviceMode.Replicated = &swarm.ReplicatedService{Replicas: replicas}
 | 
						|
	default:
 | 
						|
		return serviceMode, fmt.Errorf("Unknown mode: %s", mode)
 | 
						|
	}
 | 
						|
	return serviceMode, nil
 | 
						|
}
 |