update.go 25 KB

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