opts.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. package service
  2. import (
  3. "errors"
  4. "fmt"
  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/opts"
  11. runconfigopts "github.com/docker/docker/runconfig/opts"
  12. "github.com/spf13/cobra"
  13. )
  14. type int64Value interface {
  15. Value() int64
  16. }
  17. // PositiveDurationOpt is an option type for time.Duration that uses a pointer.
  18. // It bahave similarly to DurationOpt but only allows positive duration values.
  19. type PositiveDurationOpt struct {
  20. DurationOpt
  21. }
  22. // Set a new value on the option. Setting a negative duration value will cause
  23. // an error to be returned.
  24. func (d *PositiveDurationOpt) Set(s string) error {
  25. err := d.DurationOpt.Set(s)
  26. if err != nil {
  27. return err
  28. }
  29. if *d.DurationOpt.value < 0 {
  30. return fmt.Errorf("duration cannot be negative")
  31. }
  32. return nil
  33. }
  34. // DurationOpt is an option type for time.Duration that uses a pointer. This
  35. // allows us to get nil values outside, instead of defaulting to 0
  36. type DurationOpt struct {
  37. value *time.Duration
  38. }
  39. // Set a new value on the option
  40. func (d *DurationOpt) Set(s string) error {
  41. v, err := time.ParseDuration(s)
  42. d.value = &v
  43. return err
  44. }
  45. // Type returns the type of this option, which will be displayed in `--help` output
  46. func (d *DurationOpt) Type() string {
  47. return "duration"
  48. }
  49. // String returns a string repr of this option
  50. func (d *DurationOpt) String() string {
  51. if d.value != nil {
  52. return d.value.String()
  53. }
  54. return ""
  55. }
  56. // Value returns the time.Duration
  57. func (d *DurationOpt) Value() *time.Duration {
  58. return d.value
  59. }
  60. // Uint64Opt represents a uint64.
  61. type Uint64Opt struct {
  62. value *uint64
  63. }
  64. // Set a new value on the option
  65. func (i *Uint64Opt) Set(s string) error {
  66. v, err := strconv.ParseUint(s, 0, 64)
  67. i.value = &v
  68. return err
  69. }
  70. // Type returns the type of this option, which will be displayed in `--help` output
  71. func (i *Uint64Opt) Type() string {
  72. return "uint"
  73. }
  74. // String returns a string repr of this option
  75. func (i *Uint64Opt) String() string {
  76. if i.value != nil {
  77. return fmt.Sprintf("%v", *i.value)
  78. }
  79. return ""
  80. }
  81. // Value returns the uint64
  82. func (i *Uint64Opt) Value() *uint64 {
  83. return i.value
  84. }
  85. type floatValue float32
  86. func (f *floatValue) Set(s string) error {
  87. v, err := strconv.ParseFloat(s, 32)
  88. *f = floatValue(v)
  89. return err
  90. }
  91. func (f *floatValue) Type() string {
  92. return "float"
  93. }
  94. func (f *floatValue) String() string {
  95. return strconv.FormatFloat(float64(*f), 'g', -1, 32)
  96. }
  97. func (f *floatValue) Value() float32 {
  98. return float32(*f)
  99. }
  100. // placementPrefOpts holds a list of placement preferences.
  101. type placementPrefOpts struct {
  102. prefs []swarm.PlacementPreference
  103. strings []string
  104. }
  105. func (opts *placementPrefOpts) String() string {
  106. if len(opts.strings) == 0 {
  107. return ""
  108. }
  109. return fmt.Sprintf("%v", opts.strings)
  110. }
  111. // Set validates the input value and adds it to the internal slices.
  112. // Note: in the future strategies other than "spread", may be supported,
  113. // as well as additional comma-separated options.
  114. func (opts *placementPrefOpts) Set(value string) error {
  115. fields := strings.Split(value, "=")
  116. if len(fields) != 2 {
  117. return errors.New(`placement preference must be of the format "<strategy>=<arg>"`)
  118. }
  119. if fields[0] != "spread" {
  120. return fmt.Errorf("unsupported placement preference %s (only spread is supported)", fields[0])
  121. }
  122. opts.prefs = append(opts.prefs, swarm.PlacementPreference{
  123. Spread: &swarm.SpreadOver{
  124. SpreadDescriptor: fields[1],
  125. },
  126. })
  127. opts.strings = append(opts.strings, value)
  128. return nil
  129. }
  130. // Type returns a string name for this Option type
  131. func (opts *placementPrefOpts) Type() string {
  132. return "pref"
  133. }
  134. type updateOptions struct {
  135. parallelism uint64
  136. delay time.Duration
  137. monitor time.Duration
  138. onFailure string
  139. maxFailureRatio floatValue
  140. }
  141. func (opts updateOptions) config() *swarm.UpdateConfig {
  142. return &swarm.UpdateConfig{
  143. Parallelism: opts.parallelism,
  144. Delay: opts.delay,
  145. Monitor: opts.monitor,
  146. FailureAction: opts.onFailure,
  147. MaxFailureRatio: opts.maxFailureRatio.Value(),
  148. }
  149. }
  150. type resourceOptions struct {
  151. limitCPU opts.NanoCPUs
  152. limitMemBytes opts.MemBytes
  153. resCPU opts.NanoCPUs
  154. resMemBytes opts.MemBytes
  155. }
  156. func (r *resourceOptions) ToResourceRequirements() *swarm.ResourceRequirements {
  157. return &swarm.ResourceRequirements{
  158. Limits: &swarm.Resources{
  159. NanoCPUs: r.limitCPU.Value(),
  160. MemoryBytes: r.limitMemBytes.Value(),
  161. },
  162. Reservations: &swarm.Resources{
  163. NanoCPUs: r.resCPU.Value(),
  164. MemoryBytes: r.resMemBytes.Value(),
  165. },
  166. }
  167. }
  168. type restartPolicyOptions struct {
  169. condition string
  170. delay DurationOpt
  171. maxAttempts Uint64Opt
  172. window DurationOpt
  173. }
  174. func (r *restartPolicyOptions) ToRestartPolicy() *swarm.RestartPolicy {
  175. return &swarm.RestartPolicy{
  176. Condition: swarm.RestartPolicyCondition(r.condition),
  177. Delay: r.delay.Value(),
  178. MaxAttempts: r.maxAttempts.Value(),
  179. Window: r.window.Value(),
  180. }
  181. }
  182. func convertNetworks(networks []string) []swarm.NetworkAttachmentConfig {
  183. nets := []swarm.NetworkAttachmentConfig{}
  184. for _, network := range networks {
  185. nets = append(nets, swarm.NetworkAttachmentConfig{Target: network})
  186. }
  187. return nets
  188. }
  189. type endpointOptions struct {
  190. mode string
  191. publishPorts opts.PortOpt
  192. }
  193. func (e *endpointOptions) ToEndpointSpec() *swarm.EndpointSpec {
  194. return &swarm.EndpointSpec{
  195. Mode: swarm.ResolutionMode(strings.ToLower(e.mode)),
  196. Ports: e.publishPorts.Value(),
  197. }
  198. }
  199. type logDriverOptions struct {
  200. name string
  201. opts opts.ListOpts
  202. }
  203. func newLogDriverOptions() logDriverOptions {
  204. return logDriverOptions{opts: opts.NewListOpts(opts.ValidateEnv)}
  205. }
  206. func (ldo *logDriverOptions) toLogDriver() *swarm.Driver {
  207. if ldo.name == "" {
  208. return nil
  209. }
  210. // set the log driver only if specified.
  211. return &swarm.Driver{
  212. Name: ldo.name,
  213. Options: runconfigopts.ConvertKVStringsToMap(ldo.opts.GetAll()),
  214. }
  215. }
  216. type healthCheckOptions struct {
  217. cmd string
  218. interval PositiveDurationOpt
  219. timeout PositiveDurationOpt
  220. retries int
  221. noHealthcheck bool
  222. }
  223. func (opts *healthCheckOptions) toHealthConfig() (*container.HealthConfig, error) {
  224. var healthConfig *container.HealthConfig
  225. haveHealthSettings := opts.cmd != "" ||
  226. opts.interval.Value() != nil ||
  227. opts.timeout.Value() != nil ||
  228. opts.retries != 0
  229. if opts.noHealthcheck {
  230. if haveHealthSettings {
  231. return nil, fmt.Errorf("--%s conflicts with --health-* options", flagNoHealthcheck)
  232. }
  233. healthConfig = &container.HealthConfig{Test: []string{"NONE"}}
  234. } else if haveHealthSettings {
  235. var test []string
  236. if opts.cmd != "" {
  237. test = []string{"CMD-SHELL", opts.cmd}
  238. }
  239. var interval, timeout time.Duration
  240. if ptr := opts.interval.Value(); ptr != nil {
  241. interval = *ptr
  242. }
  243. if ptr := opts.timeout.Value(); ptr != nil {
  244. timeout = *ptr
  245. }
  246. healthConfig = &container.HealthConfig{
  247. Test: test,
  248. Interval: interval,
  249. Timeout: timeout,
  250. Retries: opts.retries,
  251. }
  252. }
  253. return healthConfig, nil
  254. }
  255. // convertExtraHostsToSwarmHosts converts an array of extra hosts in cli
  256. // <host>:<ip>
  257. // into a swarmkit host format:
  258. // IP_address canonical_hostname [aliases...]
  259. // This assumes input value (<host>:<ip>) has already been validated
  260. func convertExtraHostsToSwarmHosts(extraHosts []string) []string {
  261. hosts := []string{}
  262. for _, extraHost := range extraHosts {
  263. parts := strings.SplitN(extraHost, ":", 2)
  264. hosts = append(hosts, fmt.Sprintf("%s %s", parts[1], parts[0]))
  265. }
  266. return hosts
  267. }
  268. type serviceOptions struct {
  269. name string
  270. labels opts.ListOpts
  271. containerLabels opts.ListOpts
  272. image string
  273. args []string
  274. hostname string
  275. env opts.ListOpts
  276. envFile opts.ListOpts
  277. workdir string
  278. user string
  279. groups opts.ListOpts
  280. stopSignal string
  281. tty bool
  282. readOnly bool
  283. mounts opts.MountOpt
  284. dns opts.ListOpts
  285. dnsSearch opts.ListOpts
  286. dnsOption opts.ListOpts
  287. hosts opts.ListOpts
  288. resources resourceOptions
  289. stopGrace DurationOpt
  290. replicas Uint64Opt
  291. mode string
  292. restartPolicy restartPolicyOptions
  293. constraints opts.ListOpts
  294. placementPrefs placementPrefOpts
  295. update updateOptions
  296. rollback updateOptions
  297. networks opts.ListOpts
  298. endpoint endpointOptions
  299. registryAuth bool
  300. logDriver logDriverOptions
  301. healthcheck healthCheckOptions
  302. secrets opts.SecretOpt
  303. }
  304. func newServiceOptions() *serviceOptions {
  305. return &serviceOptions{
  306. labels: opts.NewListOpts(opts.ValidateEnv),
  307. constraints: opts.NewListOpts(nil),
  308. containerLabels: opts.NewListOpts(opts.ValidateEnv),
  309. env: opts.NewListOpts(opts.ValidateEnv),
  310. envFile: opts.NewListOpts(nil),
  311. groups: opts.NewListOpts(nil),
  312. logDriver: newLogDriverOptions(),
  313. dns: opts.NewListOpts(opts.ValidateIPAddress),
  314. dnsOption: opts.NewListOpts(nil),
  315. dnsSearch: opts.NewListOpts(opts.ValidateDNSSearch),
  316. hosts: opts.NewListOpts(opts.ValidateExtraHost),
  317. networks: opts.NewListOpts(nil),
  318. }
  319. }
  320. func (opts *serviceOptions) ToServiceMode() (swarm.ServiceMode, error) {
  321. serviceMode := swarm.ServiceMode{}
  322. switch opts.mode {
  323. case "global":
  324. if opts.replicas.Value() != nil {
  325. return serviceMode, fmt.Errorf("replicas can only be used with replicated mode")
  326. }
  327. serviceMode.Global = &swarm.GlobalService{}
  328. case "replicated":
  329. serviceMode.Replicated = &swarm.ReplicatedService{
  330. Replicas: opts.replicas.Value(),
  331. }
  332. default:
  333. return serviceMode, fmt.Errorf("Unknown mode: %s, only replicated and global supported", opts.mode)
  334. }
  335. return serviceMode, nil
  336. }
  337. func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) {
  338. var service swarm.ServiceSpec
  339. envVariables, err := runconfigopts.ReadKVStrings(opts.envFile.GetAll(), opts.env.GetAll())
  340. if err != nil {
  341. return service, err
  342. }
  343. currentEnv := make([]string, 0, len(envVariables))
  344. for _, env := range envVariables { // need to process each var, in order
  345. k := strings.SplitN(env, "=", 2)[0]
  346. for i, current := range currentEnv { // remove duplicates
  347. if current == env {
  348. continue // no update required, may hide this behind flag to preserve order of envVariables
  349. }
  350. if strings.HasPrefix(current, k+"=") {
  351. currentEnv = append(currentEnv[:i], currentEnv[i+1:]...)
  352. }
  353. }
  354. currentEnv = append(currentEnv, env)
  355. }
  356. healthConfig, err := opts.healthcheck.toHealthConfig()
  357. if err != nil {
  358. return service, err
  359. }
  360. serviceMode, err := opts.ToServiceMode()
  361. if err != nil {
  362. return service, err
  363. }
  364. service = swarm.ServiceSpec{
  365. Annotations: swarm.Annotations{
  366. Name: opts.name,
  367. Labels: runconfigopts.ConvertKVStringsToMap(opts.labels.GetAll()),
  368. },
  369. TaskTemplate: swarm.TaskSpec{
  370. ContainerSpec: swarm.ContainerSpec{
  371. Image: opts.image,
  372. Args: opts.args,
  373. Env: currentEnv,
  374. Hostname: opts.hostname,
  375. Labels: runconfigopts.ConvertKVStringsToMap(opts.containerLabels.GetAll()),
  376. Dir: opts.workdir,
  377. User: opts.user,
  378. Groups: opts.groups.GetAll(),
  379. StopSignal: opts.stopSignal,
  380. TTY: opts.tty,
  381. ReadOnly: opts.readOnly,
  382. Mounts: opts.mounts.Value(),
  383. DNSConfig: &swarm.DNSConfig{
  384. Nameservers: opts.dns.GetAll(),
  385. Search: opts.dnsSearch.GetAll(),
  386. Options: opts.dnsOption.GetAll(),
  387. },
  388. Hosts: convertExtraHostsToSwarmHosts(opts.hosts.GetAll()),
  389. StopGracePeriod: opts.stopGrace.Value(),
  390. Secrets: nil,
  391. Healthcheck: healthConfig,
  392. },
  393. Networks: convertNetworks(opts.networks.GetAll()),
  394. Resources: opts.resources.ToResourceRequirements(),
  395. RestartPolicy: opts.restartPolicy.ToRestartPolicy(),
  396. Placement: &swarm.Placement{
  397. Constraints: opts.constraints.GetAll(),
  398. Preferences: opts.placementPrefs.prefs,
  399. },
  400. LogDriver: opts.logDriver.toLogDriver(),
  401. },
  402. Networks: convertNetworks(opts.networks.GetAll()),
  403. Mode: serviceMode,
  404. UpdateConfig: opts.update.config(),
  405. RollbackConfig: opts.rollback.config(),
  406. EndpointSpec: opts.endpoint.ToEndpointSpec(),
  407. }
  408. return service, nil
  409. }
  410. // addServiceFlags adds all flags that are common to both `create` and `update`.
  411. // Any flags that are not common are added separately in the individual command
  412. func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) {
  413. flags := cmd.Flags()
  414. flags.StringVarP(&opts.workdir, flagWorkdir, "w", "", "Working directory inside the container")
  415. flags.StringVarP(&opts.user, flagUser, "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
  416. flags.StringVar(&opts.hostname, flagHostname, "", "Container hostname")
  417. flags.SetAnnotation(flagHostname, "version", []string{"1.25"})
  418. flags.Var(&opts.resources.limitCPU, flagLimitCPU, "Limit CPUs")
  419. flags.Var(&opts.resources.limitMemBytes, flagLimitMemory, "Limit Memory")
  420. flags.Var(&opts.resources.resCPU, flagReserveCPU, "Reserve CPUs")
  421. flags.Var(&opts.resources.resMemBytes, flagReserveMemory, "Reserve Memory")
  422. flags.Var(&opts.stopGrace, flagStopGracePeriod, "Time to wait before force killing a container (ns|us|ms|s|m|h)")
  423. flags.Var(&opts.replicas, flagReplicas, "Number of tasks")
  424. flags.StringVar(&opts.restartPolicy.condition, flagRestartCondition, "", `Restart when condition is met ("none"|"on-failure"|"any")`)
  425. flags.Var(&opts.restartPolicy.delay, flagRestartDelay, "Delay between restart attempts (ns|us|ms|s|m|h)")
  426. flags.Var(&opts.restartPolicy.maxAttempts, flagRestartMaxAttempts, "Maximum number of restarts before giving up")
  427. flags.Var(&opts.restartPolicy.window, flagRestartWindow, "Window used to evaluate the restart policy (ns|us|ms|s|m|h)")
  428. flags.Uint64Var(&opts.update.parallelism, flagUpdateParallelism, 1, "Maximum number of tasks updated simultaneously (0 to update all at once)")
  429. flags.DurationVar(&opts.update.delay, flagUpdateDelay, time.Duration(0), "Delay between updates (ns|us|ms|s|m|h) (default 0s)")
  430. flags.DurationVar(&opts.update.monitor, flagUpdateMonitor, time.Duration(0), "Duration after each task update to monitor for failure (ns|us|ms|s|m|h) (default 0s)")
  431. flags.SetAnnotation(flagUpdateMonitor, "version", []string{"1.25"})
  432. flags.StringVar(&opts.update.onFailure, flagUpdateFailureAction, "pause", `Action on update failure ("pause"|"continue"|"rollback")`)
  433. flags.Var(&opts.update.maxFailureRatio, flagUpdateMaxFailureRatio, "Failure rate to tolerate during an update")
  434. flags.SetAnnotation(flagUpdateMaxFailureRatio, "version", []string{"1.25"})
  435. flags.Uint64Var(&opts.rollback.parallelism, flagRollbackParallelism, 1, "Maximum number of tasks rolled back simultaneously (0 to roll back all at once)")
  436. flags.SetAnnotation(flagRollbackParallelism, "version", []string{"1.27"})
  437. flags.DurationVar(&opts.rollback.delay, flagRollbackDelay, time.Duration(0), "Delay between task rollbacks (ns|us|ms|s|m|h) (default 0s)")
  438. flags.SetAnnotation(flagRollbackDelay, "version", []string{"1.27"})
  439. flags.DurationVar(&opts.rollback.monitor, flagRollbackMonitor, time.Duration(0), "Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h) (default 0s)")
  440. flags.SetAnnotation(flagRollbackMonitor, "version", []string{"1.27"})
  441. flags.StringVar(&opts.rollback.onFailure, flagRollbackFailureAction, "pause", `Action on rollback failure ("pause"|"continue")`)
  442. flags.SetAnnotation(flagRollbackFailureAction, "version", []string{"1.27"})
  443. flags.Var(&opts.rollback.maxFailureRatio, flagRollbackMaxFailureRatio, "Failure rate to tolerate during a rollback")
  444. flags.SetAnnotation(flagRollbackMaxFailureRatio, "version", []string{"1.27"})
  445. flags.StringVar(&opts.endpoint.mode, flagEndpointMode, "vip", "Endpoint mode (vip or dnsrr)")
  446. flags.BoolVar(&opts.registryAuth, flagRegistryAuth, false, "Send registry authentication details to swarm agents")
  447. flags.StringVar(&opts.logDriver.name, flagLogDriver, "", "Logging driver for service")
  448. flags.Var(&opts.logDriver.opts, flagLogOpt, "Logging driver options")
  449. flags.StringVar(&opts.healthcheck.cmd, flagHealthCmd, "", "Command to run to check health")
  450. flags.SetAnnotation(flagHealthCmd, "version", []string{"1.25"})
  451. flags.Var(&opts.healthcheck.interval, flagHealthInterval, "Time between running the check (ns|us|ms|s|m|h)")
  452. flags.SetAnnotation(flagHealthInterval, "version", []string{"1.25"})
  453. flags.Var(&opts.healthcheck.timeout, flagHealthTimeout, "Maximum time to allow one check to run (ns|us|ms|s|m|h)")
  454. flags.SetAnnotation(flagHealthTimeout, "version", []string{"1.25"})
  455. flags.IntVar(&opts.healthcheck.retries, flagHealthRetries, 0, "Consecutive failures needed to report unhealthy")
  456. flags.SetAnnotation(flagHealthRetries, "version", []string{"1.25"})
  457. flags.BoolVar(&opts.healthcheck.noHealthcheck, flagNoHealthcheck, false, "Disable any container-specified HEALTHCHECK")
  458. flags.SetAnnotation(flagNoHealthcheck, "version", []string{"1.25"})
  459. flags.BoolVarP(&opts.tty, flagTTY, "t", false, "Allocate a pseudo-TTY")
  460. flags.SetAnnotation(flagTTY, "version", []string{"1.25"})
  461. flags.BoolVar(&opts.readOnly, flagReadOnly, false, "Mount the container's root filesystem as read only")
  462. flags.SetAnnotation(flagReadOnly, "version", []string{"1.27"})
  463. flags.StringVar(&opts.stopSignal, flagStopSignal, "", "Signal to stop the container")
  464. flags.SetAnnotation(flagStopSignal, "version", []string{"1.27"})
  465. }
  466. const (
  467. flagPlacementPref = "placement-pref"
  468. flagPlacementPrefAdd = "placement-pref-add"
  469. flagPlacementPrefRemove = "placement-pref-rm"
  470. flagConstraint = "constraint"
  471. flagConstraintRemove = "constraint-rm"
  472. flagConstraintAdd = "constraint-add"
  473. flagContainerLabel = "container-label"
  474. flagContainerLabelRemove = "container-label-rm"
  475. flagContainerLabelAdd = "container-label-add"
  476. flagDNS = "dns"
  477. flagDNSRemove = "dns-rm"
  478. flagDNSAdd = "dns-add"
  479. flagDNSOption = "dns-option"
  480. flagDNSOptionRemove = "dns-option-rm"
  481. flagDNSOptionAdd = "dns-option-add"
  482. flagDNSSearch = "dns-search"
  483. flagDNSSearchRemove = "dns-search-rm"
  484. flagDNSSearchAdd = "dns-search-add"
  485. flagEndpointMode = "endpoint-mode"
  486. flagHost = "host"
  487. flagHostAdd = "host-add"
  488. flagHostRemove = "host-rm"
  489. flagHostname = "hostname"
  490. flagEnv = "env"
  491. flagEnvFile = "env-file"
  492. flagEnvRemove = "env-rm"
  493. flagEnvAdd = "env-add"
  494. flagGroup = "group"
  495. flagGroupAdd = "group-add"
  496. flagGroupRemove = "group-rm"
  497. flagLabel = "label"
  498. flagLabelRemove = "label-rm"
  499. flagLabelAdd = "label-add"
  500. flagLimitCPU = "limit-cpu"
  501. flagLimitMemory = "limit-memory"
  502. flagMode = "mode"
  503. flagMount = "mount"
  504. flagMountRemove = "mount-rm"
  505. flagMountAdd = "mount-add"
  506. flagName = "name"
  507. flagNetwork = "network"
  508. flagPublish = "publish"
  509. flagPublishRemove = "publish-rm"
  510. flagPublishAdd = "publish-add"
  511. flagReadOnly = "read-only"
  512. flagReplicas = "replicas"
  513. flagReserveCPU = "reserve-cpu"
  514. flagReserveMemory = "reserve-memory"
  515. flagRestartCondition = "restart-condition"
  516. flagRestartDelay = "restart-delay"
  517. flagRestartMaxAttempts = "restart-max-attempts"
  518. flagRestartWindow = "restart-window"
  519. flagRollbackDelay = "rollback-delay"
  520. flagRollbackFailureAction = "rollback-failure-action"
  521. flagRollbackMaxFailureRatio = "rollback-max-failure-ratio"
  522. flagRollbackMonitor = "rollback-monitor"
  523. flagRollbackParallelism = "rollback-parallelism"
  524. flagStopGracePeriod = "stop-grace-period"
  525. flagStopSignal = "stop-signal"
  526. flagTTY = "tty"
  527. flagUpdateDelay = "update-delay"
  528. flagUpdateFailureAction = "update-failure-action"
  529. flagUpdateMaxFailureRatio = "update-max-failure-ratio"
  530. flagUpdateMonitor = "update-monitor"
  531. flagUpdateParallelism = "update-parallelism"
  532. flagUser = "user"
  533. flagWorkdir = "workdir"
  534. flagRegistryAuth = "with-registry-auth"
  535. flagLogDriver = "log-driver"
  536. flagLogOpt = "log-opt"
  537. flagHealthCmd = "health-cmd"
  538. flagHealthInterval = "health-interval"
  539. flagHealthRetries = "health-retries"
  540. flagHealthTimeout = "health-timeout"
  541. flagNoHealthcheck = "no-healthcheck"
  542. flagSecret = "secret"
  543. flagSecretAdd = "secret-add"
  544. flagSecretRemove = "secret-rm"
  545. )