From 13384ba34b8d57d9cc6e68ff9c8b0b72bc72f9c3 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Fri, 28 Oct 2016 17:30:20 -0400 Subject: [PATCH] Add swarmkit fields to stack service. Signed-off-by: Daniel Nephin --- cli/command/stack/deploy.go | 118 +++++++++++++++++++++++++++++------- opts/opts.go | 26 +++++--- 2 files changed, 111 insertions(+), 33 deletions(-) diff --git a/cli/command/stack/deploy.go b/cli/command/stack/deploy.go index 96bd175450..e72abcc8cc 100644 --- a/cli/command/stack/deploy.go +++ b/cli/command/stack/deploy.go @@ -5,7 +5,6 @@ import ( "io/ioutil" "os" "strings" - "time" "github.com/spf13/cobra" "golang.org/x/net/context" @@ -19,6 +18,8 @@ import ( "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" servicecmd "github.com/docker/docker/cli/command/service" + runconfigopts "github.com/docker/docker/runconfig/opts" + "github.com/docker/docker/opts" "github.com/docker/go-connections/nat" ) @@ -343,23 +344,37 @@ func convertService( 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 + } + serviceSpec := swarm.ServiceSpec{ Annotations: swarm.Annotations{ Name: name, - Labels: getStackLabels(namespace, service.Labels), + Labels: getStackLabels(namespace, service.Deploy.Labels), }, TaskTemplate: swarm.TaskSpec{ ContainerSpec: swarm.ContainerSpec{ - Image: service.Image, - Command: service.Entrypoint, - Args: service.Command, - Hostname: service.Hostname, - Env: convertEnvironment(service.Environment), - Labels: getStackLabels(namespace, service.Deploy.Labels), - Dir: service.WorkingDir, - User: service.User, - Mounts: mounts, + Image: service.Image, + Command: service.Entrypoint, + Args: service.Command, + Hostname: service.Hostname, + Env: convertEnvironment(service.Environment), + Labels: getStackLabels(namespace, service.Labels), + Dir: service.WorkingDir, + User: service.User, + Mounts: mounts, + StopGracePeriod: service.StopGracePeriod, }, + Resources: resources, + RestartPolicy: restartPolicy, Placement: &swarm.Placement{ Constraints: service.Deploy.Placement.Constraints, }, @@ -367,20 +382,77 @@ func convertService( EndpointSpec: endpoint, Mode: mode, Networks: convertNetworks(service.Networks, namespace, service.Name), + UpdateConfig: convertUpdateConfig(service.Deploy.UpdateConfig), } - if service.StopGracePeriod != nil { - stopGrace, err := time.ParseDuration(*service.StopGracePeriod) - if err != nil { - return swarm.ServiceSpec{}, err - } - serviceSpec.TaskTemplate.ContainerSpec.StopGracePeriod = &stopGrace - } - - // TODO: convert mounts return serviceSpec, 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(), policy.IsAlways(), policy.IsUnlessStopped(): + return nil, 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 + } + return &swarm.UpdateConfig{ + Parallelism: source.Parallelism, + 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) @@ -407,17 +479,17 @@ func convertEnvironment(source map[string]string) []string { return output } -func convertDeployMode(mode string, replicas uint64) (swarm.ServiceMode, error) { +func convertDeployMode(mode string, replicas *uint64) (swarm.ServiceMode, error) { serviceMode := swarm.ServiceMode{} switch mode { case "global": - if replicas != 0 { + 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} + serviceMode.Replicated = &swarm.ReplicatedService{Replicas: replicas} default: return serviceMode, fmt.Errorf("Unknown mode: %s", mode) } diff --git a/opts/opts.go b/opts/opts.go index 0ac391add8..ae851537ec 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -331,16 +331,9 @@ func (c *NanoCPUs) String() string { // Set sets the value of the NanoCPU by passing a string func (c *NanoCPUs) Set(value string) error { - cpu, ok := new(big.Rat).SetString(value) - if !ok { - return fmt.Errorf("Failed to parse %v as a rational number", value) - } - nano := cpu.Mul(cpu, big.NewRat(1e9, 1)) - if !nano.IsInt() { - return fmt.Errorf("value is too precise") - } - *c = NanoCPUs(nano.Num().Int64()) - return nil + cpus, err := ParseCPUs(value) + *c = NanoCPUs(cpus) + return err } // Type returns the type @@ -352,3 +345,16 @@ func (c *NanoCPUs) Type() string { func (c *NanoCPUs) Value() int64 { return int64(*c) } + +// ParseCPUs takes a string ratio and returns an integer value of nano cpus +func ParseCPUs(value string) (int64, error) { + cpu, ok := new(big.Rat).SetString(value) + if !ok { + return 0, fmt.Errorf("failed to parse %v as a rational number", value) + } + nano := cpu.Mul(cpu, big.NewRat(1e9, 1)) + if !nano.IsInt() { + return 0, fmt.Errorf("value is too precise") + } + return nano.Num().Int64(), nil +}