update.go 20 KB

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