| package convert // import "github.com/docker/docker/daemon/cluster/convert" | |
| import ( | |
| "fmt" | |
| "strings" | |
| types "github.com/docker/docker/api/types/swarm" | |
| "github.com/docker/docker/api/types/swarm/runtime" | |
| "github.com/docker/docker/pkg/namesgenerator" | |
| "github.com/gogo/protobuf/proto" | |
| gogotypes "github.com/gogo/protobuf/types" | |
| swarmapi "github.com/moby/swarmkit/v2/api" | |
| "github.com/moby/swarmkit/v2/api/genericresource" | |
| "github.com/pkg/errors" | |
| ) | |
| var ( | |
| // ErrUnsupportedRuntime returns an error if the runtime is not supported by the daemon | |
| ErrUnsupportedRuntime = errors.New("unsupported runtime") | |
| // ErrMismatchedRuntime returns an error if the runtime does not match the provided spec | |
| ErrMismatchedRuntime = errors.New("mismatched Runtime and *Spec fields") | |
| ) | |
| // ServiceFromGRPC converts a grpc Service to a Service. | |
| func ServiceFromGRPC(s swarmapi.Service) (types.Service, error) { | |
| curSpec, err := serviceSpecFromGRPC(&s.Spec) | |
| if err != nil { | |
| return types.Service{}, err | |
| } | |
| prevSpec, err := serviceSpecFromGRPC(s.PreviousSpec) | |
| if err != nil { | |
| return types.Service{}, err | |
| } | |
| service := types.Service{ | |
| ID: s.ID, | |
| Spec: *curSpec, | |
| PreviousSpec: prevSpec, | |
| Endpoint: endpointFromGRPC(s.Endpoint), | |
| } | |
| // Meta | |
| service.Version.Index = s.Meta.Version.Index | |
| service.CreatedAt, _ = gogotypes.TimestampFromProto(s.Meta.CreatedAt) | |
| service.UpdatedAt, _ = gogotypes.TimestampFromProto(s.Meta.UpdatedAt) | |
| if s.JobStatus != nil { | |
| service.JobStatus = &types.JobStatus{ | |
| JobIteration: types.Version{ | |
| Index: s.JobStatus.JobIteration.Index, | |
| }, | |
| } | |
| service.JobStatus.LastExecution, _ = gogotypes.TimestampFromProto(s.JobStatus.LastExecution) | |
| } | |
| // UpdateStatus | |
| if s.UpdateStatus != nil { | |
| service.UpdateStatus = &types.UpdateStatus{} | |
| switch s.UpdateStatus.State { | |
| case swarmapi.UpdateStatus_UPDATING: | |
| service.UpdateStatus.State = types.UpdateStateUpdating | |
| case swarmapi.UpdateStatus_PAUSED: | |
| service.UpdateStatus.State = types.UpdateStatePaused | |
| case swarmapi.UpdateStatus_COMPLETED: | |
| service.UpdateStatus.State = types.UpdateStateCompleted | |
| case swarmapi.UpdateStatus_ROLLBACK_STARTED: | |
| service.UpdateStatus.State = types.UpdateStateRollbackStarted | |
| case swarmapi.UpdateStatus_ROLLBACK_PAUSED: | |
| service.UpdateStatus.State = types.UpdateStateRollbackPaused | |
| case swarmapi.UpdateStatus_ROLLBACK_COMPLETED: | |
| service.UpdateStatus.State = types.UpdateStateRollbackCompleted | |
| } | |
| startedAt, _ := gogotypes.TimestampFromProto(s.UpdateStatus.StartedAt) | |
| if !startedAt.IsZero() && startedAt.Unix() != 0 { | |
| service.UpdateStatus.StartedAt = &startedAt | |
| } | |
| completedAt, _ := gogotypes.TimestampFromProto(s.UpdateStatus.CompletedAt) | |
| if !completedAt.IsZero() && completedAt.Unix() != 0 { | |
| service.UpdateStatus.CompletedAt = &completedAt | |
| } | |
| service.UpdateStatus.Message = s.UpdateStatus.Message | |
| } | |
| return service, nil | |
| } | |
| func serviceSpecFromGRPC(spec *swarmapi.ServiceSpec) (*types.ServiceSpec, error) { | |
| if spec == nil { | |
| return nil, nil | |
| } | |
| serviceNetworks := make([]types.NetworkAttachmentConfig, 0, len(spec.Networks)) | |
| for _, n := range spec.Networks { | |
| netConfig := types.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverOpts: n.DriverAttachmentOpts} | |
| serviceNetworks = append(serviceNetworks, netConfig) | |
| } | |
| taskTemplate, err := taskSpecFromGRPC(spec.Task) | |
| if err != nil { | |
| return nil, err | |
| } | |
| switch t := spec.Task.GetRuntime().(type) { | |
| case *swarmapi.TaskSpec_Container: | |
| containerConfig := t.Container | |
| taskTemplate.ContainerSpec = containerSpecFromGRPC(containerConfig) | |
| taskTemplate.Runtime = types.RuntimeContainer | |
| case *swarmapi.TaskSpec_Generic: | |
| switch t.Generic.Kind { | |
| case string(types.RuntimePlugin): | |
| taskTemplate.Runtime = types.RuntimePlugin | |
| default: | |
| return nil, fmt.Errorf("unknown task runtime type: %s", t.Generic.Payload.TypeUrl) | |
| } | |
| default: | |
| return nil, fmt.Errorf("error creating service; unsupported runtime %T", t) | |
| } | |
| convertedSpec := &types.ServiceSpec{ | |
| Annotations: annotationsFromGRPC(spec.Annotations), | |
| TaskTemplate: taskTemplate, | |
| Networks: serviceNetworks, | |
| EndpointSpec: endpointSpecFromGRPC(spec.Endpoint), | |
| } | |
| // UpdateConfig | |
| convertedSpec.UpdateConfig = updateConfigFromGRPC(spec.Update) | |
| convertedSpec.RollbackConfig = updateConfigFromGRPC(spec.Rollback) | |
| // Mode | |
| switch t := spec.GetMode().(type) { | |
| case *swarmapi.ServiceSpec_Global: | |
| convertedSpec.Mode.Global = &types.GlobalService{} | |
| case *swarmapi.ServiceSpec_Replicated: | |
| convertedSpec.Mode.Replicated = &types.ReplicatedService{ | |
| Replicas: &t.Replicated.Replicas, | |
| } | |
| case *swarmapi.ServiceSpec_ReplicatedJob: | |
| convertedSpec.Mode.ReplicatedJob = &types.ReplicatedJob{ | |
| MaxConcurrent: &t.ReplicatedJob.MaxConcurrent, | |
| TotalCompletions: &t.ReplicatedJob.TotalCompletions, | |
| } | |
| case *swarmapi.ServiceSpec_GlobalJob: | |
| convertedSpec.Mode.GlobalJob = &types.GlobalJob{} | |
| } | |
| return convertedSpec, nil | |
| } | |
| // ServiceSpecToGRPC converts a ServiceSpec to a grpc ServiceSpec. | |
| func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) { | |
| name := s.Name | |
| if name == "" { | |
| name = namesgenerator.GetRandomName(0) | |
| } | |
| serviceNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.Networks)) | |
| for _, n := range s.Networks { | |
| netConfig := &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverAttachmentOpts: n.DriverOpts} | |
| serviceNetworks = append(serviceNetworks, netConfig) | |
| } | |
| taskNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.TaskTemplate.Networks)) | |
| for _, n := range s.TaskTemplate.Networks { | |
| netConfig := &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverAttachmentOpts: n.DriverOpts} | |
| taskNetworks = append(taskNetworks, netConfig) | |
| } | |
| spec := swarmapi.ServiceSpec{ | |
| Annotations: swarmapi.Annotations{ | |
| Name: name, | |
| Labels: s.Labels, | |
| }, | |
| Task: swarmapi.TaskSpec{ | |
| Resources: resourcesToGRPC(s.TaskTemplate.Resources), | |
| LogDriver: driverToGRPC(s.TaskTemplate.LogDriver), | |
| Networks: taskNetworks, | |
| ForceUpdate: s.TaskTemplate.ForceUpdate, | |
| }, | |
| Networks: serviceNetworks, | |
| } | |
| switch s.TaskTemplate.Runtime { | |
| case types.RuntimeContainer, "": // if empty runtime default to container | |
| if s.TaskTemplate.ContainerSpec != nil { | |
| containerSpec, err := containerToGRPC(s.TaskTemplate.ContainerSpec) | |
| if err != nil { | |
| return swarmapi.ServiceSpec{}, err | |
| } | |
| if s.TaskTemplate.Resources != nil && s.TaskTemplate.Resources.Limits != nil { | |
| // TODO remove this (or keep for backward compat) once SwarmKit API moved PidsLimit into Resources | |
| containerSpec.PidsLimit = s.TaskTemplate.Resources.Limits.Pids | |
| } | |
| spec.Task.Runtime = &swarmapi.TaskSpec_Container{Container: containerSpec} | |
| } else { | |
| // If the ContainerSpec is nil, we can't set the task runtime | |
| return swarmapi.ServiceSpec{}, ErrMismatchedRuntime | |
| } | |
| case types.RuntimePlugin: | |
| if s.TaskTemplate.PluginSpec != nil { | |
| if s.Mode.Replicated != nil { | |
| return swarmapi.ServiceSpec{}, errors.New("plugins must not use replicated mode") | |
| } | |
| s.Mode.Global = &types.GlobalService{} // must always be global | |
| pluginSpec, err := proto.Marshal(s.TaskTemplate.PluginSpec) | |
| if err != nil { | |
| return swarmapi.ServiceSpec{}, err | |
| } | |
| spec.Task.Runtime = &swarmapi.TaskSpec_Generic{ | |
| Generic: &swarmapi.GenericRuntimeSpec{ | |
| Kind: string(types.RuntimePlugin), | |
| Payload: &gogotypes.Any{ | |
| TypeUrl: string(types.RuntimeURLPlugin), | |
| Value: pluginSpec, | |
| }, | |
| }, | |
| } | |
| } else { | |
| return swarmapi.ServiceSpec{}, ErrMismatchedRuntime | |
| } | |
| case types.RuntimeNetworkAttachment: | |
| // NOTE(dperny) I'm leaving this case here for completeness. The actual | |
| // code is left out deliberately, as we should refuse to parse a | |
| // Network Attachment runtime; it will cause weird behavior all over | |
| // the system if we do. Instead, fallthrough and return | |
| // ErrUnsupportedRuntime if we get one. | |
| fallthrough | |
| default: | |
| return swarmapi.ServiceSpec{}, ErrUnsupportedRuntime | |
| } | |
| restartPolicy, err := restartPolicyToGRPC(s.TaskTemplate.RestartPolicy) | |
| if err != nil { | |
| return swarmapi.ServiceSpec{}, err | |
| } | |
| spec.Task.Restart = restartPolicy | |
| if s.TaskTemplate.Placement != nil { | |
| var preferences []*swarmapi.PlacementPreference | |
| for _, pref := range s.TaskTemplate.Placement.Preferences { | |
| if pref.Spread != nil { | |
| preferences = append(preferences, &swarmapi.PlacementPreference{ | |
| Preference: &swarmapi.PlacementPreference_Spread{ | |
| Spread: &swarmapi.SpreadOver{ | |
| SpreadDescriptor: pref.Spread.SpreadDescriptor, | |
| }, | |
| }, | |
| }) | |
| } | |
| } | |
| var platforms []*swarmapi.Platform | |
| for _, plat := range s.TaskTemplate.Placement.Platforms { | |
| platforms = append(platforms, &swarmapi.Platform{ | |
| Architecture: plat.Architecture, | |
| OS: plat.OS, | |
| }) | |
| } | |
| spec.Task.Placement = &swarmapi.Placement{ | |
| Constraints: s.TaskTemplate.Placement.Constraints, | |
| Preferences: preferences, | |
| MaxReplicas: s.TaskTemplate.Placement.MaxReplicas, | |
| Platforms: platforms, | |
| } | |
| } | |
| spec.Update, err = updateConfigToGRPC(s.UpdateConfig) | |
| if err != nil { | |
| return swarmapi.ServiceSpec{}, err | |
| } | |
| spec.Rollback, err = updateConfigToGRPC(s.RollbackConfig) | |
| if err != nil { | |
| return swarmapi.ServiceSpec{}, err | |
| } | |
| if s.EndpointSpec != nil { | |
| if s.EndpointSpec.Mode != "" && | |
| s.EndpointSpec.Mode != types.ResolutionModeVIP && | |
| s.EndpointSpec.Mode != types.ResolutionModeDNSRR { | |
| return swarmapi.ServiceSpec{}, fmt.Errorf("invalid resolution mode: %q", s.EndpointSpec.Mode) | |
| } | |
| spec.Endpoint = &swarmapi.EndpointSpec{} | |
| spec.Endpoint.Mode = swarmapi.EndpointSpec_ResolutionMode(swarmapi.EndpointSpec_ResolutionMode_value[strings.ToUpper(string(s.EndpointSpec.Mode))]) | |
| for _, portConfig := range s.EndpointSpec.Ports { | |
| spec.Endpoint.Ports = append(spec.Endpoint.Ports, &swarmapi.PortConfig{ | |
| Name: portConfig.Name, | |
| Protocol: swarmapi.PortConfig_Protocol(swarmapi.PortConfig_Protocol_value[strings.ToUpper(string(portConfig.Protocol))]), | |
| PublishMode: swarmapi.PortConfig_PublishMode(swarmapi.PortConfig_PublishMode_value[strings.ToUpper(string(portConfig.PublishMode))]), | |
| TargetPort: portConfig.TargetPort, | |
| PublishedPort: portConfig.PublishedPort, | |
| }) | |
| } | |
| } | |
| // Mode | |
| numModes := 0 | |
| if s.Mode.Global != nil { | |
| numModes++ | |
| } | |
| if s.Mode.Replicated != nil { | |
| numModes++ | |
| } | |
| if s.Mode.ReplicatedJob != nil { | |
| numModes++ | |
| } | |
| if s.Mode.GlobalJob != nil { | |
| numModes++ | |
| } | |
| if numModes > 1 { | |
| return swarmapi.ServiceSpec{}, fmt.Errorf("must specify only one service mode") | |
| } | |
| if s.Mode.Global != nil { | |
| spec.Mode = &swarmapi.ServiceSpec_Global{ | |
| Global: &swarmapi.GlobalService{}, | |
| } | |
| } else if s.Mode.GlobalJob != nil { | |
| spec.Mode = &swarmapi.ServiceSpec_GlobalJob{ | |
| GlobalJob: &swarmapi.GlobalJob{}, | |
| } | |
| } else if s.Mode.ReplicatedJob != nil { | |
| // if the service is a replicated job, we have two different kinds of | |
| // values that might need to be defaulted. | |
| r := &swarmapi.ReplicatedJob{} | |
| if s.Mode.ReplicatedJob.MaxConcurrent != nil { | |
| r.MaxConcurrent = *s.Mode.ReplicatedJob.MaxConcurrent | |
| } else { | |
| r.MaxConcurrent = 1 | |
| } | |
| if s.Mode.ReplicatedJob.TotalCompletions != nil { | |
| r.TotalCompletions = *s.Mode.ReplicatedJob.TotalCompletions | |
| } else { | |
| r.TotalCompletions = r.MaxConcurrent | |
| } | |
| spec.Mode = &swarmapi.ServiceSpec_ReplicatedJob{ | |
| ReplicatedJob: r, | |
| } | |
| } else if s.Mode.Replicated != nil && s.Mode.Replicated.Replicas != nil { | |
| spec.Mode = &swarmapi.ServiceSpec_Replicated{ | |
| Replicated: &swarmapi.ReplicatedService{Replicas: *s.Mode.Replicated.Replicas}, | |
| } | |
| } else { | |
| spec.Mode = &swarmapi.ServiceSpec_Replicated{ | |
| Replicated: &swarmapi.ReplicatedService{Replicas: 1}, | |
| } | |
| } | |
| return spec, nil | |
| } | |
| func annotationsFromGRPC(ann swarmapi.Annotations) types.Annotations { | |
| a := types.Annotations{ | |
| Name: ann.Name, | |
| Labels: ann.Labels, | |
| } | |
| if a.Labels == nil { | |
| a.Labels = make(map[string]string) | |
| } | |
| return a | |
| } | |
| // GenericResourcesFromGRPC converts a GRPC GenericResource to a GenericResource | |
| func GenericResourcesFromGRPC(genericRes []*swarmapi.GenericResource) []types.GenericResource { | |
| var generic []types.GenericResource | |
| for _, res := range genericRes { | |
| var current types.GenericResource | |
| switch r := res.Resource.(type) { | |
| case *swarmapi.GenericResource_DiscreteResourceSpec: | |
| current.DiscreteResourceSpec = &types.DiscreteGenericResource{ | |
| Kind: r.DiscreteResourceSpec.Kind, | |
| Value: r.DiscreteResourceSpec.Value, | |
| } | |
| case *swarmapi.GenericResource_NamedResourceSpec: | |
| current.NamedResourceSpec = &types.NamedGenericResource{ | |
| Kind: r.NamedResourceSpec.Kind, | |
| Value: r.NamedResourceSpec.Value, | |
| } | |
| } | |
| generic = append(generic, current) | |
| } | |
| return generic | |
| } | |
| // resourcesFromGRPC creates a ResourceRequirements from the GRPC TaskSpec. | |
| // We currently require the whole TaskSpec to be passed, because PidsLimit | |
| // is returned as part of the container spec, instead of Resources | |
| // TODO move PidsLimit to Resources in the Swarm API | |
| func resourcesFromGRPC(ts *swarmapi.TaskSpec) *types.ResourceRequirements { | |
| var resources *types.ResourceRequirements | |
| if cs := ts.GetContainer(); cs != nil && cs.PidsLimit != 0 { | |
| resources = &types.ResourceRequirements{ | |
| Limits: &types.Limit{ | |
| Pids: cs.PidsLimit, | |
| }, | |
| } | |
| } | |
| if ts.Resources != nil { | |
| if resources == nil { | |
| resources = &types.ResourceRequirements{} | |
| } | |
| res := ts.Resources | |
| if res.Limits != nil { | |
| if resources.Limits == nil { | |
| resources.Limits = &types.Limit{} | |
| } | |
| resources.Limits.NanoCPUs = res.Limits.NanoCPUs | |
| resources.Limits.MemoryBytes = res.Limits.MemoryBytes | |
| } | |
| if res.Reservations != nil { | |
| resources.Reservations = &types.Resources{ | |
| NanoCPUs: res.Reservations.NanoCPUs, | |
| MemoryBytes: res.Reservations.MemoryBytes, | |
| GenericResources: GenericResourcesFromGRPC(res.Reservations.Generic), | |
| } | |
| } | |
| } | |
| return resources | |
| } | |
| // GenericResourcesToGRPC converts a GenericResource to a GRPC GenericResource | |
| func GenericResourcesToGRPC(genericRes []types.GenericResource) []*swarmapi.GenericResource { | |
| var generic []*swarmapi.GenericResource | |
| for _, res := range genericRes { | |
| var r *swarmapi.GenericResource | |
| if res.DiscreteResourceSpec != nil { | |
| r = genericresource.NewDiscrete(res.DiscreteResourceSpec.Kind, res.DiscreteResourceSpec.Value) | |
| } else if res.NamedResourceSpec != nil { | |
| r = genericresource.NewString(res.NamedResourceSpec.Kind, res.NamedResourceSpec.Value) | |
| } | |
| generic = append(generic, r) | |
| } | |
| return generic | |
| } | |
| func resourcesToGRPC(res *types.ResourceRequirements) *swarmapi.ResourceRequirements { | |
| var reqs *swarmapi.ResourceRequirements | |
| if res != nil { | |
| reqs = &swarmapi.ResourceRequirements{} | |
| if res.Limits != nil { | |
| // TODO add PidsLimit once Swarm API has been updated to move it into Limits | |
| reqs.Limits = &swarmapi.Resources{ | |
| NanoCPUs: res.Limits.NanoCPUs, | |
| MemoryBytes: res.Limits.MemoryBytes, | |
| } | |
| } | |
| if res.Reservations != nil { | |
| reqs.Reservations = &swarmapi.Resources{ | |
| NanoCPUs: res.Reservations.NanoCPUs, | |
| MemoryBytes: res.Reservations.MemoryBytes, | |
| Generic: GenericResourcesToGRPC(res.Reservations.GenericResources), | |
| } | |
| } | |
| } | |
| return reqs | |
| } | |
| func restartPolicyFromGRPC(p *swarmapi.RestartPolicy) *types.RestartPolicy { | |
| var rp *types.RestartPolicy | |
| if p != nil { | |
| rp = &types.RestartPolicy{} | |
| switch p.Condition { | |
| case swarmapi.RestartOnNone: | |
| rp.Condition = types.RestartPolicyConditionNone | |
| case swarmapi.RestartOnFailure: | |
| rp.Condition = types.RestartPolicyConditionOnFailure | |
| case swarmapi.RestartOnAny: | |
| rp.Condition = types.RestartPolicyConditionAny | |
| default: | |
| rp.Condition = types.RestartPolicyConditionAny | |
| } | |
| if p.Delay != nil { | |
| delay, _ := gogotypes.DurationFromProto(p.Delay) | |
| rp.Delay = &delay | |
| } | |
| if p.Window != nil { | |
| window, _ := gogotypes.DurationFromProto(p.Window) | |
| rp.Window = &window | |
| } | |
| rp.MaxAttempts = &p.MaxAttempts | |
| } | |
| return rp | |
| } | |
| func restartPolicyToGRPC(p *types.RestartPolicy) (*swarmapi.RestartPolicy, error) { | |
| var rp *swarmapi.RestartPolicy | |
| if p != nil { | |
| rp = &swarmapi.RestartPolicy{} | |
| switch p.Condition { | |
| case types.RestartPolicyConditionNone: | |
| rp.Condition = swarmapi.RestartOnNone | |
| case types.RestartPolicyConditionOnFailure: | |
| rp.Condition = swarmapi.RestartOnFailure | |
| case types.RestartPolicyConditionAny: | |
| rp.Condition = swarmapi.RestartOnAny | |
| default: | |
| if string(p.Condition) != "" { | |
| return nil, fmt.Errorf("invalid RestartCondition: %q", p.Condition) | |
| } | |
| rp.Condition = swarmapi.RestartOnAny | |
| } | |
| if p.Delay != nil { | |
| rp.Delay = gogotypes.DurationProto(*p.Delay) | |
| } | |
| if p.Window != nil { | |
| rp.Window = gogotypes.DurationProto(*p.Window) | |
| } | |
| if p.MaxAttempts != nil { | |
| rp.MaxAttempts = *p.MaxAttempts | |
| } | |
| } | |
| return rp, nil | |
| } | |
| func placementFromGRPC(p *swarmapi.Placement) *types.Placement { | |
| if p == nil { | |
| return nil | |
| } | |
| r := &types.Placement{ | |
| Constraints: p.Constraints, | |
| MaxReplicas: p.MaxReplicas, | |
| } | |
| for _, pref := range p.Preferences { | |
| if spread := pref.GetSpread(); spread != nil { | |
| r.Preferences = append(r.Preferences, types.PlacementPreference{ | |
| Spread: &types.SpreadOver{ | |
| SpreadDescriptor: spread.SpreadDescriptor, | |
| }, | |
| }) | |
| } | |
| } | |
| for _, plat := range p.Platforms { | |
| r.Platforms = append(r.Platforms, types.Platform{ | |
| Architecture: plat.Architecture, | |
| OS: plat.OS, | |
| }) | |
| } | |
| return r | |
| } | |
| func driverFromGRPC(p *swarmapi.Driver) *types.Driver { | |
| if p == nil { | |
| return nil | |
| } | |
| return &types.Driver{ | |
| Name: p.Name, | |
| Options: p.Options, | |
| } | |
| } | |
| func driverToGRPC(p *types.Driver) *swarmapi.Driver { | |
| if p == nil { | |
| return nil | |
| } | |
| return &swarmapi.Driver{ | |
| Name: p.Name, | |
| Options: p.Options, | |
| } | |
| } | |
| func updateConfigFromGRPC(updateConfig *swarmapi.UpdateConfig) *types.UpdateConfig { | |
| if updateConfig == nil { | |
| return nil | |
| } | |
| converted := &types.UpdateConfig{ | |
| Parallelism: updateConfig.Parallelism, | |
| MaxFailureRatio: updateConfig.MaxFailureRatio, | |
| } | |
| converted.Delay = updateConfig.Delay | |
| if updateConfig.Monitor != nil { | |
| converted.Monitor, _ = gogotypes.DurationFromProto(updateConfig.Monitor) | |
| } | |
| switch updateConfig.FailureAction { | |
| case swarmapi.UpdateConfig_PAUSE: | |
| converted.FailureAction = types.UpdateFailureActionPause | |
| case swarmapi.UpdateConfig_CONTINUE: | |
| converted.FailureAction = types.UpdateFailureActionContinue | |
| case swarmapi.UpdateConfig_ROLLBACK: | |
| converted.FailureAction = types.UpdateFailureActionRollback | |
| } | |
| switch updateConfig.Order { | |
| case swarmapi.UpdateConfig_STOP_FIRST: | |
| converted.Order = types.UpdateOrderStopFirst | |
| case swarmapi.UpdateConfig_START_FIRST: | |
| converted.Order = types.UpdateOrderStartFirst | |
| } | |
| return converted | |
| } | |
| func updateConfigToGRPC(updateConfig *types.UpdateConfig) (*swarmapi.UpdateConfig, error) { | |
| if updateConfig == nil { | |
| return nil, nil | |
| } | |
| converted := &swarmapi.UpdateConfig{ | |
| Parallelism: updateConfig.Parallelism, | |
| Delay: updateConfig.Delay, | |
| MaxFailureRatio: updateConfig.MaxFailureRatio, | |
| } | |
| switch updateConfig.FailureAction { | |
| case types.UpdateFailureActionPause, "": | |
| converted.FailureAction = swarmapi.UpdateConfig_PAUSE | |
| case types.UpdateFailureActionContinue: | |
| converted.FailureAction = swarmapi.UpdateConfig_CONTINUE | |
| case types.UpdateFailureActionRollback: | |
| converted.FailureAction = swarmapi.UpdateConfig_ROLLBACK | |
| default: | |
| return nil, fmt.Errorf("unrecognized update failure action %s", updateConfig.FailureAction) | |
| } | |
| if updateConfig.Monitor != 0 { | |
| converted.Monitor = gogotypes.DurationProto(updateConfig.Monitor) | |
| } | |
| switch updateConfig.Order { | |
| case types.UpdateOrderStopFirst, "": | |
| converted.Order = swarmapi.UpdateConfig_STOP_FIRST | |
| case types.UpdateOrderStartFirst: | |
| converted.Order = swarmapi.UpdateConfig_START_FIRST | |
| default: | |
| return nil, fmt.Errorf("unrecognized update order %s", updateConfig.Order) | |
| } | |
| return converted, nil | |
| } | |
| func networkAttachmentSpecFromGRPC(attachment swarmapi.NetworkAttachmentSpec) *types.NetworkAttachmentSpec { | |
| return &types.NetworkAttachmentSpec{ | |
| ContainerID: attachment.ContainerID, | |
| } | |
| } | |
| func taskSpecFromGRPC(taskSpec swarmapi.TaskSpec) (types.TaskSpec, error) { | |
| taskNetworks := make([]types.NetworkAttachmentConfig, 0, len(taskSpec.Networks)) | |
| for _, n := range taskSpec.Networks { | |
| netConfig := types.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverOpts: n.DriverAttachmentOpts} | |
| taskNetworks = append(taskNetworks, netConfig) | |
| } | |
| t := types.TaskSpec{ | |
| Resources: resourcesFromGRPC(&taskSpec), | |
| RestartPolicy: restartPolicyFromGRPC(taskSpec.Restart), | |
| Placement: placementFromGRPC(taskSpec.Placement), | |
| LogDriver: driverFromGRPC(taskSpec.LogDriver), | |
| Networks: taskNetworks, | |
| ForceUpdate: taskSpec.ForceUpdate, | |
| } | |
| switch taskSpec.GetRuntime().(type) { | |
| case *swarmapi.TaskSpec_Container, nil: | |
| c := taskSpec.GetContainer() | |
| if c != nil { | |
| t.ContainerSpec = containerSpecFromGRPC(c) | |
| } | |
| case *swarmapi.TaskSpec_Generic: | |
| g := taskSpec.GetGeneric() | |
| if g != nil { | |
| switch g.Kind { | |
| case string(types.RuntimePlugin): | |
| var p runtime.PluginSpec | |
| if err := proto.Unmarshal(g.Payload.Value, &p); err != nil { | |
| return t, errors.Wrap(err, "error unmarshalling plugin spec") | |
| } | |
| t.PluginSpec = &p | |
| } | |
| } | |
| case *swarmapi.TaskSpec_Attachment: | |
| a := taskSpec.GetAttachment() | |
| if a != nil { | |
| t.NetworkAttachmentSpec = networkAttachmentSpecFromGRPC(*a) | |
| } | |
| t.Runtime = types.RuntimeNetworkAttachment | |
| } | |
| return t, nil | |
| } |