service.go 21 KB

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