service.go 18 KB

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