update.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. package service
  2. import (
  3. "errors"
  4. "fmt"
  5. "sort"
  6. "strings"
  7. "time"
  8. "github.com/docker/docker/api/types"
  9. "github.com/docker/docker/api/types/container"
  10. mounttypes "github.com/docker/docker/api/types/mount"
  11. "github.com/docker/docker/api/types/swarm"
  12. "github.com/docker/docker/api/types/versions"
  13. "github.com/docker/docker/cli"
  14. "github.com/docker/docker/cli/command"
  15. "github.com/docker/docker/client"
  16. "github.com/docker/docker/opts"
  17. runconfigopts "github.com/docker/docker/runconfig/opts"
  18. "github.com/docker/go-connections/nat"
  19. shlex "github.com/flynn-archive/go-shlex"
  20. "github.com/spf13/cobra"
  21. "github.com/spf13/pflag"
  22. "golang.org/x/net/context"
  23. )
  24. func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
  25. serviceOpts := newServiceOptions()
  26. cmd := &cobra.Command{
  27. Use: "update [OPTIONS] SERVICE",
  28. Short: "Update a service",
  29. Args: cli.ExactArgs(1),
  30. RunE: func(cmd *cobra.Command, args []string) error {
  31. return runUpdate(dockerCli, cmd.Flags(), args[0])
  32. },
  33. }
  34. flags := cmd.Flags()
  35. flags.String("image", "", "Service image tag")
  36. flags.String("args", "", "Service command args")
  37. flags.Bool("rollback", false, "Rollback to previous specification")
  38. flags.SetAnnotation("rollback", "version", []string{"1.25"})
  39. flags.Bool("force", false, "Force update even if no changes require it")
  40. flags.SetAnnotation("force", "version", []string{"1.25"})
  41. addServiceFlags(cmd, serviceOpts)
  42. flags.Var(newListOptsVar(), flagEnvRemove, "Remove an environment variable")
  43. flags.Var(newListOptsVar(), flagGroupRemove, "Remove a previously added supplementary user group from the container")
  44. flags.SetAnnotation(flagGroupRemove, "version", []string{"1.25"})
  45. flags.Var(newListOptsVar(), flagLabelRemove, "Remove a label by its key")
  46. flags.Var(newListOptsVar(), flagContainerLabelRemove, "Remove a container label by its key")
  47. flags.Var(newListOptsVar(), flagMountRemove, "Remove a mount by its target path")
  48. // flags.Var(newListOptsVar().WithValidator(validatePublishRemove), flagPublishRemove, "Remove a published port by its target port")
  49. flags.Var(&opts.PortOpt{}, flagPublishRemove, "Remove a published port by its target port")
  50. flags.Var(newListOptsVar(), flagConstraintRemove, "Remove a constraint")
  51. flags.Var(newListOptsVar(), flagDNSRemove, "Remove a custom DNS server")
  52. flags.SetAnnotation(flagDNSRemove, "version", []string{"1.25"})
  53. flags.Var(newListOptsVar(), flagDNSOptionRemove, "Remove a DNS option")
  54. flags.SetAnnotation(flagDNSOptionRemove, "version", []string{"1.25"})
  55. flags.Var(newListOptsVar(), flagDNSSearchRemove, "Remove a DNS search domain")
  56. flags.SetAnnotation(flagDNSSearchRemove, "version", []string{"1.25"})
  57. flags.Var(newListOptsVar(), flagHostRemove, "Remove a custom host-to-IP mapping (host:ip)")
  58. flags.SetAnnotation(flagHostRemove, "version", []string{"1.25"})
  59. flags.Var(&serviceOpts.labels, flagLabelAdd, "Add or update a service label")
  60. flags.Var(&serviceOpts.containerLabels, flagContainerLabelAdd, "Add or update a container label")
  61. flags.Var(&serviceOpts.env, flagEnvAdd, "Add or update an environment variable")
  62. flags.Var(newListOptsVar(), flagSecretRemove, "Remove a secret")
  63. flags.SetAnnotation(flagSecretRemove, "version", []string{"1.25"})
  64. flags.Var(&serviceOpts.secrets, flagSecretAdd, "Add or update a secret on a service")
  65. flags.SetAnnotation(flagSecretAdd, "version", []string{"1.25"})
  66. flags.Var(&serviceOpts.mounts, flagMountAdd, "Add or update a mount on a service")
  67. flags.Var(&serviceOpts.constraints, flagConstraintAdd, "Add or update a placement constraint")
  68. flags.Var(&serviceOpts.placementPrefs, flagPlacementPrefAdd, "Add a placement preference")
  69. flags.SetAnnotation(flagPlacementPrefAdd, "version", []string{"1.28"})
  70. flags.Var(&placementPrefOpts{}, flagPlacementPrefRemove, "Remove a placement preference")
  71. flags.SetAnnotation(flagPlacementPrefRemove, "version", []string{"1.28"})
  72. flags.Var(&serviceOpts.endpoint.publishPorts, flagPublishAdd, "Add or update a published port")
  73. flags.Var(&serviceOpts.groups, flagGroupAdd, "Add an additional supplementary user group to the container")
  74. flags.SetAnnotation(flagGroupAdd, "version", []string{"1.25"})
  75. flags.Var(&serviceOpts.dns, flagDNSAdd, "Add or update a custom DNS server")
  76. flags.SetAnnotation(flagDNSAdd, "version", []string{"1.25"})
  77. flags.Var(&serviceOpts.dnsOption, flagDNSOptionAdd, "Add or update a DNS option")
  78. flags.SetAnnotation(flagDNSOptionAdd, "version", []string{"1.25"})
  79. flags.Var(&serviceOpts.dnsSearch, flagDNSSearchAdd, "Add or update a custom DNS search domain")
  80. flags.SetAnnotation(flagDNSSearchAdd, "version", []string{"1.25"})
  81. flags.Var(&serviceOpts.hosts, flagHostAdd, "Add or update a custom host-to-IP mapping (host:ip)")
  82. flags.SetAnnotation(flagHostAdd, "version", []string{"1.25"})
  83. return cmd
  84. }
  85. func newListOptsVar() *opts.ListOpts {
  86. return opts.NewListOptsRef(&[]string{}, nil)
  87. }
  88. func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, serviceID string) error {
  89. apiClient := dockerCli.Client()
  90. ctx := context.Background()
  91. service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID)
  92. if err != nil {
  93. return err
  94. }
  95. rollback, err := flags.GetBool("rollback")
  96. if err != nil {
  97. return err
  98. }
  99. // There are two ways to do user-requested rollback. The old way is
  100. // client-side, but with a sufficiently recent daemon we prefer
  101. // server-side, because it will honor the rollback parameters.
  102. var (
  103. clientSideRollback bool
  104. serverSideRollback bool
  105. )
  106. spec := &service.Spec
  107. if rollback {
  108. // Rollback can't be combined with other flags.
  109. otherFlagsPassed := false
  110. flags.VisitAll(func(f *pflag.Flag) {
  111. if f.Name == "rollback" {
  112. return
  113. }
  114. if flags.Changed(f.Name) {
  115. otherFlagsPassed = true
  116. }
  117. })
  118. if otherFlagsPassed {
  119. return errors.New("other flags may not be combined with --rollback")
  120. }
  121. if versions.LessThan(dockerCli.Client().ClientVersion(), "1.28") {
  122. clientSideRollback = true
  123. spec = service.PreviousSpec
  124. if spec == nil {
  125. return fmt.Errorf("service does not have a previous specification to roll back to")
  126. }
  127. } else {
  128. serverSideRollback = true
  129. }
  130. }
  131. updateOpts := types.ServiceUpdateOptions{}
  132. if serverSideRollback {
  133. updateOpts.Rollback = "previous"
  134. }
  135. err = updateService(flags, spec)
  136. if err != nil {
  137. return err
  138. }
  139. if flags.Changed("image") {
  140. if err := resolveServiceImageDigest(dockerCli, spec); err != nil {
  141. return err
  142. }
  143. }
  144. updatedSecrets, err := getUpdatedSecrets(apiClient, flags, spec.TaskTemplate.ContainerSpec.Secrets)
  145. if err != nil {
  146. return err
  147. }
  148. spec.TaskTemplate.ContainerSpec.Secrets = updatedSecrets
  149. // only send auth if flag was set
  150. sendAuth, err := flags.GetBool(flagRegistryAuth)
  151. if err != nil {
  152. return err
  153. }
  154. if sendAuth {
  155. // Retrieve encoded auth token from the image reference
  156. // This would be the old image if it didn't change in this update
  157. image := spec.TaskTemplate.ContainerSpec.Image
  158. encodedAuth, err := command.RetrieveAuthTokenFromImage(ctx, dockerCli, image)
  159. if err != nil {
  160. return err
  161. }
  162. updateOpts.EncodedRegistryAuth = encodedAuth
  163. } else if clientSideRollback {
  164. updateOpts.RegistryAuthFrom = types.RegistryAuthFromPreviousSpec
  165. } else {
  166. updateOpts.RegistryAuthFrom = types.RegistryAuthFromSpec
  167. }
  168. response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, *spec, updateOpts)
  169. if err != nil {
  170. return err
  171. }
  172. for _, warning := range response.Warnings {
  173. fmt.Fprintln(dockerCli.Err(), warning)
  174. }
  175. fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID)
  176. return nil
  177. }
  178. func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
  179. updateString := func(flag string, field *string) {
  180. if flags.Changed(flag) {
  181. *field, _ = flags.GetString(flag)
  182. }
  183. }
  184. updateInt64Value := func(flag string, field *int64) {
  185. if flags.Changed(flag) {
  186. *field = flags.Lookup(flag).Value.(int64Value).Value()
  187. }
  188. }
  189. updateFloatValue := func(flag string, field *float32) {
  190. if flags.Changed(flag) {
  191. *field = flags.Lookup(flag).Value.(*floatValue).Value()
  192. }
  193. }
  194. updateDuration := func(flag string, field *time.Duration) {
  195. if flags.Changed(flag) {
  196. *field, _ = flags.GetDuration(flag)
  197. }
  198. }
  199. updateDurationOpt := func(flag string, field **time.Duration) {
  200. if flags.Changed(flag) {
  201. val := *flags.Lookup(flag).Value.(*DurationOpt).Value()
  202. *field = &val
  203. }
  204. }
  205. updateUint64 := func(flag string, field *uint64) {
  206. if flags.Changed(flag) {
  207. *field, _ = flags.GetUint64(flag)
  208. }
  209. }
  210. updateUint64Opt := func(flag string, field **uint64) {
  211. if flags.Changed(flag) {
  212. val := *flags.Lookup(flag).Value.(*Uint64Opt).Value()
  213. *field = &val
  214. }
  215. }
  216. cspec := &spec.TaskTemplate.ContainerSpec
  217. task := &spec.TaskTemplate
  218. taskResources := func() *swarm.ResourceRequirements {
  219. if task.Resources == nil {
  220. task.Resources = &swarm.ResourceRequirements{}
  221. }
  222. return task.Resources
  223. }
  224. updateLabels(flags, &spec.Labels)
  225. updateContainerLabels(flags, &cspec.Labels)
  226. updateString("image", &cspec.Image)
  227. updateStringToSlice(flags, "args", &cspec.Args)
  228. updateEnvironment(flags, &cspec.Env)
  229. updateString(flagWorkdir, &cspec.Dir)
  230. updateString(flagUser, &cspec.User)
  231. updateString(flagHostname, &cspec.Hostname)
  232. if err := updateMounts(flags, &cspec.Mounts); err != nil {
  233. return err
  234. }
  235. if flags.Changed(flagLimitCPU) || flags.Changed(flagLimitMemory) {
  236. taskResources().Limits = &swarm.Resources{}
  237. updateInt64Value(flagLimitCPU, &task.Resources.Limits.NanoCPUs)
  238. updateInt64Value(flagLimitMemory, &task.Resources.Limits.MemoryBytes)
  239. }
  240. if flags.Changed(flagReserveCPU) || flags.Changed(flagReserveMemory) {
  241. taskResources().Reservations = &swarm.Resources{}
  242. updateInt64Value(flagReserveCPU, &task.Resources.Reservations.NanoCPUs)
  243. updateInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes)
  244. }
  245. updateDurationOpt(flagStopGracePeriod, &cspec.StopGracePeriod)
  246. if anyChanged(flags, flagRestartCondition, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow) {
  247. if task.RestartPolicy == nil {
  248. task.RestartPolicy = &swarm.RestartPolicy{}
  249. }
  250. if flags.Changed(flagRestartCondition) {
  251. value, _ := flags.GetString(flagRestartCondition)
  252. task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value)
  253. }
  254. updateDurationOpt(flagRestartDelay, &task.RestartPolicy.Delay)
  255. updateUint64Opt(flagRestartMaxAttempts, &task.RestartPolicy.MaxAttempts)
  256. updateDurationOpt(flagRestartWindow, &task.RestartPolicy.Window)
  257. }
  258. if anyChanged(flags, flagConstraintAdd, flagConstraintRemove) {
  259. if task.Placement == nil {
  260. task.Placement = &swarm.Placement{}
  261. }
  262. updatePlacementConstraints(flags, task.Placement)
  263. }
  264. if anyChanged(flags, flagPlacementPrefAdd, flagPlacementPrefRemove) {
  265. if task.Placement == nil {
  266. task.Placement = &swarm.Placement{}
  267. }
  268. updatePlacementPreferences(flags, task.Placement)
  269. }
  270. if err := updateReplicas(flags, &spec.Mode); err != nil {
  271. return err
  272. }
  273. if anyChanged(flags, flagUpdateParallelism, flagUpdateDelay, flagUpdateMonitor, flagUpdateFailureAction, flagUpdateMaxFailureRatio) {
  274. if spec.UpdateConfig == nil {
  275. spec.UpdateConfig = &swarm.UpdateConfig{}
  276. }
  277. updateUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism)
  278. updateDuration(flagUpdateDelay, &spec.UpdateConfig.Delay)
  279. updateDuration(flagUpdateMonitor, &spec.UpdateConfig.Monitor)
  280. updateString(flagUpdateFailureAction, &spec.UpdateConfig.FailureAction)
  281. updateFloatValue(flagUpdateMaxFailureRatio, &spec.UpdateConfig.MaxFailureRatio)
  282. }
  283. if anyChanged(flags, flagRollbackParallelism, flagRollbackDelay, flagRollbackMonitor, flagRollbackFailureAction, flagRollbackMaxFailureRatio) {
  284. if spec.RollbackConfig == nil {
  285. spec.RollbackConfig = &swarm.UpdateConfig{}
  286. }
  287. updateUint64(flagRollbackParallelism, &spec.RollbackConfig.Parallelism)
  288. updateDuration(flagRollbackDelay, &spec.RollbackConfig.Delay)
  289. updateDuration(flagRollbackMonitor, &spec.RollbackConfig.Monitor)
  290. updateString(flagRollbackFailureAction, &spec.RollbackConfig.FailureAction)
  291. updateFloatValue(flagRollbackMaxFailureRatio, &spec.RollbackConfig.MaxFailureRatio)
  292. }
  293. if flags.Changed(flagEndpointMode) {
  294. value, _ := flags.GetString(flagEndpointMode)
  295. if spec.EndpointSpec == nil {
  296. spec.EndpointSpec = &swarm.EndpointSpec{}
  297. }
  298. spec.EndpointSpec.Mode = swarm.ResolutionMode(value)
  299. }
  300. if anyChanged(flags, flagGroupAdd, flagGroupRemove) {
  301. if err := updateGroups(flags, &cspec.Groups); err != nil {
  302. return err
  303. }
  304. }
  305. if anyChanged(flags, flagPublishAdd, flagPublishRemove) {
  306. if spec.EndpointSpec == nil {
  307. spec.EndpointSpec = &swarm.EndpointSpec{}
  308. }
  309. if err := updatePorts(flags, &spec.EndpointSpec.Ports); err != nil {
  310. return err
  311. }
  312. }
  313. if anyChanged(flags, flagDNSAdd, flagDNSRemove, flagDNSOptionAdd, flagDNSOptionRemove, flagDNSSearchAdd, flagDNSSearchRemove) {
  314. if cspec.DNSConfig == nil {
  315. cspec.DNSConfig = &swarm.DNSConfig{}
  316. }
  317. if err := updateDNSConfig(flags, &cspec.DNSConfig); err != nil {
  318. return err
  319. }
  320. }
  321. if anyChanged(flags, flagHostAdd, flagHostRemove) {
  322. if err := updateHosts(flags, &cspec.Hosts); err != nil {
  323. return err
  324. }
  325. }
  326. if err := updateLogDriver(flags, &spec.TaskTemplate); err != nil {
  327. return err
  328. }
  329. force, err := flags.GetBool("force")
  330. if err != nil {
  331. return err
  332. }
  333. if force {
  334. spec.TaskTemplate.ForceUpdate++
  335. }
  336. if err := updateHealthcheck(flags, cspec); err != nil {
  337. return err
  338. }
  339. if flags.Changed(flagTTY) {
  340. tty, err := flags.GetBool(flagTTY)
  341. if err != nil {
  342. return err
  343. }
  344. cspec.TTY = tty
  345. }
  346. if flags.Changed(flagReadOnly) {
  347. readOnly, err := flags.GetBool(flagReadOnly)
  348. if err != nil {
  349. return err
  350. }
  351. cspec.ReadOnly = readOnly
  352. }
  353. updateString(flagStopSignal, &cspec.StopSignal)
  354. return nil
  355. }
  356. func updateStringToSlice(flags *pflag.FlagSet, flag string, field *[]string) error {
  357. if !flags.Changed(flag) {
  358. return nil
  359. }
  360. value, _ := flags.GetString(flag)
  361. valueSlice, err := shlex.Split(value)
  362. *field = valueSlice
  363. return err
  364. }
  365. func anyChanged(flags *pflag.FlagSet, fields ...string) bool {
  366. for _, flag := range fields {
  367. if flags.Changed(flag) {
  368. return true
  369. }
  370. }
  371. return false
  372. }
  373. func updatePlacementConstraints(flags *pflag.FlagSet, placement *swarm.Placement) {
  374. if flags.Changed(flagConstraintAdd) {
  375. values := flags.Lookup(flagConstraintAdd).Value.(*opts.ListOpts).GetAll()
  376. placement.Constraints = append(placement.Constraints, values...)
  377. }
  378. toRemove := buildToRemoveSet(flags, flagConstraintRemove)
  379. newConstraints := []string{}
  380. for _, constraint := range placement.Constraints {
  381. if _, exists := toRemove[constraint]; !exists {
  382. newConstraints = append(newConstraints, constraint)
  383. }
  384. }
  385. // Sort so that result is predictable.
  386. sort.Strings(newConstraints)
  387. placement.Constraints = newConstraints
  388. }
  389. func updatePlacementPreferences(flags *pflag.FlagSet, placement *swarm.Placement) {
  390. var newPrefs []swarm.PlacementPreference
  391. if flags.Changed(flagPlacementPrefRemove) {
  392. for _, existing := range placement.Preferences {
  393. removed := false
  394. for _, removal := range flags.Lookup(flagPlacementPrefRemove).Value.(*placementPrefOpts).prefs {
  395. if removal.Spread != nil && existing.Spread != nil && removal.Spread.SpreadDescriptor == existing.Spread.SpreadDescriptor {
  396. removed = true
  397. break
  398. }
  399. }
  400. if !removed {
  401. newPrefs = append(newPrefs, existing)
  402. }
  403. }
  404. } else {
  405. newPrefs = placement.Preferences
  406. }
  407. if flags.Changed(flagPlacementPrefAdd) {
  408. for _, addition := range flags.Lookup(flagPlacementPrefAdd).Value.(*placementPrefOpts).prefs {
  409. newPrefs = append(newPrefs, addition)
  410. }
  411. }
  412. placement.Preferences = newPrefs
  413. }
  414. func updateContainerLabels(flags *pflag.FlagSet, field *map[string]string) {
  415. if flags.Changed(flagContainerLabelAdd) {
  416. if *field == nil {
  417. *field = map[string]string{}
  418. }
  419. values := flags.Lookup(flagContainerLabelAdd).Value.(*opts.ListOpts).GetAll()
  420. for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
  421. (*field)[key] = value
  422. }
  423. }
  424. if *field != nil && flags.Changed(flagContainerLabelRemove) {
  425. toRemove := flags.Lookup(flagContainerLabelRemove).Value.(*opts.ListOpts).GetAll()
  426. for _, label := range toRemove {
  427. delete(*field, label)
  428. }
  429. }
  430. }
  431. func updateLabels(flags *pflag.FlagSet, field *map[string]string) {
  432. if flags.Changed(flagLabelAdd) {
  433. if *field == nil {
  434. *field = map[string]string{}
  435. }
  436. values := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll()
  437. for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
  438. (*field)[key] = value
  439. }
  440. }
  441. if *field != nil && flags.Changed(flagLabelRemove) {
  442. toRemove := flags.Lookup(flagLabelRemove).Value.(*opts.ListOpts).GetAll()
  443. for _, label := range toRemove {
  444. delete(*field, label)
  445. }
  446. }
  447. }
  448. func updateEnvironment(flags *pflag.FlagSet, field *[]string) {
  449. envSet := map[string]string{}
  450. for _, v := range *field {
  451. envSet[envKey(v)] = v
  452. }
  453. if flags.Changed(flagEnvAdd) {
  454. value := flags.Lookup(flagEnvAdd).Value.(*opts.ListOpts)
  455. for _, v := range value.GetAll() {
  456. envSet[envKey(v)] = v
  457. }
  458. }
  459. *field = []string{}
  460. for _, v := range envSet {
  461. *field = append(*field, v)
  462. }
  463. toRemove := buildToRemoveSet(flags, flagEnvRemove)
  464. *field = removeItems(*field, toRemove, envKey)
  465. }
  466. func getUpdatedSecrets(apiClient client.SecretAPIClient, flags *pflag.FlagSet, secrets []*swarm.SecretReference) ([]*swarm.SecretReference, error) {
  467. newSecrets := []*swarm.SecretReference{}
  468. toRemove := buildToRemoveSet(flags, flagSecretRemove)
  469. for _, secret := range secrets {
  470. if _, exists := toRemove[secret.SecretName]; !exists {
  471. newSecrets = append(newSecrets, secret)
  472. }
  473. }
  474. if flags.Changed(flagSecretAdd) {
  475. values := flags.Lookup(flagSecretAdd).Value.(*opts.SecretOpt).Value()
  476. addSecrets, err := ParseSecrets(apiClient, values)
  477. if err != nil {
  478. return nil, err
  479. }
  480. newSecrets = append(newSecrets, addSecrets...)
  481. }
  482. return newSecrets, nil
  483. }
  484. func envKey(value string) string {
  485. kv := strings.SplitN(value, "=", 2)
  486. return kv[0]
  487. }
  488. func itemKey(value string) string {
  489. return value
  490. }
  491. func buildToRemoveSet(flags *pflag.FlagSet, flag string) map[string]struct{} {
  492. var empty struct{}
  493. toRemove := make(map[string]struct{})
  494. if !flags.Changed(flag) {
  495. return toRemove
  496. }
  497. toRemoveSlice := flags.Lookup(flag).Value.(*opts.ListOpts).GetAll()
  498. for _, key := range toRemoveSlice {
  499. toRemove[key] = empty
  500. }
  501. return toRemove
  502. }
  503. func removeItems(
  504. seq []string,
  505. toRemove map[string]struct{},
  506. keyFunc func(string) string,
  507. ) []string {
  508. newSeq := []string{}
  509. for _, item := range seq {
  510. if _, exists := toRemove[keyFunc(item)]; !exists {
  511. newSeq = append(newSeq, item)
  512. }
  513. }
  514. return newSeq
  515. }
  516. type byMountSource []mounttypes.Mount
  517. func (m byMountSource) Len() int { return len(m) }
  518. func (m byMountSource) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
  519. func (m byMountSource) Less(i, j int) bool {
  520. a, b := m[i], m[j]
  521. if a.Source == b.Source {
  522. return a.Target < b.Target
  523. }
  524. return a.Source < b.Source
  525. }
  526. func updateMounts(flags *pflag.FlagSet, mounts *[]mounttypes.Mount) error {
  527. mountsByTarget := map[string]mounttypes.Mount{}
  528. if flags.Changed(flagMountAdd) {
  529. values := flags.Lookup(flagMountAdd).Value.(*opts.MountOpt).Value()
  530. for _, mount := range values {
  531. if _, ok := mountsByTarget[mount.Target]; ok {
  532. return fmt.Errorf("duplicate mount target")
  533. }
  534. mountsByTarget[mount.Target] = mount
  535. }
  536. }
  537. // Add old list of mount points minus updated one.
  538. for _, mount := range *mounts {
  539. if _, ok := mountsByTarget[mount.Target]; !ok {
  540. mountsByTarget[mount.Target] = mount
  541. }
  542. }
  543. newMounts := []mounttypes.Mount{}
  544. toRemove := buildToRemoveSet(flags, flagMountRemove)
  545. for _, mount := range mountsByTarget {
  546. if _, exists := toRemove[mount.Target]; !exists {
  547. newMounts = append(newMounts, mount)
  548. }
  549. }
  550. sort.Sort(byMountSource(newMounts))
  551. *mounts = newMounts
  552. return nil
  553. }
  554. func updateGroups(flags *pflag.FlagSet, groups *[]string) error {
  555. if flags.Changed(flagGroupAdd) {
  556. values := flags.Lookup(flagGroupAdd).Value.(*opts.ListOpts).GetAll()
  557. *groups = append(*groups, values...)
  558. }
  559. toRemove := buildToRemoveSet(flags, flagGroupRemove)
  560. newGroups := []string{}
  561. for _, group := range *groups {
  562. if _, exists := toRemove[group]; !exists {
  563. newGroups = append(newGroups, group)
  564. }
  565. }
  566. // Sort so that result is predictable.
  567. sort.Strings(newGroups)
  568. *groups = newGroups
  569. return nil
  570. }
  571. func removeDuplicates(entries []string) []string {
  572. hit := map[string]bool{}
  573. newEntries := []string{}
  574. for _, v := range entries {
  575. if !hit[v] {
  576. newEntries = append(newEntries, v)
  577. hit[v] = true
  578. }
  579. }
  580. return newEntries
  581. }
  582. func updateDNSConfig(flags *pflag.FlagSet, config **swarm.DNSConfig) error {
  583. newConfig := &swarm.DNSConfig{}
  584. nameservers := (*config).Nameservers
  585. if flags.Changed(flagDNSAdd) {
  586. values := flags.Lookup(flagDNSAdd).Value.(*opts.ListOpts).GetAll()
  587. nameservers = append(nameservers, values...)
  588. }
  589. nameservers = removeDuplicates(nameservers)
  590. toRemove := buildToRemoveSet(flags, flagDNSRemove)
  591. for _, nameserver := range nameservers {
  592. if _, exists := toRemove[nameserver]; !exists {
  593. newConfig.Nameservers = append(newConfig.Nameservers, nameserver)
  594. }
  595. }
  596. // Sort so that result is predictable.
  597. sort.Strings(newConfig.Nameservers)
  598. search := (*config).Search
  599. if flags.Changed(flagDNSSearchAdd) {
  600. values := flags.Lookup(flagDNSSearchAdd).Value.(*opts.ListOpts).GetAll()
  601. search = append(search, values...)
  602. }
  603. search = removeDuplicates(search)
  604. toRemove = buildToRemoveSet(flags, flagDNSSearchRemove)
  605. for _, entry := range search {
  606. if _, exists := toRemove[entry]; !exists {
  607. newConfig.Search = append(newConfig.Search, entry)
  608. }
  609. }
  610. // Sort so that result is predictable.
  611. sort.Strings(newConfig.Search)
  612. options := (*config).Options
  613. if flags.Changed(flagDNSOptionAdd) {
  614. values := flags.Lookup(flagDNSOptionAdd).Value.(*opts.ListOpts).GetAll()
  615. options = append(options, values...)
  616. }
  617. options = removeDuplicates(options)
  618. toRemove = buildToRemoveSet(flags, flagDNSOptionRemove)
  619. for _, option := range options {
  620. if _, exists := toRemove[option]; !exists {
  621. newConfig.Options = append(newConfig.Options, option)
  622. }
  623. }
  624. // Sort so that result is predictable.
  625. sort.Strings(newConfig.Options)
  626. *config = newConfig
  627. return nil
  628. }
  629. type byPortConfig []swarm.PortConfig
  630. func (r byPortConfig) Len() int { return len(r) }
  631. func (r byPortConfig) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
  632. func (r byPortConfig) Less(i, j int) bool {
  633. // We convert PortConfig into `port/protocol`, e.g., `80/tcp`
  634. // In updatePorts we already filter out with map so there is duplicate entries
  635. return portConfigToString(&r[i]) < portConfigToString(&r[j])
  636. }
  637. func portConfigToString(portConfig *swarm.PortConfig) string {
  638. protocol := portConfig.Protocol
  639. mode := portConfig.PublishMode
  640. return fmt.Sprintf("%v:%v/%s/%s", portConfig.PublishedPort, portConfig.TargetPort, protocol, mode)
  641. }
  642. func updatePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) error {
  643. // The key of the map is `port/protocol`, e.g., `80/tcp`
  644. portSet := map[string]swarm.PortConfig{}
  645. // Build the current list of portConfig
  646. for _, entry := range *portConfig {
  647. if _, ok := portSet[portConfigToString(&entry)]; !ok {
  648. portSet[portConfigToString(&entry)] = entry
  649. }
  650. }
  651. newPorts := []swarm.PortConfig{}
  652. // Clean current ports
  653. toRemove := flags.Lookup(flagPublishRemove).Value.(*opts.PortOpt).Value()
  654. portLoop:
  655. for _, port := range portSet {
  656. for _, pConfig := range toRemove {
  657. if equalProtocol(port.Protocol, pConfig.Protocol) &&
  658. port.TargetPort == pConfig.TargetPort &&
  659. equalPublishMode(port.PublishMode, pConfig.PublishMode) {
  660. continue portLoop
  661. }
  662. }
  663. newPorts = append(newPorts, port)
  664. }
  665. // Check to see if there are any conflict in flags.
  666. if flags.Changed(flagPublishAdd) {
  667. ports := flags.Lookup(flagPublishAdd).Value.(*opts.PortOpt).Value()
  668. for _, port := range ports {
  669. if _, ok := portSet[portConfigToString(&port)]; ok {
  670. continue
  671. }
  672. //portSet[portConfigToString(&port)] = port
  673. newPorts = append(newPorts, port)
  674. }
  675. }
  676. // Sort the PortConfig to avoid unnecessary updates
  677. sort.Sort(byPortConfig(newPorts))
  678. *portConfig = newPorts
  679. return nil
  680. }
  681. func equalProtocol(prot1, prot2 swarm.PortConfigProtocol) bool {
  682. return prot1 == prot2 ||
  683. (prot1 == swarm.PortConfigProtocol("") && prot2 == swarm.PortConfigProtocolTCP) ||
  684. (prot2 == swarm.PortConfigProtocol("") && prot1 == swarm.PortConfigProtocolTCP)
  685. }
  686. func equalPublishMode(mode1, mode2 swarm.PortConfigPublishMode) bool {
  687. return mode1 == mode2 ||
  688. (mode1 == swarm.PortConfigPublishMode("") && mode2 == swarm.PortConfigPublishModeIngress) ||
  689. (mode2 == swarm.PortConfigPublishMode("") && mode1 == swarm.PortConfigPublishModeIngress)
  690. }
  691. func equalPort(targetPort nat.Port, port swarm.PortConfig) bool {
  692. return (string(port.Protocol) == targetPort.Proto() &&
  693. port.TargetPort == uint32(targetPort.Int()))
  694. }
  695. func updateReplicas(flags *pflag.FlagSet, serviceMode *swarm.ServiceMode) error {
  696. if !flags.Changed(flagReplicas) {
  697. return nil
  698. }
  699. if serviceMode == nil || serviceMode.Replicated == nil {
  700. return fmt.Errorf("replicas can only be used with replicated mode")
  701. }
  702. serviceMode.Replicated.Replicas = flags.Lookup(flagReplicas).Value.(*Uint64Opt).Value()
  703. return nil
  704. }
  705. func updateHosts(flags *pflag.FlagSet, hosts *[]string) error {
  706. // Combine existing Hosts (in swarmkit format) with the host to add (convert to swarmkit format)
  707. if flags.Changed(flagHostAdd) {
  708. values := convertExtraHostsToSwarmHosts(flags.Lookup(flagHostAdd).Value.(*opts.ListOpts).GetAll())
  709. *hosts = append(*hosts, values...)
  710. }
  711. // Remove duplicate
  712. *hosts = removeDuplicates(*hosts)
  713. keysToRemove := make(map[string]struct{})
  714. if flags.Changed(flagHostRemove) {
  715. var empty struct{}
  716. extraHostsToRemove := flags.Lookup(flagHostRemove).Value.(*opts.ListOpts).GetAll()
  717. for _, entry := range extraHostsToRemove {
  718. key := strings.SplitN(entry, ":", 2)[0]
  719. keysToRemove[key] = empty
  720. }
  721. }
  722. newHosts := []string{}
  723. for _, entry := range *hosts {
  724. // Since this is in swarmkit format, we need to find the key, which is canonical_hostname of:
  725. // IP_address canonical_hostname [aliases...]
  726. parts := strings.Fields(entry)
  727. if len(parts) > 1 {
  728. key := parts[1]
  729. if _, exists := keysToRemove[key]; !exists {
  730. newHosts = append(newHosts, entry)
  731. }
  732. } else {
  733. newHosts = append(newHosts, entry)
  734. }
  735. }
  736. // Sort so that result is predictable.
  737. sort.Strings(newHosts)
  738. *hosts = newHosts
  739. return nil
  740. }
  741. // updateLogDriver updates the log driver only if the log driver flag is set.
  742. // All options will be replaced with those provided on the command line.
  743. func updateLogDriver(flags *pflag.FlagSet, taskTemplate *swarm.TaskSpec) error {
  744. if !flags.Changed(flagLogDriver) {
  745. return nil
  746. }
  747. name, err := flags.GetString(flagLogDriver)
  748. if err != nil {
  749. return err
  750. }
  751. if name == "" {
  752. return nil
  753. }
  754. taskTemplate.LogDriver = &swarm.Driver{
  755. Name: name,
  756. Options: runconfigopts.ConvertKVStringsToMap(flags.Lookup(flagLogOpt).Value.(*opts.ListOpts).GetAll()),
  757. }
  758. return nil
  759. }
  760. func updateHealthcheck(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec) error {
  761. if !anyChanged(flags, flagNoHealthcheck, flagHealthCmd, flagHealthInterval, flagHealthRetries, flagHealthTimeout) {
  762. return nil
  763. }
  764. if containerSpec.Healthcheck == nil {
  765. containerSpec.Healthcheck = &container.HealthConfig{}
  766. }
  767. noHealthcheck, err := flags.GetBool(flagNoHealthcheck)
  768. if err != nil {
  769. return err
  770. }
  771. if noHealthcheck {
  772. if !anyChanged(flags, flagHealthCmd, flagHealthInterval, flagHealthRetries, flagHealthTimeout) {
  773. containerSpec.Healthcheck = &container.HealthConfig{
  774. Test: []string{"NONE"},
  775. }
  776. return nil
  777. }
  778. return fmt.Errorf("--%s conflicts with --health-* options", flagNoHealthcheck)
  779. }
  780. if len(containerSpec.Healthcheck.Test) > 0 && containerSpec.Healthcheck.Test[0] == "NONE" {
  781. containerSpec.Healthcheck.Test = nil
  782. }
  783. if flags.Changed(flagHealthInterval) {
  784. val := *flags.Lookup(flagHealthInterval).Value.(*PositiveDurationOpt).Value()
  785. containerSpec.Healthcheck.Interval = val
  786. }
  787. if flags.Changed(flagHealthTimeout) {
  788. val := *flags.Lookup(flagHealthTimeout).Value.(*PositiveDurationOpt).Value()
  789. containerSpec.Healthcheck.Timeout = val
  790. }
  791. if flags.Changed(flagHealthRetries) {
  792. containerSpec.Healthcheck.Retries, _ = flags.GetInt(flagHealthRetries)
  793. }
  794. if flags.Changed(flagHealthCmd) {
  795. cmd, _ := flags.GetString(flagHealthCmd)
  796. if cmd != "" {
  797. containerSpec.Healthcheck.Test = []string{"CMD-SHELL", cmd}
  798. } else {
  799. containerSpec.Healthcheck.Test = nil
  800. }
  801. }
  802. return nil
  803. }