service.go 15 KB

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