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