container.go 14 KB

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