opts.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  1. package service
  2. import (
  3. "fmt"
  4. "sort"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "github.com/docker/docker/api/types/container"
  9. "github.com/docker/docker/api/types/swarm"
  10. "github.com/docker/docker/client"
  11. "github.com/docker/docker/opts"
  12. runconfigopts "github.com/docker/docker/runconfig/opts"
  13. "github.com/docker/swarmkit/api"
  14. "github.com/docker/swarmkit/api/defaults"
  15. shlex "github.com/flynn-archive/go-shlex"
  16. gogotypes "github.com/gogo/protobuf/types"
  17. "github.com/pkg/errors"
  18. "github.com/spf13/pflag"
  19. "golang.org/x/net/context"
  20. )
  21. type int64Value interface {
  22. Value() int64
  23. }
  24. // PositiveDurationOpt is an option type for time.Duration that uses a pointer.
  25. // It bahave similarly to DurationOpt but only allows positive duration values.
  26. type PositiveDurationOpt struct {
  27. DurationOpt
  28. }
  29. // Set a new value on the option. Setting a negative duration value will cause
  30. // an error to be returned.
  31. func (d *PositiveDurationOpt) Set(s string) error {
  32. err := d.DurationOpt.Set(s)
  33. if err != nil {
  34. return err
  35. }
  36. if *d.DurationOpt.value < 0 {
  37. return errors.Errorf("duration cannot be negative")
  38. }
  39. return nil
  40. }
  41. // DurationOpt is an option type for time.Duration that uses a pointer. This
  42. // allows us to get nil values outside, instead of defaulting to 0
  43. type DurationOpt struct {
  44. value *time.Duration
  45. }
  46. // Set a new value on the option
  47. func (d *DurationOpt) Set(s string) error {
  48. v, err := time.ParseDuration(s)
  49. d.value = &v
  50. return err
  51. }
  52. // Type returns the type of this option, which will be displayed in `--help` output
  53. func (d *DurationOpt) Type() string {
  54. return "duration"
  55. }
  56. // String returns a string repr of this option
  57. func (d *DurationOpt) String() string {
  58. if d.value != nil {
  59. return d.value.String()
  60. }
  61. return ""
  62. }
  63. // Value returns the time.Duration
  64. func (d *DurationOpt) Value() *time.Duration {
  65. return d.value
  66. }
  67. // Uint64Opt represents a uint64.
  68. type Uint64Opt struct {
  69. value *uint64
  70. }
  71. // Set a new value on the option
  72. func (i *Uint64Opt) Set(s string) error {
  73. v, err := strconv.ParseUint(s, 0, 64)
  74. i.value = &v
  75. return err
  76. }
  77. // Type returns the type of this option, which will be displayed in `--help` output
  78. func (i *Uint64Opt) Type() string {
  79. return "uint"
  80. }
  81. // String returns a string repr of this option
  82. func (i *Uint64Opt) String() string {
  83. if i.value != nil {
  84. return fmt.Sprintf("%v", *i.value)
  85. }
  86. return ""
  87. }
  88. // Value returns the uint64
  89. func (i *Uint64Opt) Value() *uint64 {
  90. return i.value
  91. }
  92. type floatValue float32
  93. func (f *floatValue) Set(s string) error {
  94. v, err := strconv.ParseFloat(s, 32)
  95. *f = floatValue(v)
  96. return err
  97. }
  98. func (f *floatValue) Type() string {
  99. return "float"
  100. }
  101. func (f *floatValue) String() string {
  102. return strconv.FormatFloat(float64(*f), 'g', -1, 32)
  103. }
  104. func (f *floatValue) Value() float32 {
  105. return float32(*f)
  106. }
  107. // placementPrefOpts holds a list of placement preferences.
  108. type placementPrefOpts struct {
  109. prefs []swarm.PlacementPreference
  110. strings []string
  111. }
  112. func (opts *placementPrefOpts) String() string {
  113. if len(opts.strings) == 0 {
  114. return ""
  115. }
  116. return fmt.Sprintf("%v", opts.strings)
  117. }
  118. // Set validates the input value and adds it to the internal slices.
  119. // Note: in the future strategies other than "spread", may be supported,
  120. // as well as additional comma-separated options.
  121. func (opts *placementPrefOpts) Set(value string) error {
  122. fields := strings.Split(value, "=")
  123. if len(fields) != 2 {
  124. return errors.New(`placement preference must be of the format "<strategy>=<arg>"`)
  125. }
  126. if fields[0] != "spread" {
  127. return errors.Errorf("unsupported placement preference %s (only spread is supported)", fields[0])
  128. }
  129. opts.prefs = append(opts.prefs, swarm.PlacementPreference{
  130. Spread: &swarm.SpreadOver{
  131. SpreadDescriptor: fields[1],
  132. },
  133. })
  134. opts.strings = append(opts.strings, value)
  135. return nil
  136. }
  137. // Type returns a string name for this Option type
  138. func (opts *placementPrefOpts) Type() string {
  139. return "pref"
  140. }
  141. // ShlexOpt is a flag Value which parses a string as a list of shell words
  142. type ShlexOpt []string
  143. // Set the value
  144. func (s *ShlexOpt) Set(value string) error {
  145. valueSlice, err := shlex.Split(value)
  146. *s = ShlexOpt(valueSlice)
  147. return err
  148. }
  149. // Type returns the tyep of the value
  150. func (s *ShlexOpt) Type() string {
  151. return "command"
  152. }
  153. func (s *ShlexOpt) String() string {
  154. if len(*s) == 0 {
  155. return ""
  156. }
  157. return fmt.Sprint(*s)
  158. }
  159. // Value returns the value as a string slice
  160. func (s *ShlexOpt) Value() []string {
  161. return []string(*s)
  162. }
  163. type updateOptions struct {
  164. parallelism uint64
  165. delay time.Duration
  166. monitor time.Duration
  167. onFailure string
  168. maxFailureRatio floatValue
  169. order string
  170. }
  171. func updateConfigFromDefaults(defaultUpdateConfig *api.UpdateConfig) *swarm.UpdateConfig {
  172. defaultFailureAction := strings.ToLower(api.UpdateConfig_FailureAction_name[int32(defaultUpdateConfig.FailureAction)])
  173. defaultMonitor, _ := gogotypes.DurationFromProto(defaultUpdateConfig.Monitor)
  174. return &swarm.UpdateConfig{
  175. Parallelism: defaultUpdateConfig.Parallelism,
  176. Delay: defaultUpdateConfig.Delay,
  177. Monitor: defaultMonitor,
  178. FailureAction: defaultFailureAction,
  179. MaxFailureRatio: defaultUpdateConfig.MaxFailureRatio,
  180. Order: defaultOrder(defaultUpdateConfig.Order),
  181. }
  182. }
  183. func (opts updateOptions) updateConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
  184. if !anyChanged(flags, flagUpdateParallelism, flagUpdateDelay, flagUpdateMonitor, flagUpdateFailureAction, flagUpdateMaxFailureRatio) {
  185. return nil
  186. }
  187. updateConfig := updateConfigFromDefaults(defaults.Service.Update)
  188. if flags.Changed(flagUpdateParallelism) {
  189. updateConfig.Parallelism = opts.parallelism
  190. }
  191. if flags.Changed(flagUpdateDelay) {
  192. updateConfig.Delay = opts.delay
  193. }
  194. if flags.Changed(flagUpdateMonitor) {
  195. updateConfig.Monitor = opts.monitor
  196. }
  197. if flags.Changed(flagUpdateFailureAction) {
  198. updateConfig.FailureAction = opts.onFailure
  199. }
  200. if flags.Changed(flagUpdateMaxFailureRatio) {
  201. updateConfig.MaxFailureRatio = opts.maxFailureRatio.Value()
  202. }
  203. if flags.Changed(flagUpdateOrder) {
  204. updateConfig.Order = opts.order
  205. }
  206. return updateConfig
  207. }
  208. func (opts updateOptions) rollbackConfig(flags *pflag.FlagSet) *swarm.UpdateConfig {
  209. if !anyChanged(flags, flagRollbackParallelism, flagRollbackDelay, flagRollbackMonitor, flagRollbackFailureAction, flagRollbackMaxFailureRatio) {
  210. return nil
  211. }
  212. updateConfig := updateConfigFromDefaults(defaults.Service.Rollback)
  213. if flags.Changed(flagRollbackParallelism) {
  214. updateConfig.Parallelism = opts.parallelism
  215. }
  216. if flags.Changed(flagRollbackDelay) {
  217. updateConfig.Delay = opts.delay
  218. }
  219. if flags.Changed(flagRollbackMonitor) {
  220. updateConfig.Monitor = opts.monitor
  221. }
  222. if flags.Changed(flagRollbackFailureAction) {
  223. updateConfig.FailureAction = opts.onFailure
  224. }
  225. if flags.Changed(flagRollbackMaxFailureRatio) {
  226. updateConfig.MaxFailureRatio = opts.maxFailureRatio.Value()
  227. }
  228. if flags.Changed(flagRollbackOrder) {
  229. updateConfig.Order = opts.order
  230. }
  231. return updateConfig
  232. }
  233. type resourceOptions struct {
  234. limitCPU opts.NanoCPUs
  235. limitMemBytes opts.MemBytes
  236. resCPU opts.NanoCPUs
  237. resMemBytes opts.MemBytes
  238. }
  239. func (r *resourceOptions) ToResourceRequirements() *swarm.ResourceRequirements {
  240. return &swarm.ResourceRequirements{
  241. Limits: &swarm.Resources{
  242. NanoCPUs: r.limitCPU.Value(),
  243. MemoryBytes: r.limitMemBytes.Value(),
  244. },
  245. Reservations: &swarm.Resources{
  246. NanoCPUs: r.resCPU.Value(),
  247. MemoryBytes: r.resMemBytes.Value(),
  248. },
  249. }
  250. }
  251. type restartPolicyOptions struct {
  252. condition string
  253. delay DurationOpt
  254. maxAttempts Uint64Opt
  255. window DurationOpt
  256. }
  257. func defaultRestartPolicy() *swarm.RestartPolicy {
  258. defaultMaxAttempts := defaults.Service.Task.Restart.MaxAttempts
  259. rp := &swarm.RestartPolicy{
  260. MaxAttempts: &defaultMaxAttempts,
  261. }
  262. if defaults.Service.Task.Restart.Delay != nil {
  263. defaultRestartDelay, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Delay)
  264. rp.Delay = &defaultRestartDelay
  265. }
  266. if defaults.Service.Task.Restart.Window != nil {
  267. defaultRestartWindow, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Window)
  268. rp.Window = &defaultRestartWindow
  269. }
  270. rp.Condition = defaultRestartCondition()
  271. return rp
  272. }
  273. func defaultRestartCondition() swarm.RestartPolicyCondition {
  274. switch defaults.Service.Task.Restart.Condition {
  275. case api.RestartOnNone:
  276. return "none"
  277. case api.RestartOnFailure:
  278. return "on-failure"
  279. case api.RestartOnAny:
  280. return "any"
  281. default:
  282. return ""
  283. }
  284. }
  285. func defaultOrder(order api.UpdateConfig_UpdateOrder) string {
  286. switch order {
  287. case api.UpdateConfig_STOP_FIRST:
  288. return "stop-first"
  289. case api.UpdateConfig_START_FIRST:
  290. return "start-first"
  291. default:
  292. return ""
  293. }
  294. }
  295. func (r *restartPolicyOptions) ToRestartPolicy(flags *pflag.FlagSet) *swarm.RestartPolicy {
  296. if !anyChanged(flags, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow, flagRestartCondition) {
  297. return nil
  298. }
  299. restartPolicy := defaultRestartPolicy()
  300. if flags.Changed(flagRestartDelay) {
  301. restartPolicy.Delay = r.delay.Value()
  302. }
  303. if flags.Changed(flagRestartCondition) {
  304. restartPolicy.Condition = swarm.RestartPolicyCondition(r.condition)
  305. }
  306. if flags.Changed(flagRestartMaxAttempts) {
  307. restartPolicy.MaxAttempts = r.maxAttempts.Value()
  308. }
  309. if flags.Changed(flagRestartWindow) {
  310. restartPolicy.Window = r.window.Value()
  311. }
  312. return restartPolicy
  313. }
  314. type credentialSpecOpt struct {
  315. value *swarm.CredentialSpec
  316. source string
  317. }
  318. func (c *credentialSpecOpt) Set(value string) error {
  319. c.source = value
  320. c.value = &swarm.CredentialSpec{}
  321. switch {
  322. case strings.HasPrefix(value, "file://"):
  323. c.value.File = strings.TrimPrefix(value, "file://")
  324. case strings.HasPrefix(value, "registry://"):
  325. c.value.Registry = strings.TrimPrefix(value, "registry://")
  326. default:
  327. return errors.New("Invalid credential spec - value must be prefixed file:// or registry:// followed by a value")
  328. }
  329. return nil
  330. }
  331. func (c *credentialSpecOpt) Type() string {
  332. return "credential-spec"
  333. }
  334. func (c *credentialSpecOpt) String() string {
  335. return c.source
  336. }
  337. func (c *credentialSpecOpt) Value() *swarm.CredentialSpec {
  338. return c.value
  339. }
  340. func convertNetworks(ctx context.Context, apiClient client.NetworkAPIClient, networks []string) ([]swarm.NetworkAttachmentConfig, error) {
  341. nets := []swarm.NetworkAttachmentConfig{}
  342. for _, networkIDOrName := range networks {
  343. network, err := apiClient.NetworkInspect(ctx, networkIDOrName, false)
  344. if err != nil {
  345. return nil, err
  346. }
  347. nets = append(nets, swarm.NetworkAttachmentConfig{Target: network.ID})
  348. }
  349. sort.Sort(byNetworkTarget(nets))
  350. return nets, nil
  351. }
  352. type endpointOptions struct {
  353. mode string
  354. publishPorts opts.PortOpt
  355. }
  356. func (e *endpointOptions) ToEndpointSpec() *swarm.EndpointSpec {
  357. return &swarm.EndpointSpec{
  358. Mode: swarm.ResolutionMode(strings.ToLower(e.mode)),
  359. Ports: e.publishPorts.Value(),
  360. }
  361. }
  362. type logDriverOptions struct {
  363. name string
  364. opts opts.ListOpts
  365. }
  366. func newLogDriverOptions() logDriverOptions {
  367. return logDriverOptions{opts: opts.NewListOpts(opts.ValidateEnv)}
  368. }
  369. func (ldo *logDriverOptions) toLogDriver() *swarm.Driver {
  370. if ldo.name == "" {
  371. return nil
  372. }
  373. // set the log driver only if specified.
  374. return &swarm.Driver{
  375. Name: ldo.name,
  376. Options: runconfigopts.ConvertKVStringsToMap(ldo.opts.GetAll()),
  377. }
  378. }
  379. type healthCheckOptions struct {
  380. cmd string
  381. interval PositiveDurationOpt
  382. timeout PositiveDurationOpt
  383. retries int
  384. startPeriod PositiveDurationOpt
  385. noHealthcheck bool
  386. }
  387. func (opts *healthCheckOptions) toHealthConfig() (*container.HealthConfig, error) {
  388. var healthConfig *container.HealthConfig
  389. haveHealthSettings := opts.cmd != "" ||
  390. opts.interval.Value() != nil ||
  391. opts.timeout.Value() != nil ||
  392. opts.retries != 0
  393. if opts.noHealthcheck {
  394. if haveHealthSettings {
  395. return nil, errors.Errorf("--%s conflicts with --health-* options", flagNoHealthcheck)
  396. }
  397. healthConfig = &container.HealthConfig{Test: []string{"NONE"}}
  398. } else if haveHealthSettings {
  399. var test []string
  400. if opts.cmd != "" {
  401. test = []string{"CMD-SHELL", opts.cmd}
  402. }
  403. var interval, timeout, startPeriod time.Duration
  404. if ptr := opts.interval.Value(); ptr != nil {
  405. interval = *ptr
  406. }
  407. if ptr := opts.timeout.Value(); ptr != nil {
  408. timeout = *ptr
  409. }
  410. if ptr := opts.startPeriod.Value(); ptr != nil {
  411. startPeriod = *ptr
  412. }
  413. healthConfig = &container.HealthConfig{
  414. Test: test,
  415. Interval: interval,
  416. Timeout: timeout,
  417. Retries: opts.retries,
  418. StartPeriod: startPeriod,
  419. }
  420. }
  421. return healthConfig, nil
  422. }
  423. // convertExtraHostsToSwarmHosts converts an array of extra hosts in cli
  424. // <host>:<ip>
  425. // into a swarmkit host format:
  426. // IP_address canonical_hostname [aliases...]
  427. // This assumes input value (<host>:<ip>) has already been validated
  428. func convertExtraHostsToSwarmHosts(extraHosts []string) []string {
  429. hosts := []string{}
  430. for _, extraHost := range extraHosts {
  431. parts := strings.SplitN(extraHost, ":", 2)
  432. hosts = append(hosts, fmt.Sprintf("%s %s", parts[1], parts[0]))
  433. }
  434. return hosts
  435. }
  436. type serviceOptions struct {
  437. detach bool
  438. quiet bool
  439. name string
  440. labels opts.ListOpts
  441. containerLabels opts.ListOpts
  442. image string
  443. entrypoint ShlexOpt
  444. args []string
  445. hostname string
  446. env opts.ListOpts
  447. envFile opts.ListOpts
  448. workdir string
  449. user string
  450. groups opts.ListOpts
  451. credentialSpec credentialSpecOpt
  452. stopSignal string
  453. tty bool
  454. readOnly bool
  455. mounts opts.MountOpt
  456. dns opts.ListOpts
  457. dnsSearch opts.ListOpts
  458. dnsOption opts.ListOpts
  459. hosts opts.ListOpts
  460. resources resourceOptions
  461. stopGrace DurationOpt
  462. replicas Uint64Opt
  463. mode string
  464. restartPolicy restartPolicyOptions
  465. constraints opts.ListOpts
  466. placementPrefs placementPrefOpts
  467. update updateOptions
  468. rollback updateOptions
  469. networks opts.ListOpts
  470. endpoint endpointOptions
  471. registryAuth bool
  472. logDriver logDriverOptions
  473. healthcheck healthCheckOptions
  474. secrets opts.SecretOpt
  475. }
  476. func newServiceOptions() *serviceOptions {
  477. return &serviceOptions{
  478. labels: opts.NewListOpts(opts.ValidateEnv),
  479. constraints: opts.NewListOpts(nil),
  480. containerLabels: opts.NewListOpts(opts.ValidateEnv),
  481. env: opts.NewListOpts(opts.ValidateEnv),
  482. envFile: opts.NewListOpts(nil),
  483. groups: opts.NewListOpts(nil),
  484. logDriver: newLogDriverOptions(),
  485. dns: opts.NewListOpts(opts.ValidateIPAddress),
  486. dnsOption: opts.NewListOpts(nil),
  487. dnsSearch: opts.NewListOpts(opts.ValidateDNSSearch),
  488. hosts: opts.NewListOpts(opts.ValidateExtraHost),
  489. networks: opts.NewListOpts(nil),
  490. }
  491. }
  492. func (opts *serviceOptions) ToServiceMode() (swarm.ServiceMode, error) {
  493. serviceMode := swarm.ServiceMode{}
  494. switch opts.mode {
  495. case "global":
  496. if opts.replicas.Value() != nil {
  497. return serviceMode, errors.Errorf("replicas can only be used with replicated mode")
  498. }
  499. serviceMode.Global = &swarm.GlobalService{}
  500. case "replicated":
  501. serviceMode.Replicated = &swarm.ReplicatedService{
  502. Replicas: opts.replicas.Value(),
  503. }
  504. default:
  505. return serviceMode, errors.Errorf("Unknown mode: %s, only replicated and global supported", opts.mode)
  506. }
  507. return serviceMode, nil
  508. }
  509. func (opts *serviceOptions) ToStopGracePeriod(flags *pflag.FlagSet) *time.Duration {
  510. if flags.Changed(flagStopGracePeriod) {
  511. return opts.stopGrace.Value()
  512. }
  513. return nil
  514. }
  515. func (opts *serviceOptions) ToService(ctx context.Context, apiClient client.APIClient, flags *pflag.FlagSet) (swarm.ServiceSpec, error) {
  516. var service swarm.ServiceSpec
  517. envVariables, err := runconfigopts.ReadKVStrings(opts.envFile.GetAll(), opts.env.GetAll())
  518. if err != nil {
  519. return service, err
  520. }
  521. currentEnv := make([]string, 0, len(envVariables))
  522. for _, env := range envVariables { // need to process each var, in order
  523. k := strings.SplitN(env, "=", 2)[0]
  524. for i, current := range currentEnv { // remove duplicates
  525. if current == env {
  526. continue // no update required, may hide this behind flag to preserve order of envVariables
  527. }
  528. if strings.HasPrefix(current, k+"=") {
  529. currentEnv = append(currentEnv[:i], currentEnv[i+1:]...)
  530. }
  531. }
  532. currentEnv = append(currentEnv, env)
  533. }
  534. healthConfig, err := opts.healthcheck.toHealthConfig()
  535. if err != nil {
  536. return service, err
  537. }
  538. serviceMode, err := opts.ToServiceMode()
  539. if err != nil {
  540. return service, err
  541. }
  542. networks, err := convertNetworks(ctx, apiClient, opts.networks.GetAll())
  543. if err != nil {
  544. return service, err
  545. }
  546. service = swarm.ServiceSpec{
  547. Annotations: swarm.Annotations{
  548. Name: opts.name,
  549. Labels: runconfigopts.ConvertKVStringsToMap(opts.labels.GetAll()),
  550. },
  551. TaskTemplate: swarm.TaskSpec{
  552. ContainerSpec: swarm.ContainerSpec{
  553. Image: opts.image,
  554. Args: opts.args,
  555. Command: opts.entrypoint.Value(),
  556. Env: currentEnv,
  557. Hostname: opts.hostname,
  558. Labels: runconfigopts.ConvertKVStringsToMap(opts.containerLabels.GetAll()),
  559. Dir: opts.workdir,
  560. User: opts.user,
  561. Groups: opts.groups.GetAll(),
  562. StopSignal: opts.stopSignal,
  563. TTY: opts.tty,
  564. ReadOnly: opts.readOnly,
  565. Mounts: opts.mounts.Value(),
  566. DNSConfig: &swarm.DNSConfig{
  567. Nameservers: opts.dns.GetAll(),
  568. Search: opts.dnsSearch.GetAll(),
  569. Options: opts.dnsOption.GetAll(),
  570. },
  571. Hosts: convertExtraHostsToSwarmHosts(opts.hosts.GetAll()),
  572. StopGracePeriod: opts.ToStopGracePeriod(flags),
  573. Secrets: nil,
  574. Healthcheck: healthConfig,
  575. },
  576. Networks: networks,
  577. Resources: opts.resources.ToResourceRequirements(),
  578. RestartPolicy: opts.restartPolicy.ToRestartPolicy(flags),
  579. Placement: &swarm.Placement{
  580. Constraints: opts.constraints.GetAll(),
  581. Preferences: opts.placementPrefs.prefs,
  582. },
  583. LogDriver: opts.logDriver.toLogDriver(),
  584. },
  585. Mode: serviceMode,
  586. UpdateConfig: opts.update.updateConfig(flags),
  587. RollbackConfig: opts.update.rollbackConfig(flags),
  588. EndpointSpec: opts.endpoint.ToEndpointSpec(),
  589. }
  590. if opts.credentialSpec.Value() != nil {
  591. service.TaskTemplate.ContainerSpec.Privileges = &swarm.Privileges{
  592. CredentialSpec: opts.credentialSpec.Value(),
  593. }
  594. }
  595. return service, nil
  596. }
  597. type flagDefaults map[string]interface{}
  598. func (fd flagDefaults) getUint64(flagName string) uint64 {
  599. if val, ok := fd[flagName].(uint64); ok {
  600. return val
  601. }
  602. return 0
  603. }
  604. func (fd flagDefaults) getString(flagName string) string {
  605. if val, ok := fd[flagName].(string); ok {
  606. return val
  607. }
  608. return ""
  609. }
  610. func buildServiceDefaultFlagMapping() flagDefaults {
  611. defaultFlagValues := make(map[string]interface{})
  612. defaultFlagValues[flagStopGracePeriod], _ = gogotypes.DurationFromProto(defaults.Service.Task.GetContainer().StopGracePeriod)
  613. defaultFlagValues[flagRestartCondition] = `"` + defaultRestartCondition() + `"`
  614. defaultFlagValues[flagRestartDelay], _ = gogotypes.DurationFromProto(defaults.Service.Task.Restart.Delay)
  615. if defaults.Service.Task.Restart.MaxAttempts != 0 {
  616. defaultFlagValues[flagRestartMaxAttempts] = defaults.Service.Task.Restart.MaxAttempts
  617. }
  618. defaultRestartWindow, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Window)
  619. if defaultRestartWindow != 0 {
  620. defaultFlagValues[flagRestartWindow] = defaultRestartWindow
  621. }
  622. defaultFlagValues[flagUpdateParallelism] = defaults.Service.Update.Parallelism
  623. defaultFlagValues[flagUpdateDelay] = defaults.Service.Update.Delay
  624. defaultFlagValues[flagUpdateMonitor], _ = gogotypes.DurationFromProto(defaults.Service.Update.Monitor)
  625. defaultFlagValues[flagUpdateFailureAction] = `"` + strings.ToLower(api.UpdateConfig_FailureAction_name[int32(defaults.Service.Update.FailureAction)]) + `"`
  626. defaultFlagValues[flagUpdateMaxFailureRatio] = defaults.Service.Update.MaxFailureRatio
  627. defaultFlagValues[flagUpdateOrder] = `"` + defaultOrder(defaults.Service.Update.Order) + `"`
  628. defaultFlagValues[flagRollbackParallelism] = defaults.Service.Rollback.Parallelism
  629. defaultFlagValues[flagRollbackDelay] = defaults.Service.Rollback.Delay
  630. defaultFlagValues[flagRollbackMonitor], _ = gogotypes.DurationFromProto(defaults.Service.Rollback.Monitor)
  631. defaultFlagValues[flagRollbackFailureAction] = `"` + strings.ToLower(api.UpdateConfig_FailureAction_name[int32(defaults.Service.Rollback.FailureAction)]) + `"`
  632. defaultFlagValues[flagRollbackMaxFailureRatio] = defaults.Service.Rollback.MaxFailureRatio
  633. defaultFlagValues[flagRollbackOrder] = `"` + defaultOrder(defaults.Service.Rollback.Order) + `"`
  634. defaultFlagValues[flagEndpointMode] = "vip"
  635. return defaultFlagValues
  636. }
  637. // addServiceFlags adds all flags that are common to both `create` and `update`.
  638. // Any flags that are not common are added separately in the individual command
  639. func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValues flagDefaults) {
  640. flagDesc := func(flagName string, desc string) string {
  641. if defaultValue, ok := defaultFlagValues[flagName]; ok {
  642. return fmt.Sprintf("%s (default %v)", desc, defaultValue)
  643. }
  644. return desc
  645. }
  646. flags.BoolVarP(&opts.detach, "detach", "d", true, "Exit immediately instead of waiting for the service to converge")
  647. flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress progress output")
  648. flags.StringVarP(&opts.workdir, flagWorkdir, "w", "", "Working directory inside the container")
  649. flags.StringVarP(&opts.user, flagUser, "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
  650. flags.Var(&opts.credentialSpec, flagCredentialSpec, "Credential spec for managed service account (Windows only)")
  651. flags.SetAnnotation(flagCredentialSpec, "version", []string{"1.29"})
  652. flags.StringVar(&opts.hostname, flagHostname, "", "Container hostname")
  653. flags.SetAnnotation(flagHostname, "version", []string{"1.25"})
  654. flags.Var(&opts.entrypoint, flagEntrypoint, "Overwrite the default ENTRYPOINT of the image")
  655. flags.Var(&opts.resources.limitCPU, flagLimitCPU, "Limit CPUs")
  656. flags.Var(&opts.resources.limitMemBytes, flagLimitMemory, "Limit Memory")
  657. flags.Var(&opts.resources.resCPU, flagReserveCPU, "Reserve CPUs")
  658. flags.Var(&opts.resources.resMemBytes, flagReserveMemory, "Reserve Memory")
  659. flags.Var(&opts.stopGrace, flagStopGracePeriod, flagDesc(flagStopGracePeriod, "Time to wait before force killing a container (ns|us|ms|s|m|h)"))
  660. flags.Var(&opts.replicas, flagReplicas, "Number of tasks")
  661. flags.StringVar(&opts.restartPolicy.condition, flagRestartCondition, "", flagDesc(flagRestartCondition, `Restart when condition is met ("none"|"on-failure"|"any")`))
  662. flags.Var(&opts.restartPolicy.delay, flagRestartDelay, flagDesc(flagRestartDelay, "Delay between restart attempts (ns|us|ms|s|m|h)"))
  663. flags.Var(&opts.restartPolicy.maxAttempts, flagRestartMaxAttempts, flagDesc(flagRestartMaxAttempts, "Maximum number of restarts before giving up"))
  664. flags.Var(&opts.restartPolicy.window, flagRestartWindow, flagDesc(flagRestartWindow, "Window used to evaluate the restart policy (ns|us|ms|s|m|h)"))
  665. flags.Uint64Var(&opts.update.parallelism, flagUpdateParallelism, defaultFlagValues.getUint64(flagUpdateParallelism), "Maximum number of tasks updated simultaneously (0 to update all at once)")
  666. flags.DurationVar(&opts.update.delay, flagUpdateDelay, 0, flagDesc(flagUpdateDelay, "Delay between updates (ns|us|ms|s|m|h)"))
  667. flags.DurationVar(&opts.update.monitor, flagUpdateMonitor, 0, flagDesc(flagUpdateMonitor, "Duration after each task update to monitor for failure (ns|us|ms|s|m|h)"))
  668. flags.SetAnnotation(flagUpdateMonitor, "version", []string{"1.25"})
  669. flags.StringVar(&opts.update.onFailure, flagUpdateFailureAction, "", flagDesc(flagUpdateFailureAction, `Action on update failure ("pause"|"continue"|"rollback")`))
  670. flags.Var(&opts.update.maxFailureRatio, flagUpdateMaxFailureRatio, flagDesc(flagUpdateMaxFailureRatio, "Failure rate to tolerate during an update"))
  671. flags.SetAnnotation(flagUpdateMaxFailureRatio, "version", []string{"1.25"})
  672. flags.StringVar(&opts.update.order, flagUpdateOrder, "", flagDesc(flagUpdateOrder, `Update order ("start-first"|"stop-first")`))
  673. flags.SetAnnotation(flagUpdateOrder, "version", []string{"1.29"})
  674. flags.Uint64Var(&opts.rollback.parallelism, flagRollbackParallelism, defaultFlagValues.getUint64(flagRollbackParallelism), "Maximum number of tasks rolled back simultaneously (0 to roll back all at once)")
  675. flags.SetAnnotation(flagRollbackParallelism, "version", []string{"1.28"})
  676. flags.DurationVar(&opts.rollback.delay, flagRollbackDelay, 0, flagDesc(flagRollbackDelay, "Delay between task rollbacks (ns|us|ms|s|m|h)"))
  677. flags.SetAnnotation(flagRollbackDelay, "version", []string{"1.28"})
  678. flags.DurationVar(&opts.rollback.monitor, flagRollbackMonitor, 0, flagDesc(flagRollbackMonitor, "Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h)"))
  679. flags.SetAnnotation(flagRollbackMonitor, "version", []string{"1.28"})
  680. flags.StringVar(&opts.rollback.onFailure, flagRollbackFailureAction, "", flagDesc(flagRollbackFailureAction, `Action on rollback failure ("pause"|"continue")`))
  681. flags.SetAnnotation(flagRollbackFailureAction, "version", []string{"1.28"})
  682. flags.Var(&opts.rollback.maxFailureRatio, flagRollbackMaxFailureRatio, flagDesc(flagRollbackMaxFailureRatio, "Failure rate to tolerate during a rollback"))
  683. flags.SetAnnotation(flagRollbackMaxFailureRatio, "version", []string{"1.28"})
  684. flags.StringVar(&opts.rollback.order, flagRollbackOrder, "", flagDesc(flagRollbackOrder, `Rollback order ("start-first"|"stop-first")`))
  685. flags.SetAnnotation(flagRollbackOrder, "version", []string{"1.29"})
  686. flags.StringVar(&opts.endpoint.mode, flagEndpointMode, defaultFlagValues.getString(flagEndpointMode), "Endpoint mode (vip or dnsrr)")
  687. flags.BoolVar(&opts.registryAuth, flagRegistryAuth, false, "Send registry authentication details to swarm agents")
  688. flags.StringVar(&opts.logDriver.name, flagLogDriver, "", "Logging driver for service")
  689. flags.Var(&opts.logDriver.opts, flagLogOpt, "Logging driver options")
  690. flags.StringVar(&opts.healthcheck.cmd, flagHealthCmd, "", "Command to run to check health")
  691. flags.SetAnnotation(flagHealthCmd, "version", []string{"1.25"})
  692. flags.Var(&opts.healthcheck.interval, flagHealthInterval, "Time between running the check (ms|s|m|h)")
  693. flags.SetAnnotation(flagHealthInterval, "version", []string{"1.25"})
  694. flags.Var(&opts.healthcheck.timeout, flagHealthTimeout, "Maximum time to allow one check to run (ms|s|m|h)")
  695. flags.SetAnnotation(flagHealthTimeout, "version", []string{"1.25"})
  696. flags.IntVar(&opts.healthcheck.retries, flagHealthRetries, 0, "Consecutive failures needed to report unhealthy")
  697. flags.SetAnnotation(flagHealthRetries, "version", []string{"1.25"})
  698. flags.Var(&opts.healthcheck.startPeriod, flagHealthStartPeriod, "Start period for the container to initialize before counting retries towards unstable (ms|s|m|h)")
  699. flags.SetAnnotation(flagHealthStartPeriod, "version", []string{"1.29"})
  700. flags.BoolVar(&opts.healthcheck.noHealthcheck, flagNoHealthcheck, false, "Disable any container-specified HEALTHCHECK")
  701. flags.SetAnnotation(flagNoHealthcheck, "version", []string{"1.25"})
  702. flags.BoolVarP(&opts.tty, flagTTY, "t", false, "Allocate a pseudo-TTY")
  703. flags.SetAnnotation(flagTTY, "version", []string{"1.25"})
  704. flags.BoolVar(&opts.readOnly, flagReadOnly, false, "Mount the container's root filesystem as read only")
  705. flags.SetAnnotation(flagReadOnly, "version", []string{"1.28"})
  706. flags.StringVar(&opts.stopSignal, flagStopSignal, "", "Signal to stop the container")
  707. flags.SetAnnotation(flagStopSignal, "version", []string{"1.28"})
  708. }
  709. const (
  710. flagCredentialSpec = "credential-spec"
  711. flagPlacementPref = "placement-pref"
  712. flagPlacementPrefAdd = "placement-pref-add"
  713. flagPlacementPrefRemove = "placement-pref-rm"
  714. flagConstraint = "constraint"
  715. flagConstraintRemove = "constraint-rm"
  716. flagConstraintAdd = "constraint-add"
  717. flagContainerLabel = "container-label"
  718. flagContainerLabelRemove = "container-label-rm"
  719. flagContainerLabelAdd = "container-label-add"
  720. flagDNS = "dns"
  721. flagDNSRemove = "dns-rm"
  722. flagDNSAdd = "dns-add"
  723. flagDNSOption = "dns-option"
  724. flagDNSOptionRemove = "dns-option-rm"
  725. flagDNSOptionAdd = "dns-option-add"
  726. flagDNSSearch = "dns-search"
  727. flagDNSSearchRemove = "dns-search-rm"
  728. flagDNSSearchAdd = "dns-search-add"
  729. flagEndpointMode = "endpoint-mode"
  730. flagEntrypoint = "entrypoint"
  731. flagHost = "host"
  732. flagHostAdd = "host-add"
  733. flagHostRemove = "host-rm"
  734. flagHostname = "hostname"
  735. flagEnv = "env"
  736. flagEnvFile = "env-file"
  737. flagEnvRemove = "env-rm"
  738. flagEnvAdd = "env-add"
  739. flagGroup = "group"
  740. flagGroupAdd = "group-add"
  741. flagGroupRemove = "group-rm"
  742. flagLabel = "label"
  743. flagLabelRemove = "label-rm"
  744. flagLabelAdd = "label-add"
  745. flagLimitCPU = "limit-cpu"
  746. flagLimitMemory = "limit-memory"
  747. flagMode = "mode"
  748. flagMount = "mount"
  749. flagMountRemove = "mount-rm"
  750. flagMountAdd = "mount-add"
  751. flagName = "name"
  752. flagNetwork = "network"
  753. flagNetworkAdd = "network-add"
  754. flagNetworkRemove = "network-rm"
  755. flagPublish = "publish"
  756. flagPublishRemove = "publish-rm"
  757. flagPublishAdd = "publish-add"
  758. flagReadOnly = "read-only"
  759. flagReplicas = "replicas"
  760. flagReserveCPU = "reserve-cpu"
  761. flagReserveMemory = "reserve-memory"
  762. flagRestartCondition = "restart-condition"
  763. flagRestartDelay = "restart-delay"
  764. flagRestartMaxAttempts = "restart-max-attempts"
  765. flagRestartWindow = "restart-window"
  766. flagRollbackDelay = "rollback-delay"
  767. flagRollbackFailureAction = "rollback-failure-action"
  768. flagRollbackMaxFailureRatio = "rollback-max-failure-ratio"
  769. flagRollbackMonitor = "rollback-monitor"
  770. flagRollbackOrder = "rollback-order"
  771. flagRollbackParallelism = "rollback-parallelism"
  772. flagStopGracePeriod = "stop-grace-period"
  773. flagStopSignal = "stop-signal"
  774. flagTTY = "tty"
  775. flagUpdateDelay = "update-delay"
  776. flagUpdateFailureAction = "update-failure-action"
  777. flagUpdateMaxFailureRatio = "update-max-failure-ratio"
  778. flagUpdateMonitor = "update-monitor"
  779. flagUpdateOrder = "update-order"
  780. flagUpdateParallelism = "update-parallelism"
  781. flagUser = "user"
  782. flagWorkdir = "workdir"
  783. flagRegistryAuth = "with-registry-auth"
  784. flagLogDriver = "log-driver"
  785. flagLogOpt = "log-opt"
  786. flagHealthCmd = "health-cmd"
  787. flagHealthInterval = "health-interval"
  788. flagHealthRetries = "health-retries"
  789. flagHealthTimeout = "health-timeout"
  790. flagHealthStartPeriod = "health-start-period"
  791. flagNoHealthcheck = "no-healthcheck"
  792. flagSecret = "secret"
  793. flagSecretAdd = "secret-add"
  794. flagSecretRemove = "secret-rm"
  795. )