update.go 25 KB

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