From 31355030b38d6e856098df8d790feebeb9edddc6 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 30 Nov 2016 17:38:40 -0500 Subject: [PATCH] Move ConvertService to composetransform package. Signed-off-by: Daniel Nephin --- command/stack/common.go | 13 ++ command/stack/deploy.go | 327 +---------------------------- command/stack/deploy_bundlefile.go | 13 +- command/stack/list.go | 12 +- command/stack/ps.go | 3 +- command/stack/services.go | 4 +- 6 files changed, 30 insertions(+), 342 deletions(-) diff --git a/command/stack/common.go b/command/stack/common.go index 050528de4e..c3a43f2cd8 100644 --- a/command/stack/common.go +++ b/command/stack/common.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" + "github.com/docker/docker/opts" "github.com/docker/docker/pkg/composetransform" ) @@ -16,6 +17,18 @@ func getStackFilter(namespace string) filters.Args { return filter } +func getStackFilterFromOpt(namespace string, opt opts.FilterOpt) filters.Args { + filter := opt.Value() + filter.Add("label", composetransform.LabelNamespace+"="+namespace) + return filter +} + +func getAllStacksFilter() filters.Args { + filter := filters.NewArgs() + filter.Add("label", composetransform.LabelNamespace) + return filter +} + func getServices( ctx context.Context, apiclient client.APIClient, diff --git a/command/stack/deploy.go b/command/stack/deploy.go index e8238cde66..957f92f29e 100644 --- a/command/stack/deploy.go +++ b/command/stack/deploy.go @@ -7,7 +7,6 @@ import ( "os" "sort" "strings" - "time" "github.com/spf13/cobra" "golang.org/x/net/context" @@ -15,15 +14,11 @@ import ( "github.com/aanand/compose-file/loader" composetypes "github.com/aanand/compose-file/types" "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" dockerclient "github.com/docker/docker/client" - "github.com/docker/docker/opts" "github.com/docker/docker/pkg/composetransform" - runconfigopts "github.com/docker/docker/runconfig/opts" - "github.com/docker/go-connections/nat" ) const ( @@ -129,7 +124,7 @@ func deployCompose(ctx context.Context, dockerCli *command.DockerCli, opts deplo if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil { return err } - services, err := convertServices(namespace, config) + services, err := composetransform.ConvertServices(namespace, config) if err != nil { return err } @@ -237,54 +232,17 @@ func createNetworks( return nil } -func convertServiceNetworks( - networks map[string]*composetypes.ServiceNetworkConfig, - networkConfigs map[string]composetypes.NetworkConfig, - namespace composetransform.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("invalid network: %s", networkName) - } - var aliases []string - if network != nil { - aliases = network.Aliases - } - target := namespace.scope(networkName) - if networkConfig.External.External { - target = networkName - } - nets = append(nets, swarm.NetworkAttachmentConfig{ - Target: target, - Aliases: append(aliases, name), - }) - } - return nets, nil -} - func deployServices( ctx context.Context, dockerCli *command.DockerCli, services map[string]swarm.ServiceSpec, - namespace namespace, + namespace composetransform.Namespace, sendAuth bool, ) error { apiClient := dockerCli.Client() out := dockerCli.Out() - existingServices, err := getServices(ctx, apiClient, namespace.name) + existingServices, err := getServices(ctx, apiClient, namespace.Name()) if err != nil { return err } @@ -295,7 +253,7 @@ func deployServices( } for internalName, serviceSpec := range services { - name := namespace.scope(internalName) + name := namespace.Scope(internalName) encodedAuth := "" if sendAuth { @@ -343,280 +301,3 @@ func deployServices( return nil } - -func convertServices( - 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 := composetransform.ConvertVolumes(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: getStackLabels(namespace.name, 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: getStackLabels(namespace.name, 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 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("command and disable key 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 - } - // TODO: is this an accurate convertion? - 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 - } - } - 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 -} diff --git a/command/stack/deploy_bundlefile.go b/command/stack/deploy_bundlefile.go index c82c46e424..f9a4162389 100644 --- a/command/stack/deploy_bundlefile.go +++ b/command/stack/deploy_bundlefile.go @@ -6,6 +6,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/cli/command" + "github.com/docker/docker/pkg/composetransform" ) func deployBundle(ctx context.Context, dockerCli *command.DockerCli, opts deployOptions) error { @@ -18,20 +19,20 @@ func deployBundle(ctx context.Context, dockerCli *command.DockerCli, opts deploy return err } - namespace := namespace{name: opts.namespace} + namespace := composetransform.NewNamespace(opts.namespace) networks := make(map[string]types.NetworkCreate) for _, service := range bundle.Services { for _, networkName := range service.Networks { networks[networkName] = types.NetworkCreate{ - Labels: getStackLabels(namespace.name, nil), + Labels: composetransform.AddStackLabel(namespace, nil), } } } services := make(map[string]swarm.ServiceSpec) for internalName, service := range bundle.Services { - name := namespace.scope(internalName) + name := namespace.Scope(internalName) var ports []swarm.PortConfig for _, portSpec := range service.Ports { @@ -44,7 +45,7 @@ func deployBundle(ctx context.Context, dockerCli *command.DockerCli, opts deploy nets := []swarm.NetworkAttachmentConfig{} for _, networkName := range service.Networks { nets = append(nets, swarm.NetworkAttachmentConfig{ - Target: namespace.scope(networkName), + Target: namespace.Scope(networkName), Aliases: []string{networkName}, }) } @@ -52,7 +53,7 @@ func deployBundle(ctx context.Context, dockerCli *command.DockerCli, opts deploy serviceSpec := swarm.ServiceSpec{ Annotations: swarm.Annotations{ Name: name, - Labels: getStackLabels(namespace.name, service.Labels), + Labels: composetransform.AddStackLabel(namespace, service.Labels), }, TaskTemplate: swarm.TaskSpec{ ContainerSpec: swarm.ContainerSpec{ @@ -63,7 +64,7 @@ func deployBundle(ctx context.Context, dockerCli *command.DockerCli, opts deploy // Service Labels will not be copied to Containers // automatically during the deployment so we apply // it here. - Labels: getStackLabels(namespace.name, nil), + Labels: composetransform.AddStackLabel(namespace, nil), }, }, EndpointSpec: &swarm.EndpointSpec{ diff --git a/command/stack/list.go b/command/stack/list.go index f655b929ad..52e593316e 100644 --- a/command/stack/list.go +++ b/command/stack/list.go @@ -9,10 +9,10 @@ import ( "golang.org/x/net/context" "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/docker/docker/client" + "github.com/docker/docker/pkg/composetransform" "github.com/spf13/cobra" ) @@ -81,23 +81,19 @@ func getStacks( ctx context.Context, apiclient client.APIClient, ) ([]*stack, error) { - - filter := filters.NewArgs() - filter.Add("label", labelNamespace) - services, err := apiclient.ServiceList( ctx, - types.ServiceListOptions{Filters: filter}) + types.ServiceListOptions{Filters: getAllStacksFilter()}) if err != nil { return nil, err } m := make(map[string]*stack, 0) for _, service := range services { labels := service.Spec.Labels - name, ok := labels[labelNamespace] + name, ok := labels[composetransform.LabelNamespace] if !ok { return nil, fmt.Errorf("cannot get label %s for service %s", - labelNamespace, service.ID) + composetransform.LabelNamespace, service.ID) } ztack, ok := m[name] if !ok { diff --git a/command/stack/ps.go b/command/stack/ps.go index 7a5e069cbe..497fb97b5e 100644 --- a/command/stack/ps.go +++ b/command/stack/ps.go @@ -49,8 +49,7 @@ func runPS(dockerCli *command.DockerCli, opts psOptions) error { client := dockerCli.Client() ctx := context.Background() - filter := opts.filter.Value() - filter.Add("label", labelNamespace+"="+opts.namespace) + filter := getStackFilterFromOpt(opts.namespace, opts.filter) if !opts.all && !filter.Include("desired-state") { filter.Add("desired-state", string(swarm.TaskStateRunning)) filter.Add("desired-state", string(swarm.TaskStateAccepted)) diff --git a/command/stack/services.go b/command/stack/services.go index 1ca1c8c129..a46652df7c 100644 --- a/command/stack/services.go +++ b/command/stack/services.go @@ -43,9 +43,7 @@ func runServices(dockerCli *command.DockerCli, opts servicesOptions) error { ctx := context.Background() client := dockerCli.Client() - filter := opts.filter.Value() - filter.Add("label", labelNamespace+"="+opts.namespace) - + filter := getStackFilterFromOpt(opts.namespace, opts.filter) services, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: filter}) if err != nil { return err