update.go 25 KB

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