update.go 23 KB

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