update.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  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. mounttypes "github.com/docker/docker/api/types/mount"
  10. "github.com/docker/docker/api/types/swarm"
  11. "github.com/docker/docker/cli"
  12. "github.com/docker/docker/cli/command"
  13. "github.com/docker/docker/opts"
  14. runconfigopts "github.com/docker/docker/runconfig/opts"
  15. "github.com/docker/go-connections/nat"
  16. shlex "github.com/flynn-archive/go-shlex"
  17. "github.com/spf13/cobra"
  18. "github.com/spf13/pflag"
  19. )
  20. func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
  21. opts := newServiceOptions()
  22. cmd := &cobra.Command{
  23. Use: "update [OPTIONS] SERVICE",
  24. Short: "Update a service",
  25. Args: cli.ExactArgs(1),
  26. RunE: func(cmd *cobra.Command, args []string) error {
  27. return runUpdate(dockerCli, cmd.Flags(), args[0])
  28. },
  29. }
  30. flags := cmd.Flags()
  31. flags.String("image", "", "Service image tag")
  32. flags.String("args", "", "Service command args")
  33. flags.Bool("rollback", false, "Rollback to previous specification")
  34. addServiceFlags(cmd, opts)
  35. flags.Var(newListOptsVar(), flagEnvRemove, "Remove an environment variable")
  36. flags.Var(newListOptsVar(), flagGroupRemove, "Remove previously added user groups from the container")
  37. flags.Var(newListOptsVar(), flagLabelRemove, "Remove a label by its key")
  38. flags.Var(newListOptsVar(), flagContainerLabelRemove, "Remove a container label by its key")
  39. flags.Var(newListOptsVar(), flagMountRemove, "Remove a mount by its target path")
  40. flags.Var(newListOptsVar(), flagPublishRemove, "Remove a published port by its target port")
  41. flags.Var(newListOptsVar(), flagConstraintRemove, "Remove a constraint")
  42. flags.Var(&opts.labels, flagLabelAdd, "Add or update service labels")
  43. flags.Var(&opts.containerLabels, flagContainerLabelAdd, "Add or update container labels")
  44. flags.Var(&opts.env, flagEnvAdd, "Add or update environment variables")
  45. flags.Var(&opts.mounts, flagMountAdd, "Add or update a mount on a service")
  46. flags.StringSliceVar(&opts.constraints, flagConstraintAdd, []string{}, "Add or update placement constraints")
  47. flags.Var(&opts.endpoint.ports, flagPublishAdd, "Add or update a published port")
  48. return cmd
  49. }
  50. func newListOptsVar() *opts.ListOpts {
  51. return opts.NewListOptsRef(&[]string{}, nil)
  52. }
  53. func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, serviceID string) error {
  54. apiClient := dockerCli.Client()
  55. ctx := context.Background()
  56. updateOpts := types.ServiceUpdateOptions{}
  57. service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID)
  58. if err != nil {
  59. return err
  60. }
  61. rollback, err := flags.GetBool("rollback")
  62. if err != nil {
  63. return err
  64. }
  65. spec := &service.Spec
  66. if rollback {
  67. spec = service.PreviousSpec
  68. if spec == nil {
  69. return fmt.Errorf("service does not have a previous specification to roll back to")
  70. }
  71. }
  72. err = updateService(flags, spec)
  73. if err != nil {
  74. return err
  75. }
  76. // only send auth if flag was set
  77. sendAuth, err := flags.GetBool(flagRegistryAuth)
  78. if err != nil {
  79. return err
  80. }
  81. if sendAuth {
  82. // Retrieve encoded auth token from the image reference
  83. // This would be the old image if it didn't change in this update
  84. image := spec.TaskTemplate.ContainerSpec.Image
  85. encodedAuth, err := command.RetrieveAuthTokenFromImage(ctx, dockerCli, image)
  86. if err != nil {
  87. return err
  88. }
  89. updateOpts.EncodedRegistryAuth = encodedAuth
  90. } else if rollback {
  91. updateOpts.RegistryAuthFrom = types.RegistryAuthFromPreviousSpec
  92. } else {
  93. updateOpts.RegistryAuthFrom = types.RegistryAuthFromSpec
  94. }
  95. err = apiClient.ServiceUpdate(ctx, service.ID, service.Version, *spec, updateOpts)
  96. if err != nil {
  97. return err
  98. }
  99. fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID)
  100. return nil
  101. }
  102. func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
  103. updateString := func(flag string, field *string) {
  104. if flags.Changed(flag) {
  105. *field, _ = flags.GetString(flag)
  106. }
  107. }
  108. updateInt64Value := func(flag string, field *int64) {
  109. if flags.Changed(flag) {
  110. *field = flags.Lookup(flag).Value.(int64Value).Value()
  111. }
  112. }
  113. updateFloat32 := func(flag string, field *float32) {
  114. if flags.Changed(flag) {
  115. *field, _ = flags.GetFloat32(flag)
  116. }
  117. }
  118. updateDuration := func(flag string, field *time.Duration) {
  119. if flags.Changed(flag) {
  120. *field, _ = flags.GetDuration(flag)
  121. }
  122. }
  123. updateDurationOpt := func(flag string, field **time.Duration) {
  124. if flags.Changed(flag) {
  125. val := *flags.Lookup(flag).Value.(*DurationOpt).Value()
  126. *field = &val
  127. }
  128. }
  129. updateUint64 := func(flag string, field *uint64) {
  130. if flags.Changed(flag) {
  131. *field, _ = flags.GetUint64(flag)
  132. }
  133. }
  134. updateUint64Opt := func(flag string, field **uint64) {
  135. if flags.Changed(flag) {
  136. val := *flags.Lookup(flag).Value.(*Uint64Opt).Value()
  137. *field = &val
  138. }
  139. }
  140. cspec := &spec.TaskTemplate.ContainerSpec
  141. task := &spec.TaskTemplate
  142. taskResources := func() *swarm.ResourceRequirements {
  143. if task.Resources == nil {
  144. task.Resources = &swarm.ResourceRequirements{}
  145. }
  146. return task.Resources
  147. }
  148. updateString(flagName, &spec.Name)
  149. updateLabels(flags, &spec.Labels)
  150. updateContainerLabels(flags, &cspec.Labels)
  151. updateString("image", &cspec.Image)
  152. updateStringToSlice(flags, "args", &cspec.Args)
  153. updateEnvironment(flags, &cspec.Env)
  154. updateString(flagWorkdir, &cspec.Dir)
  155. updateString(flagUser, &cspec.User)
  156. updateMounts(flags, &cspec.Mounts)
  157. if flags.Changed(flagLimitCPU) || flags.Changed(flagLimitMemory) {
  158. taskResources().Limits = &swarm.Resources{}
  159. updateInt64Value(flagLimitCPU, &task.Resources.Limits.NanoCPUs)
  160. updateInt64Value(flagLimitMemory, &task.Resources.Limits.MemoryBytes)
  161. }
  162. if flags.Changed(flagReserveCPU) || flags.Changed(flagReserveMemory) {
  163. taskResources().Reservations = &swarm.Resources{}
  164. updateInt64Value(flagReserveCPU, &task.Resources.Reservations.NanoCPUs)
  165. updateInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes)
  166. }
  167. updateDurationOpt(flagStopGracePeriod, &cspec.StopGracePeriod)
  168. if anyChanged(flags, flagRestartCondition, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow) {
  169. if task.RestartPolicy == nil {
  170. task.RestartPolicy = &swarm.RestartPolicy{}
  171. }
  172. if flags.Changed(flagRestartCondition) {
  173. value, _ := flags.GetString(flagRestartCondition)
  174. task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value)
  175. }
  176. updateDurationOpt(flagRestartDelay, &task.RestartPolicy.Delay)
  177. updateUint64Opt(flagRestartMaxAttempts, &task.RestartPolicy.MaxAttempts)
  178. updateDurationOpt(flagRestartWindow, &task.RestartPolicy.Window)
  179. }
  180. if anyChanged(flags, flagConstraintAdd, flagConstraintRemove) {
  181. if task.Placement == nil {
  182. task.Placement = &swarm.Placement{}
  183. }
  184. updatePlacement(flags, task.Placement)
  185. }
  186. if err := updateReplicas(flags, &spec.Mode); err != nil {
  187. return err
  188. }
  189. if anyChanged(flags, flagUpdateParallelism, flagUpdateDelay, flagUpdateMonitor, flagUpdateFailureAction, flagUpdateMaxFailureRatio) {
  190. if spec.UpdateConfig == nil {
  191. spec.UpdateConfig = &swarm.UpdateConfig{}
  192. }
  193. updateUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism)
  194. updateDuration(flagUpdateDelay, &spec.UpdateConfig.Delay)
  195. updateDuration(flagUpdateMonitor, &spec.UpdateConfig.Monitor)
  196. updateString(flagUpdateFailureAction, &spec.UpdateConfig.FailureAction)
  197. updateFloat32(flagUpdateMaxFailureRatio, &spec.UpdateConfig.MaxFailureRatio)
  198. }
  199. if flags.Changed(flagEndpointMode) {
  200. value, _ := flags.GetString(flagEndpointMode)
  201. if spec.EndpointSpec == nil {
  202. spec.EndpointSpec = &swarm.EndpointSpec{}
  203. }
  204. spec.EndpointSpec.Mode = swarm.ResolutionMode(value)
  205. }
  206. if anyChanged(flags, flagGroupAdd, flagGroupRemove) {
  207. if err := updateGroups(flags, &cspec.Groups); err != nil {
  208. return err
  209. }
  210. }
  211. if anyChanged(flags, flagPublishAdd, flagPublishRemove) {
  212. if spec.EndpointSpec == nil {
  213. spec.EndpointSpec = &swarm.EndpointSpec{}
  214. }
  215. if err := updatePorts(flags, &spec.EndpointSpec.Ports); err != nil {
  216. return err
  217. }
  218. }
  219. if err := updateLogDriver(flags, &spec.TaskTemplate); err != nil {
  220. return err
  221. }
  222. return nil
  223. }
  224. func updateStringToSlice(flags *pflag.FlagSet, flag string, field *[]string) error {
  225. if !flags.Changed(flag) {
  226. return nil
  227. }
  228. value, _ := flags.GetString(flag)
  229. valueSlice, err := shlex.Split(value)
  230. *field = valueSlice
  231. return err
  232. }
  233. func anyChanged(flags *pflag.FlagSet, fields ...string) bool {
  234. for _, flag := range fields {
  235. if flags.Changed(flag) {
  236. return true
  237. }
  238. }
  239. return false
  240. }
  241. func updatePlacement(flags *pflag.FlagSet, placement *swarm.Placement) {
  242. field, _ := flags.GetStringSlice(flagConstraintAdd)
  243. placement.Constraints = append(placement.Constraints, field...)
  244. toRemove := buildToRemoveSet(flags, flagConstraintRemove)
  245. placement.Constraints = removeItems(placement.Constraints, toRemove, itemKey)
  246. }
  247. func updateContainerLabels(flags *pflag.FlagSet, field *map[string]string) {
  248. if flags.Changed(flagContainerLabelAdd) {
  249. if *field == nil {
  250. *field = map[string]string{}
  251. }
  252. values := flags.Lookup(flagContainerLabelAdd).Value.(*opts.ListOpts).GetAll()
  253. for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
  254. (*field)[key] = value
  255. }
  256. }
  257. if *field != nil && flags.Changed(flagContainerLabelRemove) {
  258. toRemove := flags.Lookup(flagContainerLabelRemove).Value.(*opts.ListOpts).GetAll()
  259. for _, label := range toRemove {
  260. delete(*field, label)
  261. }
  262. }
  263. }
  264. func updateLabels(flags *pflag.FlagSet, field *map[string]string) {
  265. if flags.Changed(flagLabelAdd) {
  266. if *field == nil {
  267. *field = map[string]string{}
  268. }
  269. values := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll()
  270. for key, value := range runconfigopts.ConvertKVStringsToMap(values) {
  271. (*field)[key] = value
  272. }
  273. }
  274. if *field != nil && flags.Changed(flagLabelRemove) {
  275. toRemove := flags.Lookup(flagLabelRemove).Value.(*opts.ListOpts).GetAll()
  276. for _, label := range toRemove {
  277. delete(*field, label)
  278. }
  279. }
  280. }
  281. func updateEnvironment(flags *pflag.FlagSet, field *[]string) {
  282. envSet := map[string]string{}
  283. for _, v := range *field {
  284. envSet[envKey(v)] = v
  285. }
  286. if flags.Changed(flagEnvAdd) {
  287. value := flags.Lookup(flagEnvAdd).Value.(*opts.ListOpts)
  288. for _, v := range value.GetAll() {
  289. envSet[envKey(v)] = v
  290. }
  291. }
  292. *field = []string{}
  293. for _, v := range envSet {
  294. *field = append(*field, v)
  295. }
  296. toRemove := buildToRemoveSet(flags, flagEnvRemove)
  297. *field = removeItems(*field, toRemove, envKey)
  298. }
  299. func envKey(value string) string {
  300. kv := strings.SplitN(value, "=", 2)
  301. return kv[0]
  302. }
  303. func itemKey(value string) string {
  304. return value
  305. }
  306. func buildToRemoveSet(flags *pflag.FlagSet, flag string) map[string]struct{} {
  307. var empty struct{}
  308. toRemove := make(map[string]struct{})
  309. if !flags.Changed(flag) {
  310. return toRemove
  311. }
  312. toRemoveSlice := flags.Lookup(flag).Value.(*opts.ListOpts).GetAll()
  313. for _, key := range toRemoveSlice {
  314. toRemove[key] = empty
  315. }
  316. return toRemove
  317. }
  318. func removeItems(
  319. seq []string,
  320. toRemove map[string]struct{},
  321. keyFunc func(string) string,
  322. ) []string {
  323. newSeq := []string{}
  324. for _, item := range seq {
  325. if _, exists := toRemove[keyFunc(item)]; !exists {
  326. newSeq = append(newSeq, item)
  327. }
  328. }
  329. return newSeq
  330. }
  331. func updateMounts(flags *pflag.FlagSet, mounts *[]mounttypes.Mount) {
  332. if flags.Changed(flagMountAdd) {
  333. values := flags.Lookup(flagMountAdd).Value.(*MountOpt).Value()
  334. *mounts = append(*mounts, values...)
  335. }
  336. toRemove := buildToRemoveSet(flags, flagMountRemove)
  337. newMounts := []mounttypes.Mount{}
  338. for _, mount := range *mounts {
  339. if _, exists := toRemove[mount.Target]; !exists {
  340. newMounts = append(newMounts, mount)
  341. }
  342. }
  343. *mounts = newMounts
  344. }
  345. func updateGroups(flags *pflag.FlagSet, groups *[]string) error {
  346. if flags.Changed(flagGroupAdd) {
  347. values, err := flags.GetStringSlice(flagGroupAdd)
  348. if err != nil {
  349. return err
  350. }
  351. *groups = append(*groups, values...)
  352. }
  353. toRemove := buildToRemoveSet(flags, flagGroupRemove)
  354. newGroups := []string{}
  355. for _, group := range *groups {
  356. if _, exists := toRemove[group]; !exists {
  357. newGroups = append(newGroups, group)
  358. }
  359. }
  360. // Sort so that result is predictable.
  361. sort.Strings(newGroups)
  362. *groups = newGroups
  363. return nil
  364. }
  365. type byPortConfig []swarm.PortConfig
  366. func (r byPortConfig) Len() int { return len(r) }
  367. func (r byPortConfig) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
  368. func (r byPortConfig) Less(i, j int) bool {
  369. // We convert PortConfig into `port/protocol`, e.g., `80/tcp`
  370. // In updatePorts we already filter out with map so there is duplicate entries
  371. return portConfigToString(&r[i]) < portConfigToString(&r[j])
  372. }
  373. func portConfigToString(portConfig *swarm.PortConfig) string {
  374. protocol := portConfig.Protocol
  375. if protocol == "" {
  376. protocol = "tcp"
  377. }
  378. return fmt.Sprintf("%v/%s", portConfig.PublishedPort, protocol)
  379. }
  380. func updatePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) error {
  381. // The key of the map is `port/protocol`, e.g., `80/tcp`
  382. portSet := map[string]swarm.PortConfig{}
  383. // Check to see if there are any conflict in flags.
  384. if flags.Changed(flagPublishAdd) {
  385. values := flags.Lookup(flagPublishAdd).Value.(*opts.ListOpts).GetAll()
  386. ports, portBindings, _ := nat.ParsePortSpecs(values)
  387. for port := range ports {
  388. newConfigs := convertPortToPortConfig(port, portBindings)
  389. for _, entry := range newConfigs {
  390. if v, ok := portSet[portConfigToString(&entry)]; ok && v != entry {
  391. 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)
  392. }
  393. portSet[portConfigToString(&entry)] = entry
  394. }
  395. }
  396. }
  397. // Override previous PortConfig in service if there is any duplicate
  398. for _, entry := range *portConfig {
  399. if _, ok := portSet[portConfigToString(&entry)]; !ok {
  400. portSet[portConfigToString(&entry)] = entry
  401. }
  402. }
  403. toRemove := flags.Lookup(flagPublishRemove).Value.(*opts.ListOpts).GetAll()
  404. newPorts := []swarm.PortConfig{}
  405. portLoop:
  406. for _, port := range portSet {
  407. for _, rawTargetPort := range toRemove {
  408. targetPort := nat.Port(rawTargetPort)
  409. if equalPort(targetPort, port) {
  410. continue portLoop
  411. }
  412. }
  413. newPorts = append(newPorts, port)
  414. }
  415. // Sort the PortConfig to avoid unnecessary updates
  416. sort.Sort(byPortConfig(newPorts))
  417. *portConfig = newPorts
  418. return nil
  419. }
  420. func equalPort(targetPort nat.Port, port swarm.PortConfig) bool {
  421. return (string(port.Protocol) == targetPort.Proto() &&
  422. port.TargetPort == uint32(targetPort.Int()))
  423. }
  424. func updateReplicas(flags *pflag.FlagSet, serviceMode *swarm.ServiceMode) error {
  425. if !flags.Changed(flagReplicas) {
  426. return nil
  427. }
  428. if serviceMode == nil || serviceMode.Replicated == nil {
  429. return fmt.Errorf("replicas can only be used with replicated mode")
  430. }
  431. serviceMode.Replicated.Replicas = flags.Lookup(flagReplicas).Value.(*Uint64Opt).Value()
  432. return nil
  433. }
  434. // updateLogDriver updates the log driver only if the log driver flag is set.
  435. // All options will be replaced with those provided on the command line.
  436. func updateLogDriver(flags *pflag.FlagSet, taskTemplate *swarm.TaskSpec) error {
  437. if !flags.Changed(flagLogDriver) {
  438. return nil
  439. }
  440. name, err := flags.GetString(flagLogDriver)
  441. if err != nil {
  442. return err
  443. }
  444. if name == "" {
  445. return nil
  446. }
  447. taskTemplate.LogDriver = &swarm.Driver{
  448. Name: name,
  449. Options: runconfigopts.ConvertKVStringsToMap(flags.Lookup(flagLogOpt).Value.(*opts.ListOpts).GetAll()),
  450. }
  451. return nil
  452. }