github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/daemon/cluster/convert/service.go (about) 1 package 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 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 ) 21 22 // ServiceFromGRPC converts a grpc Service to a Service. 23 func ServiceFromGRPC(s swarmapi.Service) (types.Service, error) { 24 curSpec, err := serviceSpecFromGRPC(&s.Spec) 25 if err != nil { 26 return types.Service{}, err 27 } 28 prevSpec, err := serviceSpecFromGRPC(s.PreviousSpec) 29 if err != nil { 30 return types.Service{}, err 31 } 32 service := types.Service{ 33 ID: s.ID, 34 Spec: *curSpec, 35 PreviousSpec: prevSpec, 36 37 Endpoint: endpointFromGRPC(s.Endpoint), 38 } 39 40 // Meta 41 service.Version.Index = s.Meta.Version.Index 42 service.CreatedAt, _ = gogotypes.TimestampFromProto(s.Meta.CreatedAt) 43 service.UpdatedAt, _ = gogotypes.TimestampFromProto(s.Meta.UpdatedAt) 44 45 // UpdateStatus 46 if s.UpdateStatus != nil { 47 service.UpdateStatus = &types.UpdateStatus{} 48 switch s.UpdateStatus.State { 49 case swarmapi.UpdateStatus_UPDATING: 50 service.UpdateStatus.State = types.UpdateStateUpdating 51 case swarmapi.UpdateStatus_PAUSED: 52 service.UpdateStatus.State = types.UpdateStatePaused 53 case swarmapi.UpdateStatus_COMPLETED: 54 service.UpdateStatus.State = types.UpdateStateCompleted 55 case swarmapi.UpdateStatus_ROLLBACK_STARTED: 56 service.UpdateStatus.State = types.UpdateStateRollbackStarted 57 case swarmapi.UpdateStatus_ROLLBACK_PAUSED: 58 service.UpdateStatus.State = types.UpdateStateRollbackPaused 59 case swarmapi.UpdateStatus_ROLLBACK_COMPLETED: 60 service.UpdateStatus.State = types.UpdateStateRollbackCompleted 61 } 62 63 startedAt, _ := gogotypes.TimestampFromProto(s.UpdateStatus.StartedAt) 64 if !startedAt.IsZero() && startedAt.Unix() != 0 { 65 service.UpdateStatus.StartedAt = &startedAt 66 } 67 68 completedAt, _ := gogotypes.TimestampFromProto(s.UpdateStatus.CompletedAt) 69 if !completedAt.IsZero() && completedAt.Unix() != 0 { 70 service.UpdateStatus.CompletedAt = &completedAt 71 } 72 73 service.UpdateStatus.Message = s.UpdateStatus.Message 74 } 75 76 return service, nil 77 } 78 79 func serviceSpecFromGRPC(spec *swarmapi.ServiceSpec) (*types.ServiceSpec, error) { 80 if spec == nil { 81 return nil, nil 82 } 83 84 serviceNetworks := make([]types.NetworkAttachmentConfig, 0, len(spec.Networks)) 85 for _, n := range spec.Networks { 86 netConfig := types.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverOpts: n.DriverAttachmentOpts} 87 serviceNetworks = append(serviceNetworks, netConfig) 88 89 } 90 91 taskTemplate, err := taskSpecFromGRPC(spec.Task) 92 if err != nil { 93 return nil, err 94 } 95 96 switch t := spec.Task.GetRuntime().(type) { 97 case *swarmapi.TaskSpec_Container: 98 containerConfig := t.Container 99 taskTemplate.ContainerSpec = containerSpecFromGRPC(containerConfig) 100 taskTemplate.Runtime = types.RuntimeContainer 101 case *swarmapi.TaskSpec_Generic: 102 switch t.Generic.Kind { 103 case string(types.RuntimePlugin): 104 taskTemplate.Runtime = types.RuntimePlugin 105 default: 106 return nil, fmt.Errorf("unknown task runtime type: %s", t.Generic.Payload.TypeUrl) 107 } 108 109 default: 110 return nil, fmt.Errorf("error creating service; unsupported runtime %T", t) 111 } 112 113 convertedSpec := &types.ServiceSpec{ 114 Annotations: annotationsFromGRPC(spec.Annotations), 115 TaskTemplate: taskTemplate, 116 Networks: serviceNetworks, 117 EndpointSpec: endpointSpecFromGRPC(spec.Endpoint), 118 } 119 120 // UpdateConfig 121 convertedSpec.UpdateConfig = updateConfigFromGRPC(spec.Update) 122 convertedSpec.RollbackConfig = updateConfigFromGRPC(spec.Rollback) 123 124 // Mode 125 switch t := spec.GetMode().(type) { 126 case *swarmapi.ServiceSpec_Global: 127 convertedSpec.Mode.Global = &types.GlobalService{} 128 case *swarmapi.ServiceSpec_Replicated: 129 convertedSpec.Mode.Replicated = &types.ReplicatedService{ 130 Replicas: &t.Replicated.Replicas, 131 } 132 } 133 134 return convertedSpec, nil 135 } 136 137 // ServiceSpecToGRPC converts a ServiceSpec to a grpc ServiceSpec. 138 func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) { 139 name := s.Name 140 if name == "" { 141 name = namesgenerator.GetRandomName(0) 142 } 143 144 serviceNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.Networks)) 145 for _, n := range s.Networks { 146 netConfig := &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverAttachmentOpts: n.DriverOpts} 147 serviceNetworks = append(serviceNetworks, netConfig) 148 } 149 150 taskNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.TaskTemplate.Networks)) 151 for _, n := range s.TaskTemplate.Networks { 152 netConfig := &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverAttachmentOpts: n.DriverOpts} 153 taskNetworks = append(taskNetworks, netConfig) 154 155 } 156 157 spec := swarmapi.ServiceSpec{ 158 Annotations: swarmapi.Annotations{ 159 Name: name, 160 Labels: s.Labels, 161 }, 162 Task: swarmapi.TaskSpec{ 163 Resources: resourcesToGRPC(s.TaskTemplate.Resources), 164 LogDriver: driverToGRPC(s.TaskTemplate.LogDriver), 165 Networks: taskNetworks, 166 ForceUpdate: s.TaskTemplate.ForceUpdate, 167 }, 168 Networks: serviceNetworks, 169 } 170 171 switch s.TaskTemplate.Runtime { 172 case types.RuntimeContainer, "": // if empty runtime default to container 173 if s.TaskTemplate.ContainerSpec != nil { 174 containerSpec, err := containerToGRPC(s.TaskTemplate.ContainerSpec) 175 if err != nil { 176 return swarmapi.ServiceSpec{}, err 177 } 178 spec.Task.Runtime = &swarmapi.TaskSpec_Container{Container: containerSpec} 179 } 180 case types.RuntimePlugin: 181 if s.Mode.Replicated != nil { 182 return swarmapi.ServiceSpec{}, errors.New("plugins must not use replicated mode") 183 } 184 185 s.Mode.Global = &types.GlobalService{} // must always be global 186 187 if s.TaskTemplate.PluginSpec != nil { 188 pluginSpec, err := proto.Marshal(s.TaskTemplate.PluginSpec) 189 if err != nil { 190 return swarmapi.ServiceSpec{}, err 191 } 192 spec.Task.Runtime = &swarmapi.TaskSpec_Generic{ 193 Generic: &swarmapi.GenericRuntimeSpec{ 194 Kind: string(types.RuntimePlugin), 195 Payload: &gogotypes.Any{ 196 TypeUrl: string(types.RuntimeURLPlugin), 197 Value: pluginSpec, 198 }, 199 }, 200 } 201 } 202 default: 203 return swarmapi.ServiceSpec{}, ErrUnsupportedRuntime 204 } 205 206 restartPolicy, err := restartPolicyToGRPC(s.TaskTemplate.RestartPolicy) 207 if err != nil { 208 return swarmapi.ServiceSpec{}, err 209 } 210 spec.Task.Restart = restartPolicy 211 212 if s.TaskTemplate.Placement != nil { 213 var preferences []*swarmapi.PlacementPreference 214 for _, pref := range s.TaskTemplate.Placement.Preferences { 215 if pref.Spread != nil { 216 preferences = append(preferences, &swarmapi.PlacementPreference{ 217 Preference: &swarmapi.PlacementPreference_Spread{ 218 Spread: &swarmapi.SpreadOver{ 219 SpreadDescriptor: pref.Spread.SpreadDescriptor, 220 }, 221 }, 222 }) 223 } 224 } 225 var platforms []*swarmapi.Platform 226 for _, plat := range s.TaskTemplate.Placement.Platforms { 227 platforms = append(platforms, &swarmapi.Platform{ 228 Architecture: plat.Architecture, 229 OS: plat.OS, 230 }) 231 } 232 spec.Task.Placement = &swarmapi.Placement{ 233 Constraints: s.TaskTemplate.Placement.Constraints, 234 Preferences: preferences, 235 Platforms: platforms, 236 } 237 } 238 239 spec.Update, err = updateConfigToGRPC(s.UpdateConfig) 240 if err != nil { 241 return swarmapi.ServiceSpec{}, err 242 } 243 spec.Rollback, err = updateConfigToGRPC(s.RollbackConfig) 244 if err != nil { 245 return swarmapi.ServiceSpec{}, err 246 } 247 248 if s.EndpointSpec != nil { 249 if s.EndpointSpec.Mode != "" && 250 s.EndpointSpec.Mode != types.ResolutionModeVIP && 251 s.EndpointSpec.Mode != types.ResolutionModeDNSRR { 252 return swarmapi.ServiceSpec{}, fmt.Errorf("invalid resolution mode: %q", s.EndpointSpec.Mode) 253 } 254 255 spec.Endpoint = &swarmapi.EndpointSpec{} 256 257 spec.Endpoint.Mode = swarmapi.EndpointSpec_ResolutionMode(swarmapi.EndpointSpec_ResolutionMode_value[strings.ToUpper(string(s.EndpointSpec.Mode))]) 258 259 for _, portConfig := range s.EndpointSpec.Ports { 260 spec.Endpoint.Ports = append(spec.Endpoint.Ports, &swarmapi.PortConfig{ 261 Name: portConfig.Name, 262 Protocol: swarmapi.PortConfig_Protocol(swarmapi.PortConfig_Protocol_value[strings.ToUpper(string(portConfig.Protocol))]), 263 PublishMode: swarmapi.PortConfig_PublishMode(swarmapi.PortConfig_PublishMode_value[strings.ToUpper(string(portConfig.PublishMode))]), 264 TargetPort: portConfig.TargetPort, 265 PublishedPort: portConfig.PublishedPort, 266 }) 267 } 268 } 269 270 // Mode 271 if s.Mode.Global != nil && s.Mode.Replicated != nil { 272 return swarmapi.ServiceSpec{}, fmt.Errorf("cannot specify both replicated mode and global mode") 273 } 274 275 if s.Mode.Global != nil { 276 spec.Mode = &swarmapi.ServiceSpec_Global{ 277 Global: &swarmapi.GlobalService{}, 278 } 279 } else if s.Mode.Replicated != nil && s.Mode.Replicated.Replicas != nil { 280 spec.Mode = &swarmapi.ServiceSpec_Replicated{ 281 Replicated: &swarmapi.ReplicatedService{Replicas: *s.Mode.Replicated.Replicas}, 282 } 283 } else { 284 spec.Mode = &swarmapi.ServiceSpec_Replicated{ 285 Replicated: &swarmapi.ReplicatedService{Replicas: 1}, 286 } 287 } 288 289 return spec, nil 290 } 291 292 func annotationsFromGRPC(ann swarmapi.Annotations) types.Annotations { 293 a := types.Annotations{ 294 Name: ann.Name, 295 Labels: ann.Labels, 296 } 297 298 if a.Labels == nil { 299 a.Labels = make(map[string]string) 300 } 301 302 return a 303 } 304 305 // GenericResourcesFromGRPC converts a GRPC GenericResource to a GenericResource 306 func GenericResourcesFromGRPC(genericRes []*swarmapi.GenericResource) []types.GenericResource { 307 var generic []types.GenericResource 308 for _, res := range genericRes { 309 var current types.GenericResource 310 311 switch r := res.Resource.(type) { 312 case *swarmapi.GenericResource_DiscreteResourceSpec: 313 current.DiscreteResourceSpec = &types.DiscreteGenericResource{ 314 Kind: r.DiscreteResourceSpec.Kind, 315 Value: r.DiscreteResourceSpec.Value, 316 } 317 case *swarmapi.GenericResource_NamedResourceSpec: 318 current.NamedResourceSpec = &types.NamedGenericResource{ 319 Kind: r.NamedResourceSpec.Kind, 320 Value: r.NamedResourceSpec.Value, 321 } 322 } 323 324 generic = append(generic, current) 325 } 326 327 return generic 328 } 329 330 func resourcesFromGRPC(res *swarmapi.ResourceRequirements) *types.ResourceRequirements { 331 var resources *types.ResourceRequirements 332 if res != nil { 333 resources = &types.ResourceRequirements{} 334 if res.Limits != nil { 335 resources.Limits = &types.Resources{ 336 NanoCPUs: res.Limits.NanoCPUs, 337 MemoryBytes: res.Limits.MemoryBytes, 338 } 339 } 340 if res.Reservations != nil { 341 resources.Reservations = &types.Resources{ 342 NanoCPUs: res.Reservations.NanoCPUs, 343 MemoryBytes: res.Reservations.MemoryBytes, 344 GenericResources: GenericResourcesFromGRPC(res.Reservations.Generic), 345 } 346 } 347 } 348 349 return resources 350 } 351 352 // GenericResourcesToGRPC converts a GenericResource to a GRPC GenericResource 353 func GenericResourcesToGRPC(genericRes []types.GenericResource) []*swarmapi.GenericResource { 354 var generic []*swarmapi.GenericResource 355 for _, res := range genericRes { 356 var r *swarmapi.GenericResource 357 358 if res.DiscreteResourceSpec != nil { 359 r = genericresource.NewDiscrete(res.DiscreteResourceSpec.Kind, res.DiscreteResourceSpec.Value) 360 } else if res.NamedResourceSpec != nil { 361 r = genericresource.NewString(res.NamedResourceSpec.Kind, res.NamedResourceSpec.Value) 362 } 363 364 generic = append(generic, r) 365 } 366 367 return generic 368 } 369 370 func resourcesToGRPC(res *types.ResourceRequirements) *swarmapi.ResourceRequirements { 371 var reqs *swarmapi.ResourceRequirements 372 if res != nil { 373 reqs = &swarmapi.ResourceRequirements{} 374 if res.Limits != nil { 375 reqs.Limits = &swarmapi.Resources{ 376 NanoCPUs: res.Limits.NanoCPUs, 377 MemoryBytes: res.Limits.MemoryBytes, 378 } 379 } 380 if res.Reservations != nil { 381 reqs.Reservations = &swarmapi.Resources{ 382 NanoCPUs: res.Reservations.NanoCPUs, 383 MemoryBytes: res.Reservations.MemoryBytes, 384 Generic: GenericResourcesToGRPC(res.Reservations.GenericResources), 385 } 386 387 } 388 } 389 return reqs 390 } 391 392 func restartPolicyFromGRPC(p *swarmapi.RestartPolicy) *types.RestartPolicy { 393 var rp *types.RestartPolicy 394 if p != nil { 395 rp = &types.RestartPolicy{} 396 397 switch p.Condition { 398 case swarmapi.RestartOnNone: 399 rp.Condition = types.RestartPolicyConditionNone 400 case swarmapi.RestartOnFailure: 401 rp.Condition = types.RestartPolicyConditionOnFailure 402 case swarmapi.RestartOnAny: 403 rp.Condition = types.RestartPolicyConditionAny 404 default: 405 rp.Condition = types.RestartPolicyConditionAny 406 } 407 408 if p.Delay != nil { 409 delay, _ := gogotypes.DurationFromProto(p.Delay) 410 rp.Delay = &delay 411 } 412 if p.Window != nil { 413 window, _ := gogotypes.DurationFromProto(p.Window) 414 rp.Window = &window 415 } 416 417 rp.MaxAttempts = &p.MaxAttempts 418 } 419 return rp 420 } 421 422 func restartPolicyToGRPC(p *types.RestartPolicy) (*swarmapi.RestartPolicy, error) { 423 var rp *swarmapi.RestartPolicy 424 if p != nil { 425 rp = &swarmapi.RestartPolicy{} 426 427 switch p.Condition { 428 case types.RestartPolicyConditionNone: 429 rp.Condition = swarmapi.RestartOnNone 430 case types.RestartPolicyConditionOnFailure: 431 rp.Condition = swarmapi.RestartOnFailure 432 case types.RestartPolicyConditionAny: 433 rp.Condition = swarmapi.RestartOnAny 434 default: 435 if string(p.Condition) != "" { 436 return nil, fmt.Errorf("invalid RestartCondition: %q", p.Condition) 437 } 438 rp.Condition = swarmapi.RestartOnAny 439 } 440 441 if p.Delay != nil { 442 rp.Delay = gogotypes.DurationProto(*p.Delay) 443 } 444 if p.Window != nil { 445 rp.Window = gogotypes.DurationProto(*p.Window) 446 } 447 if p.MaxAttempts != nil { 448 rp.MaxAttempts = *p.MaxAttempts 449 450 } 451 } 452 return rp, nil 453 } 454 455 func placementFromGRPC(p *swarmapi.Placement) *types.Placement { 456 if p == nil { 457 return nil 458 } 459 r := &types.Placement{ 460 Constraints: p.Constraints, 461 } 462 463 for _, pref := range p.Preferences { 464 if spread := pref.GetSpread(); spread != nil { 465 r.Preferences = append(r.Preferences, types.PlacementPreference{ 466 Spread: &types.SpreadOver{ 467 SpreadDescriptor: spread.SpreadDescriptor, 468 }, 469 }) 470 } 471 } 472 473 for _, plat := range p.Platforms { 474 r.Platforms = append(r.Platforms, types.Platform{ 475 Architecture: plat.Architecture, 476 OS: plat.OS, 477 }) 478 } 479 480 return r 481 } 482 483 func driverFromGRPC(p *swarmapi.Driver) *types.Driver { 484 if p == nil { 485 return nil 486 } 487 488 return &types.Driver{ 489 Name: p.Name, 490 Options: p.Options, 491 } 492 } 493 494 func driverToGRPC(p *types.Driver) *swarmapi.Driver { 495 if p == nil { 496 return nil 497 } 498 499 return &swarmapi.Driver{ 500 Name: p.Name, 501 Options: p.Options, 502 } 503 } 504 505 func updateConfigFromGRPC(updateConfig *swarmapi.UpdateConfig) *types.UpdateConfig { 506 if updateConfig == nil { 507 return nil 508 } 509 510 converted := &types.UpdateConfig{ 511 Parallelism: updateConfig.Parallelism, 512 MaxFailureRatio: updateConfig.MaxFailureRatio, 513 } 514 515 converted.Delay = updateConfig.Delay 516 if updateConfig.Monitor != nil { 517 converted.Monitor, _ = gogotypes.DurationFromProto(updateConfig.Monitor) 518 } 519 520 switch updateConfig.FailureAction { 521 case swarmapi.UpdateConfig_PAUSE: 522 converted.FailureAction = types.UpdateFailureActionPause 523 case swarmapi.UpdateConfig_CONTINUE: 524 converted.FailureAction = types.UpdateFailureActionContinue 525 case swarmapi.UpdateConfig_ROLLBACK: 526 converted.FailureAction = types.UpdateFailureActionRollback 527 } 528 529 switch updateConfig.Order { 530 case swarmapi.UpdateConfig_STOP_FIRST: 531 converted.Order = types.UpdateOrderStopFirst 532 case swarmapi.UpdateConfig_START_FIRST: 533 converted.Order = types.UpdateOrderStartFirst 534 } 535 536 return converted 537 } 538 539 func updateConfigToGRPC(updateConfig *types.UpdateConfig) (*swarmapi.UpdateConfig, error) { 540 if updateConfig == nil { 541 return nil, nil 542 } 543 544 converted := &swarmapi.UpdateConfig{ 545 Parallelism: updateConfig.Parallelism, 546 Delay: updateConfig.Delay, 547 MaxFailureRatio: updateConfig.MaxFailureRatio, 548 } 549 550 switch updateConfig.FailureAction { 551 case types.UpdateFailureActionPause, "": 552 converted.FailureAction = swarmapi.UpdateConfig_PAUSE 553 case types.UpdateFailureActionContinue: 554 converted.FailureAction = swarmapi.UpdateConfig_CONTINUE 555 case types.UpdateFailureActionRollback: 556 converted.FailureAction = swarmapi.UpdateConfig_ROLLBACK 557 default: 558 return nil, fmt.Errorf("unrecognized update failure action %s", updateConfig.FailureAction) 559 } 560 if updateConfig.Monitor != 0 { 561 converted.Monitor = gogotypes.DurationProto(updateConfig.Monitor) 562 } 563 564 switch updateConfig.Order { 565 case types.UpdateOrderStopFirst, "": 566 converted.Order = swarmapi.UpdateConfig_STOP_FIRST 567 case types.UpdateOrderStartFirst: 568 converted.Order = swarmapi.UpdateConfig_START_FIRST 569 default: 570 return nil, fmt.Errorf("unrecognized update order %s", updateConfig.Order) 571 } 572 573 return converted, nil 574 } 575 576 func taskSpecFromGRPC(taskSpec swarmapi.TaskSpec) (types.TaskSpec, error) { 577 taskNetworks := make([]types.NetworkAttachmentConfig, 0, len(taskSpec.Networks)) 578 for _, n := range taskSpec.Networks { 579 netConfig := types.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverOpts: n.DriverAttachmentOpts} 580 taskNetworks = append(taskNetworks, netConfig) 581 } 582 583 t := types.TaskSpec{ 584 Resources: resourcesFromGRPC(taskSpec.Resources), 585 RestartPolicy: restartPolicyFromGRPC(taskSpec.Restart), 586 Placement: placementFromGRPC(taskSpec.Placement), 587 LogDriver: driverFromGRPC(taskSpec.LogDriver), 588 Networks: taskNetworks, 589 ForceUpdate: taskSpec.ForceUpdate, 590 } 591 592 switch taskSpec.GetRuntime().(type) { 593 case *swarmapi.TaskSpec_Container, nil: 594 c := taskSpec.GetContainer() 595 if c != nil { 596 t.ContainerSpec = containerSpecFromGRPC(c) 597 } 598 case *swarmapi.TaskSpec_Generic: 599 g := taskSpec.GetGeneric() 600 if g != nil { 601 switch g.Kind { 602 case string(types.RuntimePlugin): 603 var p runtime.PluginSpec 604 if err := proto.Unmarshal(g.Payload.Value, &p); err != nil { 605 return t, errors.Wrap(err, "error unmarshalling plugin spec") 606 } 607 t.PluginSpec = &p 608 } 609 } 610 } 611 612 return t, nil 613 }