container.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. package convert // import "github.com/docker/docker/daemon/cluster/convert"
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/docker/docker/api/types/container"
  6. mounttypes "github.com/docker/docker/api/types/mount"
  7. types "github.com/docker/docker/api/types/swarm"
  8. "github.com/docker/go-units"
  9. swarmapi "github.com/docker/swarmkit/api"
  10. gogotypes "github.com/gogo/protobuf/types"
  11. "github.com/pkg/errors"
  12. "github.com/sirupsen/logrus"
  13. )
  14. func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
  15. if c == nil {
  16. return nil
  17. }
  18. containerSpec := &types.ContainerSpec{
  19. Image: c.Image,
  20. Labels: c.Labels,
  21. Command: c.Command,
  22. Args: c.Args,
  23. Hostname: c.Hostname,
  24. Env: c.Env,
  25. Dir: c.Dir,
  26. User: c.User,
  27. Groups: c.Groups,
  28. StopSignal: c.StopSignal,
  29. TTY: c.TTY,
  30. OpenStdin: c.OpenStdin,
  31. ReadOnly: c.ReadOnly,
  32. Hosts: c.Hosts,
  33. Secrets: secretReferencesFromGRPC(c.Secrets),
  34. Configs: configReferencesFromGRPC(c.Configs),
  35. Isolation: IsolationFromGRPC(c.Isolation),
  36. Init: initFromGRPC(c.Init),
  37. Sysctls: c.Sysctls,
  38. CapabilityAdd: c.CapabilityAdd,
  39. CapabilityDrop: c.CapabilityDrop,
  40. Ulimits: ulimitsFromGRPC(c.Ulimits),
  41. }
  42. if c.DNSConfig != nil {
  43. containerSpec.DNSConfig = &types.DNSConfig{
  44. Nameservers: c.DNSConfig.Nameservers,
  45. Search: c.DNSConfig.Search,
  46. Options: c.DNSConfig.Options,
  47. }
  48. }
  49. // Privileges
  50. if c.Privileges != nil {
  51. containerSpec.Privileges = &types.Privileges{}
  52. if c.Privileges.CredentialSpec != nil {
  53. containerSpec.Privileges.CredentialSpec = credentialSpecFromGRPC(c.Privileges.CredentialSpec)
  54. }
  55. if c.Privileges.SELinuxContext != nil {
  56. containerSpec.Privileges.SELinuxContext = &types.SELinuxContext{
  57. Disable: c.Privileges.SELinuxContext.Disable,
  58. User: c.Privileges.SELinuxContext.User,
  59. Type: c.Privileges.SELinuxContext.Type,
  60. Role: c.Privileges.SELinuxContext.Role,
  61. Level: c.Privileges.SELinuxContext.Level,
  62. }
  63. }
  64. }
  65. // Mounts
  66. for _, m := range c.Mounts {
  67. mount := mounttypes.Mount{
  68. Target: m.Target,
  69. Source: m.Source,
  70. Type: mounttypes.Type(strings.ToLower(swarmapi.Mount_MountType_name[int32(m.Type)])),
  71. ReadOnly: m.ReadOnly,
  72. }
  73. if m.BindOptions != nil {
  74. mount.BindOptions = &mounttypes.BindOptions{
  75. Propagation: mounttypes.Propagation(strings.ToLower(swarmapi.Mount_BindOptions_MountPropagation_name[int32(m.BindOptions.Propagation)])),
  76. NonRecursive: m.BindOptions.NonRecursive,
  77. }
  78. }
  79. if m.VolumeOptions != nil {
  80. mount.VolumeOptions = &mounttypes.VolumeOptions{
  81. NoCopy: m.VolumeOptions.NoCopy,
  82. Labels: m.VolumeOptions.Labels,
  83. }
  84. if m.VolumeOptions.DriverConfig != nil {
  85. mount.VolumeOptions.DriverConfig = &mounttypes.Driver{
  86. Name: m.VolumeOptions.DriverConfig.Name,
  87. Options: m.VolumeOptions.DriverConfig.Options,
  88. }
  89. }
  90. }
  91. if m.TmpfsOptions != nil {
  92. mount.TmpfsOptions = &mounttypes.TmpfsOptions{
  93. SizeBytes: m.TmpfsOptions.SizeBytes,
  94. Mode: m.TmpfsOptions.Mode,
  95. }
  96. }
  97. containerSpec.Mounts = append(containerSpec.Mounts, mount)
  98. }
  99. if c.StopGracePeriod != nil {
  100. grace, _ := gogotypes.DurationFromProto(c.StopGracePeriod)
  101. containerSpec.StopGracePeriod = &grace
  102. }
  103. if c.Healthcheck != nil {
  104. containerSpec.Healthcheck = healthConfigFromGRPC(c.Healthcheck)
  105. }
  106. return containerSpec
  107. }
  108. func initFromGRPC(v *gogotypes.BoolValue) *bool {
  109. if v == nil {
  110. return nil
  111. }
  112. value := v.GetValue()
  113. return &value
  114. }
  115. func initToGRPC(v *bool) *gogotypes.BoolValue {
  116. if v == nil {
  117. return nil
  118. }
  119. return &gogotypes.BoolValue{Value: *v}
  120. }
  121. func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference {
  122. refs := make([]*swarmapi.SecretReference, 0, len(sr))
  123. for _, s := range sr {
  124. ref := &swarmapi.SecretReference{
  125. SecretID: s.SecretID,
  126. SecretName: s.SecretName,
  127. }
  128. if s.File != nil {
  129. ref.Target = &swarmapi.SecretReference_File{
  130. File: &swarmapi.FileTarget{
  131. Name: s.File.Name,
  132. UID: s.File.UID,
  133. GID: s.File.GID,
  134. Mode: s.File.Mode,
  135. },
  136. }
  137. }
  138. refs = append(refs, ref)
  139. }
  140. return refs
  141. }
  142. func secretReferencesFromGRPC(sr []*swarmapi.SecretReference) []*types.SecretReference {
  143. refs := make([]*types.SecretReference, 0, len(sr))
  144. for _, s := range sr {
  145. target := s.GetFile()
  146. if target == nil {
  147. // not a file target
  148. logrus.Warnf("secret target not a file: secret=%s", s.SecretID)
  149. continue
  150. }
  151. refs = append(refs, &types.SecretReference{
  152. File: &types.SecretReferenceFileTarget{
  153. Name: target.Name,
  154. UID: target.UID,
  155. GID: target.GID,
  156. Mode: target.Mode,
  157. },
  158. SecretID: s.SecretID,
  159. SecretName: s.SecretName,
  160. })
  161. }
  162. return refs
  163. }
  164. func configReferencesToGRPC(sr []*types.ConfigReference) ([]*swarmapi.ConfigReference, error) {
  165. refs := make([]*swarmapi.ConfigReference, 0, len(sr))
  166. for _, s := range sr {
  167. ref := &swarmapi.ConfigReference{
  168. ConfigID: s.ConfigID,
  169. ConfigName: s.ConfigName,
  170. }
  171. switch {
  172. case s.Runtime == nil && s.File == nil:
  173. return nil, errors.New("either File or Runtime should be set")
  174. case s.Runtime != nil && s.File != nil:
  175. return nil, errors.New("cannot specify both File and Runtime")
  176. case s.Runtime != nil:
  177. // Runtime target was added in API v1.40 and takes precedence over
  178. // File target. However, File and Runtime targets are mutually exclusive,
  179. // so we should never have both.
  180. ref.Target = &swarmapi.ConfigReference_Runtime{
  181. Runtime: &swarmapi.RuntimeTarget{},
  182. }
  183. case s.File != nil:
  184. ref.Target = &swarmapi.ConfigReference_File{
  185. File: &swarmapi.FileTarget{
  186. Name: s.File.Name,
  187. UID: s.File.UID,
  188. GID: s.File.GID,
  189. Mode: s.File.Mode,
  190. },
  191. }
  192. }
  193. refs = append(refs, ref)
  194. }
  195. return refs, nil
  196. }
  197. func configReferencesFromGRPC(sr []*swarmapi.ConfigReference) []*types.ConfigReference {
  198. refs := make([]*types.ConfigReference, 0, len(sr))
  199. for _, s := range sr {
  200. r := &types.ConfigReference{
  201. ConfigID: s.ConfigID,
  202. ConfigName: s.ConfigName,
  203. }
  204. if target := s.GetRuntime(); target != nil {
  205. r.Runtime = &types.ConfigReferenceRuntimeTarget{}
  206. } else if target := s.GetFile(); target != nil {
  207. r.File = &types.ConfigReferenceFileTarget{
  208. Name: target.Name,
  209. UID: target.UID,
  210. GID: target.GID,
  211. Mode: target.Mode,
  212. }
  213. } else {
  214. // not a file target
  215. logrus.Warnf("config target not known: config=%s", s.ConfigID)
  216. continue
  217. }
  218. refs = append(refs, r)
  219. }
  220. return refs
  221. }
  222. func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
  223. containerSpec := &swarmapi.ContainerSpec{
  224. Image: c.Image,
  225. Labels: c.Labels,
  226. Command: c.Command,
  227. Args: c.Args,
  228. Hostname: c.Hostname,
  229. Env: c.Env,
  230. Dir: c.Dir,
  231. User: c.User,
  232. Groups: c.Groups,
  233. StopSignal: c.StopSignal,
  234. TTY: c.TTY,
  235. OpenStdin: c.OpenStdin,
  236. ReadOnly: c.ReadOnly,
  237. Hosts: c.Hosts,
  238. Secrets: secretReferencesToGRPC(c.Secrets),
  239. Isolation: isolationToGRPC(c.Isolation),
  240. Init: initToGRPC(c.Init),
  241. Sysctls: c.Sysctls,
  242. CapabilityAdd: c.CapabilityAdd,
  243. CapabilityDrop: c.CapabilityDrop,
  244. Ulimits: ulimitsToGRPC(c.Ulimits),
  245. }
  246. if c.DNSConfig != nil {
  247. containerSpec.DNSConfig = &swarmapi.ContainerSpec_DNSConfig{
  248. Nameservers: c.DNSConfig.Nameservers,
  249. Search: c.DNSConfig.Search,
  250. Options: c.DNSConfig.Options,
  251. }
  252. }
  253. if c.StopGracePeriod != nil {
  254. containerSpec.StopGracePeriod = gogotypes.DurationProto(*c.StopGracePeriod)
  255. }
  256. // Privileges
  257. if c.Privileges != nil {
  258. containerSpec.Privileges = &swarmapi.Privileges{}
  259. if c.Privileges.CredentialSpec != nil {
  260. cs, err := credentialSpecToGRPC(c.Privileges.CredentialSpec)
  261. if err != nil {
  262. return nil, errors.Wrap(err, "invalid CredentialSpec")
  263. }
  264. containerSpec.Privileges.CredentialSpec = cs
  265. }
  266. if c.Privileges.SELinuxContext != nil {
  267. containerSpec.Privileges.SELinuxContext = &swarmapi.Privileges_SELinuxContext{
  268. Disable: c.Privileges.SELinuxContext.Disable,
  269. User: c.Privileges.SELinuxContext.User,
  270. Type: c.Privileges.SELinuxContext.Type,
  271. Role: c.Privileges.SELinuxContext.Role,
  272. Level: c.Privileges.SELinuxContext.Level,
  273. }
  274. }
  275. }
  276. if c.Configs != nil {
  277. configs, err := configReferencesToGRPC(c.Configs)
  278. if err != nil {
  279. return nil, errors.Wrap(err, "invalid Config")
  280. }
  281. containerSpec.Configs = configs
  282. }
  283. // Mounts
  284. for _, m := range c.Mounts {
  285. mount := swarmapi.Mount{
  286. Target: m.Target,
  287. Source: m.Source,
  288. ReadOnly: m.ReadOnly,
  289. }
  290. if mountType, ok := swarmapi.Mount_MountType_value[strings.ToUpper(string(m.Type))]; ok {
  291. mount.Type = swarmapi.Mount_MountType(mountType)
  292. } else if string(m.Type) != "" {
  293. return nil, fmt.Errorf("invalid MountType: %q", m.Type)
  294. }
  295. if m.BindOptions != nil {
  296. if mountPropagation, ok := swarmapi.Mount_BindOptions_MountPropagation_value[strings.ToUpper(string(m.BindOptions.Propagation))]; ok {
  297. mount.BindOptions = &swarmapi.Mount_BindOptions{Propagation: swarmapi.Mount_BindOptions_MountPropagation(mountPropagation)}
  298. } else if string(m.BindOptions.Propagation) != "" {
  299. return nil, fmt.Errorf("invalid MountPropagation: %q", m.BindOptions.Propagation)
  300. }
  301. if m.BindOptions.NonRecursive {
  302. if mount.BindOptions == nil {
  303. // the propagation defaults to rprivate
  304. mount.BindOptions = &swarmapi.Mount_BindOptions{}
  305. }
  306. mount.BindOptions.NonRecursive = m.BindOptions.NonRecursive
  307. }
  308. }
  309. if m.VolumeOptions != nil {
  310. mount.VolumeOptions = &swarmapi.Mount_VolumeOptions{
  311. NoCopy: m.VolumeOptions.NoCopy,
  312. Labels: m.VolumeOptions.Labels,
  313. }
  314. if m.VolumeOptions.DriverConfig != nil {
  315. mount.VolumeOptions.DriverConfig = &swarmapi.Driver{
  316. Name: m.VolumeOptions.DriverConfig.Name,
  317. Options: m.VolumeOptions.DriverConfig.Options,
  318. }
  319. }
  320. }
  321. if m.TmpfsOptions != nil {
  322. mount.TmpfsOptions = &swarmapi.Mount_TmpfsOptions{
  323. SizeBytes: m.TmpfsOptions.SizeBytes,
  324. Mode: m.TmpfsOptions.Mode,
  325. }
  326. }
  327. containerSpec.Mounts = append(containerSpec.Mounts, mount)
  328. }
  329. if c.Healthcheck != nil {
  330. containerSpec.Healthcheck = healthConfigToGRPC(c.Healthcheck)
  331. }
  332. return containerSpec, nil
  333. }
  334. func credentialSpecFromGRPC(c *swarmapi.Privileges_CredentialSpec) *types.CredentialSpec {
  335. cs := &types.CredentialSpec{}
  336. switch c.Source.(type) {
  337. case *swarmapi.Privileges_CredentialSpec_Config:
  338. cs.Config = c.GetConfig()
  339. case *swarmapi.Privileges_CredentialSpec_File:
  340. cs.File = c.GetFile()
  341. case *swarmapi.Privileges_CredentialSpec_Registry:
  342. cs.Registry = c.GetRegistry()
  343. }
  344. return cs
  345. }
  346. func credentialSpecToGRPC(c *types.CredentialSpec) (*swarmapi.Privileges_CredentialSpec, error) {
  347. var opts []string
  348. if c.Config != "" {
  349. opts = append(opts, `"config"`)
  350. }
  351. if c.File != "" {
  352. opts = append(opts, `"file"`)
  353. }
  354. if c.Registry != "" {
  355. opts = append(opts, `"registry"`)
  356. }
  357. l := len(opts)
  358. switch {
  359. case l == 0:
  360. return nil, errors.New(`must either provide "file", "registry", or "config" for credential spec`)
  361. case l == 2:
  362. return nil, fmt.Errorf("cannot specify both %s and %s credential specs", opts[0], opts[1])
  363. case l > 2:
  364. return nil, fmt.Errorf("cannot specify both %s, and %s credential specs", strings.Join(opts[:l-1], ", "), opts[l-1])
  365. }
  366. spec := &swarmapi.Privileges_CredentialSpec{}
  367. switch {
  368. case c.Config != "":
  369. spec.Source = &swarmapi.Privileges_CredentialSpec_Config{
  370. Config: c.Config,
  371. }
  372. case c.File != "":
  373. spec.Source = &swarmapi.Privileges_CredentialSpec_File{
  374. File: c.File,
  375. }
  376. case c.Registry != "":
  377. spec.Source = &swarmapi.Privileges_CredentialSpec_Registry{
  378. Registry: c.Registry,
  379. }
  380. }
  381. return spec, nil
  382. }
  383. func healthConfigFromGRPC(h *swarmapi.HealthConfig) *container.HealthConfig {
  384. interval, _ := gogotypes.DurationFromProto(h.Interval)
  385. timeout, _ := gogotypes.DurationFromProto(h.Timeout)
  386. startPeriod, _ := gogotypes.DurationFromProto(h.StartPeriod)
  387. return &container.HealthConfig{
  388. Test: h.Test,
  389. Interval: interval,
  390. Timeout: timeout,
  391. Retries: int(h.Retries),
  392. StartPeriod: startPeriod,
  393. }
  394. }
  395. func healthConfigToGRPC(h *container.HealthConfig) *swarmapi.HealthConfig {
  396. return &swarmapi.HealthConfig{
  397. Test: h.Test,
  398. Interval: gogotypes.DurationProto(h.Interval),
  399. Timeout: gogotypes.DurationProto(h.Timeout),
  400. Retries: int32(h.Retries),
  401. StartPeriod: gogotypes.DurationProto(h.StartPeriod),
  402. }
  403. }
  404. // IsolationFromGRPC converts a swarm api container isolation to a moby isolation representation
  405. func IsolationFromGRPC(i swarmapi.ContainerSpec_Isolation) container.Isolation {
  406. switch i {
  407. case swarmapi.ContainerIsolationHyperV:
  408. return container.IsolationHyperV
  409. case swarmapi.ContainerIsolationProcess:
  410. return container.IsolationProcess
  411. case swarmapi.ContainerIsolationDefault:
  412. return container.IsolationDefault
  413. }
  414. return container.IsolationEmpty
  415. }
  416. func isolationToGRPC(i container.Isolation) swarmapi.ContainerSpec_Isolation {
  417. if i.IsHyperV() {
  418. return swarmapi.ContainerIsolationHyperV
  419. }
  420. if i.IsProcess() {
  421. return swarmapi.ContainerIsolationProcess
  422. }
  423. return swarmapi.ContainerIsolationDefault
  424. }
  425. func ulimitsFromGRPC(u []*swarmapi.ContainerSpec_Ulimit) []*units.Ulimit {
  426. ulimits := make([]*units.Ulimit, len(u))
  427. for i, ulimit := range u {
  428. ulimits[i] = &units.Ulimit{
  429. Name: ulimit.Name,
  430. Soft: ulimit.Soft,
  431. Hard: ulimit.Hard,
  432. }
  433. }
  434. return ulimits
  435. }
  436. func ulimitsToGRPC(u []*units.Ulimit) []*swarmapi.ContainerSpec_Ulimit {
  437. ulimits := make([]*swarmapi.ContainerSpec_Ulimit, len(u))
  438. for i, ulimit := range u {
  439. ulimits[i] = &swarmapi.ContainerSpec_Ulimit{
  440. Name: ulimit.Name,
  441. Soft: ulimit.Soft,
  442. Hard: ulimit.Hard,
  443. }
  444. }
  445. return ulimits
  446. }