github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/cluster/convert/service.go (about) 1 package convert // import "github.com/docker/docker/daemon/cluster/convert" 2 3 import ( 4 "fmt" 5 "strings" 6 7 types "github.com/docker/docker/api/types/swarm" 8 "github.com/docker/docker/api/types/swarm/runtime" 9 "github.com/docker/docker/pkg/namesgenerator" 10 "github.com/gogo/protobuf/proto" 11 gogotypes "github.com/gogo/protobuf/types" 12 swarmapi "github.com/moby/swarmkit/v2/api" 13 "github.com/moby/swarmkit/v2/api/genericresource" 14 "github.com/pkg/errors" 15 ) 16 17 var ( 18 // ErrUnsupportedRuntime returns an error if the runtime is not supported by the daemon 19 ErrUnsupportedRuntime = errors.New("unsupported runtime") 20 // ErrMismatchedRuntime returns an error if the runtime does not match the provided spec 21 ErrMismatchedRuntime = errors.New("mismatched Runtime and *Spec fields") 22 ) 23 24 // ServiceFromGRPC converts a grpc Service to a Service. 25 func ServiceFromGRPC(s swarmapi.Service) (types.Service, error) { 26 curSpec, err := serviceSpecFromGRPC(&s.Spec) 27 if err != nil { 28 return types.Service{}, err 29 } 30 prevSpec, err := serviceSpecFromGRPC(s.PreviousSpec) 31 if err != nil { 32 return types.Service{}, err 33 } 34 service := types.Service{ 35 ID: s.ID, 36 Spec: *curSpec, 37 PreviousSpec: prevSpec, 38 39 Endpoint: endpointFromGRPC(s.Endpoint), 40 } 41 42 // Meta 43 service.Version.Index = s.Meta.Version.Index 44 service.CreatedAt, _ = gogotypes.TimestampFromProto(s.Meta.CreatedAt) 45 service.UpdatedAt, _ = gogotypes.TimestampFromProto(s.Meta.UpdatedAt) 46 47 if s.JobStatus != nil { 48 service.JobStatus = &types.JobStatus{ 49 JobIteration: types.Version{ 50 Index: s.JobStatus.JobIteration.Index, 51 }, 52 } 53 service.JobStatus.LastExecution, _ = gogotypes.TimestampFromProto(s.JobStatus.LastExecution) 54 } 55 56 // UpdateStatus 57 if s.UpdateStatus != nil { 58 service.UpdateStatus = &types.UpdateStatus{} 59 switch s.UpdateStatus.State { 60 case swarmapi.UpdateStatus_UPDATING: 61 service.UpdateStatus.State = types.UpdateStateUpdating 62 case swarmapi.UpdateStatus_PAUSED: 63 service.UpdateStatus.State = types.UpdateStatePaused 64 case swarmapi.UpdateStatus_COMPLETED: 65 service.UpdateStatus.State = types.UpdateStateCompleted 66 case swarmapi.UpdateStatus_ROLLBACK_STARTED: 67 service.UpdateStatus.State = types.UpdateStateRollbackStarted 68 case swarmapi.UpdateStatus_ROLLBACK_PAUSED: 69 service.UpdateStatus.State = types.UpdateStateRollbackPaused 70 case swarmapi.UpdateStatus_ROLLBACK_COMPLETED: 71 service.UpdateStatus.State = types.UpdateStateRollbackCompleted 72 } 73 74 startedAt, _ := gogotypes.TimestampFromProto(s.UpdateStatus.StartedAt) 75 if !startedAt.IsZero() && startedAt.Unix() != 0 { 76 service.UpdateStatus.StartedAt = &startedAt 77 } 78 79 completedAt, _ := gogotypes.TimestampFromProto(s.UpdateStatus.CompletedAt) 80 if !completedAt.IsZero() && completedAt.Unix() != 0 { 81 service.UpdateStatus.CompletedAt = &completedAt 82 } 83 84 service.UpdateStatus.Message = s.UpdateStatus.Message 85 } 86 87 return service, nil 88 } 89 90 func serviceSpecFromGRPC(spec *swarmapi.ServiceSpec) (*types.ServiceSpec, error) { 91 if spec == nil { 92 return nil, nil 93 } 94 95 serviceNetworks := make([]types.NetworkAttachmentConfig, 0, len(spec.Networks)) 96 for _, n := range spec.Networks { 97 netConfig := types.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverOpts: n.DriverAttachmentOpts} 98 serviceNetworks = append(serviceNetworks, netConfig) 99 } 100 101 taskTemplate, err := taskSpecFromGRPC(spec.Task) 102 if err != nil { 103 return nil, err 104 } 105 106 switch t := spec.Task.GetRuntime().(type) { 107 case *swarmapi.TaskSpec_Container: 108 containerConfig := t.Container 109 taskTemplate.ContainerSpec = containerSpecFromGRPC(containerConfig) 110 taskTemplate.Runtime = types.RuntimeContainer 111 case *swarmapi.TaskSpec_Generic: 112 switch t.Generic.Kind { 113 case string(types.RuntimePlugin): 114 taskTemplate.Runtime = types.RuntimePlugin 115 default: 116 return nil, fmt.Errorf("unknown task runtime type: %s", t.Generic.Payload.TypeUrl) 117 } 118 119 default: 120 return nil, fmt.Errorf("error creating service; unsupported runtime %T", t) 121 } 122 123 convertedSpec := &types.ServiceSpec{ 124 Annotations: annotationsFromGRPC(spec.Annotations), 125 TaskTemplate: taskTemplate, 126 Networks: serviceNetworks, 127 EndpointSpec: endpointSpecFromGRPC(spec.Endpoint), 128 } 129 130 // UpdateConfig 131 convertedSpec.UpdateConfig = updateConfigFromGRPC(spec.Update) 132 convertedSpec.RollbackConfig = updateConfigFromGRPC(spec.Rollback) 133 134 // Mode 135 switch t := spec.GetMode().(type) { 136 case *swarmapi.ServiceSpec_Global: 137 convertedSpec.Mode.Global = &types.GlobalService{} 138 case *swarmapi.ServiceSpec_Replicated: 139 convertedSpec.Mode.Replicated = &types.ReplicatedService{ 140 Replicas: &t.Replicated.Replicas, 141 } 142 case *swarmapi.ServiceSpec_ReplicatedJob: 143 convertedSpec.Mode.ReplicatedJob = &types.ReplicatedJob{ 144 MaxConcurrent: &t.ReplicatedJob.MaxConcurrent, 145 TotalCompletions: &t.ReplicatedJob.TotalCompletions, 146 } 147 case *swarmapi.ServiceSpec_GlobalJob: 148 convertedSpec.Mode.GlobalJob = &types.GlobalJob{} 149 } 150 151 return convertedSpec, nil 152 } 153 154 // ServiceSpecToGRPC converts a ServiceSpec to a grpc ServiceSpec. 155 func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) { 156 name := s.Name 157 if name == "" { 158 name = namesgenerator.GetRandomName(0) 159 } 160 161 serviceNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.Networks)) 162 for _, n := range s.Networks { 163 netConfig := &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverAttachmentOpts: n.DriverOpts} 164 serviceNetworks = append(serviceNetworks, netConfig) 165 } 166 167 taskNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.TaskTemplate.Networks)) 168 for _, n := range s.TaskTemplate.Networks { 169 netConfig := &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverAttachmentOpts: n.DriverOpts} 170 taskNetworks = append(taskNetworks, netConfig) 171 } 172 173 spec := swarmapi.ServiceSpec{ 174 Annotations: swarmapi.Annotations{ 175 Name: name, 176 Labels: s.Labels, 177 }, 178 Task: swarmapi.TaskSpec{ 179 Resources: resourcesToGRPC(s.TaskTemplate.Resources), 180 LogDriver: driverToGRPC(s.TaskTemplate.LogDriver), 181 Networks: taskNetworks, 182 ForceUpdate: s.TaskTemplate.ForceUpdate, 183 }, 184 Networks: serviceNetworks, 185 } 186 187 switch s.TaskTemplate.Runtime { 188 case types.RuntimeContainer, "": // if empty runtime default to container 189 if s.TaskTemplate.ContainerSpec != nil { 190 containerSpec, err := containerToGRPC(s.TaskTemplate.ContainerSpec) 191 if err != nil { 192 return swarmapi.ServiceSpec{}, err 193 } 194 if s.TaskTemplate.Resources != nil && s.TaskTemplate.Resources.Limits != nil { 195 // TODO remove this (or keep for backward compat) once SwarmKit API moved PidsLimit into Resources 196 containerSpec.PidsLimit = s.TaskTemplate.Resources.Limits.Pids 197 } 198 spec.Task.Runtime = &swarmapi.TaskSpec_Container{Container: containerSpec} 199 } else { 200 // If the ContainerSpec is nil, we can't set the task runtime 201 return swarmapi.ServiceSpec{}, ErrMismatchedRuntime 202 } 203 case types.RuntimePlugin: 204 if s.TaskTemplate.PluginSpec != nil { 205 if s.Mode.Replicated != nil { 206 return swarmapi.ServiceSpec{}, errors.New("plugins must not use replicated mode") 207 } 208 209 s.Mode.Global = &types.GlobalService{} // must always be global 210 211 pluginSpec, err := proto.Marshal(s.TaskTemplate.PluginSpec) 212 if err != nil { 213 return swarmapi.ServiceSpec{}, err 214 } 215 spec.Task.Runtime = &swarmapi.TaskSpec_Generic{ 216 Generic: &swarmapi.GenericRuntimeSpec{ 217 Kind: string(types.RuntimePlugin), 218 Payload: &gogotypes.Any{ 219 TypeUrl: string(types.RuntimeURLPlugin), 220 Value: pluginSpec, 221 }, 222 }, 223 } 224 } else { 225 return swarmapi.ServiceSpec{}, ErrMismatchedRuntime 226 } 227 case types.RuntimeNetworkAttachment: 228 // NOTE(dperny) I'm leaving this case here for completeness. The actual 229 // code is left out deliberately, as we should refuse to parse a 230 // Network Attachment runtime; it will cause weird behavior all over 231 // the system if we do. Instead, fallthrough and return 232 // ErrUnsupportedRuntime if we get one. 233 fallthrough 234 default: 235 return swarmapi.ServiceSpec{}, ErrUnsupportedRuntime 236 } 237 238 restartPolicy, err := restartPolicyToGRPC(s.TaskTemplate.RestartPolicy) 239 if err != nil { 240 return swarmapi.ServiceSpec{}, err 241 } 242 spec.Task.Restart = restartPolicy 243 244 if s.TaskTemplate.Placement != nil { 245 var preferences []*swarmapi.PlacementPreference 246 for _, pref := range s.TaskTemplate.Placement.Preferences { 247 if pref.Spread != nil { 248 preferences = append(preferences, &swarmapi.PlacementPreference{ 249 Preference: &swarmapi.PlacementPreference_Spread{ 250 Spread: &swarmapi.SpreadOver{ 251 SpreadDescriptor: pref.Spread.SpreadDescriptor, 252 }, 253 }, 254 }) 255 } 256 } 257 var platforms []*swarmapi.Platform 258 for _, plat := range s.TaskTemplate.Placement.Platforms { 259 platforms = append(platforms, &swarmapi.Platform{ 260 Architecture: plat.Architecture, 261 OS: plat.OS, 262 }) 263 } 264 spec.Task.Placement = &swarmapi.Placement{ 265 Constraints: s.TaskTemplate.Placement.Constraints, 266 Preferences: preferences, 267 MaxReplicas: s.TaskTemplate.Placement.MaxReplicas, 268 Platforms: platforms, 269 } 270 } 271 272 spec.Update, err = updateConfigToGRPC(s.UpdateConfig) 273 if err != nil { 274 return swarmapi.ServiceSpec{}, err 275 } 276 spec.Rollback, err = updateConfigToGRPC(s.RollbackConfig) 277 if err != nil { 278 return swarmapi.ServiceSpec{}, err 279 } 280 281 if s.EndpointSpec != nil { 282 if s.EndpointSpec.Mode != "" && 283 s.EndpointSpec.Mode != types.ResolutionModeVIP && 284 s.EndpointSpec.Mode != types.ResolutionModeDNSRR { 285 return swarmapi.ServiceSpec{}, fmt.Errorf("invalid resolution mode: %q", s.EndpointSpec.Mode) 286 } 287 288 spec.Endpoint = &swarmapi.EndpointSpec{} 289 290 spec.Endpoint.Mode = swarmapi.EndpointSpec_ResolutionMode(swarmapi.EndpointSpec_ResolutionMode_value[strings.ToUpper(string(s.EndpointSpec.Mode))]) 291 292 for _, portConfig := range s.EndpointSpec.Ports { 293 spec.Endpoint.Ports = append(spec.Endpoint.Ports, &swarmapi.PortConfig{ 294 Name: portConfig.Name, 295 Protocol: swarmapi.PortConfig_Protocol(swarmapi.PortConfig_Protocol_value[strings.ToUpper(string(portConfig.Protocol))]), 296 PublishMode: swarmapi.PortConfig_PublishMode(swarmapi.PortConfig_PublishMode_value[strings.ToUpper(string(portConfig.PublishMode))]), 297 TargetPort: portConfig.TargetPort, 298 PublishedPort: portConfig.PublishedPort, 299 }) 300 } 301 } 302 303 // Mode 304 numModes := 0 305 if s.Mode.Global != nil { 306 numModes++ 307 } 308 if s.Mode.Replicated != nil { 309 numModes++ 310 } 311 if s.Mode.ReplicatedJob != nil { 312 numModes++ 313 } 314 if s.Mode.GlobalJob != nil { 315 numModes++ 316 } 317 318 if numModes > 1 { 319 return swarmapi.ServiceSpec{}, fmt.Errorf("must specify only one service mode") 320 } 321 322 if s.Mode.Global != nil { 323 spec.Mode = &swarmapi.ServiceSpec_Global{ 324 Global: &swarmapi.GlobalService{}, 325 } 326 } else if s.Mode.GlobalJob != nil { 327 spec.Mode = &swarmapi.ServiceSpec_GlobalJob{ 328 GlobalJob: &swarmapi.GlobalJob{}, 329 } 330 } else if s.Mode.ReplicatedJob != nil { 331 // if the service is a replicated job, we have two different kinds of 332 // values that might need to be defaulted. 333 334 r := &swarmapi.ReplicatedJob{} 335 if s.Mode.ReplicatedJob.MaxConcurrent != nil { 336 r.MaxConcurrent = *s.Mode.ReplicatedJob.MaxConcurrent 337 } else { 338 r.MaxConcurrent = 1 339 } 340 341 if s.Mode.ReplicatedJob.TotalCompletions != nil { 342 r.TotalCompletions = *s.Mode.ReplicatedJob.TotalCompletions 343 } else { 344 r.TotalCompletions = r.MaxConcurrent 345 } 346 347 spec.Mode = &swarmapi.ServiceSpec_ReplicatedJob{ 348 ReplicatedJob: r, 349 } 350 } else if s.Mode.Replicated != nil && s.Mode.Replicated.Replicas != nil { 351 spec.Mode = &swarmapi.ServiceSpec_Replicated{ 352 Replicated: &swarmapi.ReplicatedService{Replicas: *s.Mode.Replicated.Replicas}, 353 } 354 } else { 355 spec.Mode = &swarmapi.ServiceSpec_Replicated{ 356 Replicated: &swarmapi.ReplicatedService{Replicas: 1}, 357 } 358 } 359 360 return spec, nil 361 } 362 363 func annotationsFromGRPC(ann swarmapi.Annotations) types.Annotations { 364 a := types.Annotations{ 365 Name: ann.Name, 366 Labels: ann.Labels, 367 } 368 369 if a.Labels == nil { 370 a.Labels = make(map[string]string) 371 } 372 373 return a 374 } 375 376 // GenericResourcesFromGRPC converts a GRPC GenericResource to a GenericResource 377 func GenericResourcesFromGRPC(genericRes []*swarmapi.GenericResource) []types.GenericResource { 378 var generic []types.GenericResource 379 for _, res := range genericRes { 380 var current types.GenericResource 381 382 switch r := res.Resource.(type) { 383 case *swarmapi.GenericResource_DiscreteResourceSpec: 384 current.DiscreteResourceSpec = &types.DiscreteGenericResource{ 385 Kind: r.DiscreteResourceSpec.Kind, 386 Value: r.DiscreteResourceSpec.Value, 387 } 388 case *swarmapi.GenericResource_NamedResourceSpec: 389 current.NamedResourceSpec = &types.NamedGenericResource{ 390 Kind: r.NamedResourceSpec.Kind, 391 Value: r.NamedResourceSpec.Value, 392 } 393 } 394 395 generic = append(generic, current) 396 } 397 398 return generic 399 } 400 401 // resourcesFromGRPC creates a ResourceRequirements from the GRPC TaskSpec. 402 // We currently require the whole TaskSpec to be passed, because PidsLimit 403 // is returned as part of the container spec, instead of Resources 404 // TODO move PidsLimit to Resources in the Swarm API 405 func resourcesFromGRPC(ts *swarmapi.TaskSpec) *types.ResourceRequirements { 406 var resources *types.ResourceRequirements 407 408 if cs := ts.GetContainer(); cs != nil && cs.PidsLimit != 0 { 409 resources = &types.ResourceRequirements{ 410 Limits: &types.Limit{ 411 Pids: cs.PidsLimit, 412 }, 413 } 414 } 415 if ts.Resources != nil { 416 if resources == nil { 417 resources = &types.ResourceRequirements{} 418 } 419 res := ts.Resources 420 if res.Limits != nil { 421 if resources.Limits == nil { 422 resources.Limits = &types.Limit{} 423 } 424 resources.Limits.NanoCPUs = res.Limits.NanoCPUs 425 resources.Limits.MemoryBytes = res.Limits.MemoryBytes 426 } 427 if res.Reservations != nil { 428 resources.Reservations = &types.Resources{ 429 NanoCPUs: res.Reservations.NanoCPUs, 430 MemoryBytes: res.Reservations.MemoryBytes, 431 GenericResources: GenericResourcesFromGRPC(res.Reservations.Generic), 432 } 433 } 434 } 435 436 return resources 437 } 438 439 // GenericResourcesToGRPC converts a GenericResource to a GRPC GenericResource 440 func GenericResourcesToGRPC(genericRes []types.GenericResource) []*swarmapi.GenericResource { 441 var generic []*swarmapi.GenericResource 442 for _, res := range genericRes { 443 var r *swarmapi.GenericResource 444 445 if res.DiscreteResourceSpec != nil { 446 r = genericresource.NewDiscrete(res.DiscreteResourceSpec.Kind, res.DiscreteResourceSpec.Value) 447 } else if res.NamedResourceSpec != nil { 448 r = genericresource.NewString(res.NamedResourceSpec.Kind, res.NamedResourceSpec.Value) 449 } 450 451 generic = append(generic, r) 452 } 453 454 return generic 455 } 456 457 func resourcesToGRPC(res *types.ResourceRequirements) *swarmapi.ResourceRequirements { 458 var reqs *swarmapi.ResourceRequirements 459 if res != nil { 460 reqs = &swarmapi.ResourceRequirements{} 461 if res.Limits != nil { 462 // TODO add PidsLimit once Swarm API has been updated to move it into Limits 463 reqs.Limits = &swarmapi.Resources{ 464 NanoCPUs: res.Limits.NanoCPUs, 465 MemoryBytes: res.Limits.MemoryBytes, 466 } 467 } 468 if res.Reservations != nil { 469 reqs.Reservations = &swarmapi.Resources{ 470 NanoCPUs: res.Reservations.NanoCPUs, 471 MemoryBytes: res.Reservations.MemoryBytes, 472 Generic: GenericResourcesToGRPC(res.Reservations.GenericResources), 473 } 474 } 475 } 476 return reqs 477 } 478 479 func restartPolicyFromGRPC(p *swarmapi.RestartPolicy) *types.RestartPolicy { 480 var rp *types.RestartPolicy 481 if p != nil { 482 rp = &types.RestartPolicy{} 483 484 switch p.Condition { 485 case swarmapi.RestartOnNone: 486 rp.Condition = types.RestartPolicyConditionNone 487 case swarmapi.RestartOnFailure: 488 rp.Condition = types.RestartPolicyConditionOnFailure 489 case swarmapi.RestartOnAny: 490 rp.Condition = types.RestartPolicyConditionAny 491 default: 492 rp.Condition = types.RestartPolicyConditionAny 493 } 494 495 if p.Delay != nil { 496 delay, _ := gogotypes.DurationFromProto(p.Delay) 497 rp.Delay = &delay 498 } 499 if p.Window != nil { 500 window, _ := gogotypes.DurationFromProto(p.Window) 501 rp.Window = &window 502 } 503 504 rp.MaxAttempts = &p.MaxAttempts 505 } 506 return rp 507 } 508 509 func restartPolicyToGRPC(p *types.RestartPolicy) (*swarmapi.RestartPolicy, error) { 510 var rp *swarmapi.RestartPolicy 511 if p != nil { 512 rp = &swarmapi.RestartPolicy{} 513 514 switch p.Condition { 515 case types.RestartPolicyConditionNone: 516 rp.Condition = swarmapi.RestartOnNone 517 case types.RestartPolicyConditionOnFailure: 518 rp.Condition = swarmapi.RestartOnFailure 519 case types.RestartPolicyConditionAny: 520 rp.Condition = swarmapi.RestartOnAny 521 default: 522 if string(p.Condition) != "" { 523 return nil, fmt.Errorf("invalid RestartCondition: %q", p.Condition) 524 } 525 rp.Condition = swarmapi.RestartOnAny 526 } 527 528 if p.Delay != nil { 529 rp.Delay = gogotypes.DurationProto(*p.Delay) 530 } 531 if p.Window != nil { 532 rp.Window = gogotypes.DurationProto(*p.Window) 533 } 534 if p.MaxAttempts != nil { 535 rp.MaxAttempts = *p.MaxAttempts 536 } 537 } 538 return rp, nil 539 } 540 541 func placementFromGRPC(p *swarmapi.Placement) *types.Placement { 542 if p == nil { 543 return nil 544 } 545 r := &types.Placement{ 546 Constraints: p.Constraints, 547 MaxReplicas: p.MaxReplicas, 548 } 549 550 for _, pref := range p.Preferences { 551 if spread := pref.GetSpread(); spread != nil { 552 r.Preferences = append(r.Preferences, types.PlacementPreference{ 553 Spread: &types.SpreadOver{ 554 SpreadDescriptor: spread.SpreadDescriptor, 555 }, 556 }) 557 } 558 } 559 560 for _, plat := range p.Platforms { 561 r.Platforms = append(r.Platforms, types.Platform{ 562 Architecture: plat.Architecture, 563 OS: plat.OS, 564 }) 565 } 566 567 return r 568 } 569 570 func driverFromGRPC(p *swarmapi.Driver) *types.Driver { 571 if p == nil { 572 return nil 573 } 574 575 return &types.Driver{ 576 Name: p.Name, 577 Options: p.Options, 578 } 579 } 580 581 func driverToGRPC(p *types.Driver) *swarmapi.Driver { 582 if p == nil { 583 return nil 584 } 585 586 return &swarmapi.Driver{ 587 Name: p.Name, 588 Options: p.Options, 589 } 590 } 591 592 func updateConfigFromGRPC(updateConfig *swarmapi.UpdateConfig) *types.UpdateConfig { 593 if updateConfig == nil { 594 return nil 595 } 596 597 converted := &types.UpdateConfig{ 598 Parallelism: updateConfig.Parallelism, 599 MaxFailureRatio: updateConfig.MaxFailureRatio, 600 } 601 602 converted.Delay = updateConfig.Delay 603 if updateConfig.Monitor != nil { 604 converted.Monitor, _ = gogotypes.DurationFromProto(updateConfig.Monitor) 605 } 606 607 switch updateConfig.FailureAction { 608 case swarmapi.UpdateConfig_PAUSE: 609 converted.FailureAction = types.UpdateFailureActionPause 610 case swarmapi.UpdateConfig_CONTINUE: 611 converted.FailureAction = types.UpdateFailureActionContinue 612 case swarmapi.UpdateConfig_ROLLBACK: 613 converted.FailureAction = types.UpdateFailureActionRollback 614 } 615 616 switch updateConfig.Order { 617 case swarmapi.UpdateConfig_STOP_FIRST: 618 converted.Order = types.UpdateOrderStopFirst 619 case swarmapi.UpdateConfig_START_FIRST: 620 converted.Order = types.UpdateOrderStartFirst 621 } 622 623 return converted 624 } 625 626 func updateConfigToGRPC(updateConfig *types.UpdateConfig) (*swarmapi.UpdateConfig, error) { 627 if updateConfig == nil { 628 return nil, nil 629 } 630 631 converted := &swarmapi.UpdateConfig{ 632 Parallelism: updateConfig.Parallelism, 633 Delay: updateConfig.Delay, 634 MaxFailureRatio: updateConfig.MaxFailureRatio, 635 } 636 637 switch updateConfig.FailureAction { 638 case types.UpdateFailureActionPause, "": 639 converted.FailureAction = swarmapi.UpdateConfig_PAUSE 640 case types.UpdateFailureActionContinue: 641 converted.FailureAction = swarmapi.UpdateConfig_CONTINUE 642 case types.UpdateFailureActionRollback: 643 converted.FailureAction = swarmapi.UpdateConfig_ROLLBACK 644 default: 645 return nil, fmt.Errorf("unrecognized update failure action %s", updateConfig.FailureAction) 646 } 647 if updateConfig.Monitor != 0 { 648 converted.Monitor = gogotypes.DurationProto(updateConfig.Monitor) 649 } 650 651 switch updateConfig.Order { 652 case types.UpdateOrderStopFirst, "": 653 converted.Order = swarmapi.UpdateConfig_STOP_FIRST 654 case types.UpdateOrderStartFirst: 655 converted.Order = swarmapi.UpdateConfig_START_FIRST 656 default: 657 return nil, fmt.Errorf("unrecognized update order %s", updateConfig.Order) 658 } 659 660 return converted, nil 661 } 662 663 func networkAttachmentSpecFromGRPC(attachment swarmapi.NetworkAttachmentSpec) *types.NetworkAttachmentSpec { 664 return &types.NetworkAttachmentSpec{ 665 ContainerID: attachment.ContainerID, 666 } 667 } 668 669 func taskSpecFromGRPC(taskSpec swarmapi.TaskSpec) (types.TaskSpec, error) { 670 taskNetworks := make([]types.NetworkAttachmentConfig, 0, len(taskSpec.Networks)) 671 for _, n := range taskSpec.Networks { 672 netConfig := types.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverOpts: n.DriverAttachmentOpts} 673 taskNetworks = append(taskNetworks, netConfig) 674 } 675 676 t := types.TaskSpec{ 677 Resources: resourcesFromGRPC(&taskSpec), 678 RestartPolicy: restartPolicyFromGRPC(taskSpec.Restart), 679 Placement: placementFromGRPC(taskSpec.Placement), 680 LogDriver: driverFromGRPC(taskSpec.LogDriver), 681 Networks: taskNetworks, 682 ForceUpdate: taskSpec.ForceUpdate, 683 } 684 685 switch taskSpec.GetRuntime().(type) { 686 case *swarmapi.TaskSpec_Container, nil: 687 c := taskSpec.GetContainer() 688 if c != nil { 689 t.ContainerSpec = containerSpecFromGRPC(c) 690 } 691 case *swarmapi.TaskSpec_Generic: 692 g := taskSpec.GetGeneric() 693 if g != nil { 694 switch g.Kind { 695 case string(types.RuntimePlugin): 696 var p runtime.PluginSpec 697 if err := proto.Unmarshal(g.Payload.Value, &p); err != nil { 698 return t, errors.Wrap(err, "error unmarshalling plugin spec") 699 } 700 t.PluginSpec = &p 701 } 702 } 703 case *swarmapi.TaskSpec_Attachment: 704 a := taskSpec.GetAttachment() 705 if a != nil { 706 t.NetworkAttachmentSpec = networkAttachmentSpecFromGRPC(*a) 707 } 708 t.Runtime = types.RuntimeNetworkAttachment 709 } 710 711 return t, nil 712 }