github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/api/client/service/update.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "time" 6 7 "golang.org/x/net/context" 8 9 "github.com/docker/docker/api/client" 10 "github.com/docker/docker/cli" 11 "github.com/docker/docker/opts" 12 runconfigopts "github.com/docker/docker/runconfig/opts" 13 "github.com/docker/engine-api/types/swarm" 14 "github.com/docker/go-connections/nat" 15 "github.com/spf13/cobra" 16 "github.com/spf13/pflag" 17 ) 18 19 func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command { 20 opts := newServiceOptions() 21 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 31 flags := cmd.Flags() 32 flags.String("image", "", "Service image tag") 33 flags.StringSlice("command", []string{}, "Service command") 34 flags.StringSlice("arg", []string{}, "Service command args") 35 addServiceFlags(cmd, opts) 36 return cmd 37 } 38 39 func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID string) error { 40 client := dockerCli.Client() 41 ctx := context.Background() 42 43 service, _, err := client.ServiceInspectWithRaw(ctx, serviceID) 44 if err != nil { 45 return err 46 } 47 48 err = updateService(&service.Spec, flags) 49 if err != nil { 50 return err 51 } 52 err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec) 53 if err != nil { 54 return err 55 } 56 57 fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID) 58 return nil 59 } 60 61 func updateService(spec *swarm.ServiceSpec, flags *pflag.FlagSet) error { 62 63 updateString := func(flag string, field *string) { 64 if flags.Changed(flag) { 65 *field, _ = flags.GetString(flag) 66 } 67 } 68 69 updateListOpts := func(flag string, field *[]string) { 70 if flags.Changed(flag) { 71 value := flags.Lookup(flag).Value.(*opts.ListOpts) 72 *field = value.GetAll() 73 } 74 } 75 76 updateSlice := func(flag string, field *[]string) { 77 if flags.Changed(flag) { 78 *field, _ = flags.GetStringSlice(flag) 79 } 80 } 81 82 updateInt64Value := func(flag string, field *int64) { 83 if flags.Changed(flag) { 84 *field = flags.Lookup(flag).Value.(int64Value).Value() 85 } 86 } 87 88 updateDuration := func(flag string, field *time.Duration) { 89 if flags.Changed(flag) { 90 *field, _ = flags.GetDuration(flag) 91 } 92 } 93 94 updateDurationOpt := func(flag string, field *time.Duration) { 95 if flags.Changed(flag) { 96 *field = *flags.Lookup(flag).Value.(*DurationOpt).Value() 97 } 98 } 99 100 updateUint64 := func(flag string, field *uint64) { 101 if flags.Changed(flag) { 102 *field, _ = flags.GetUint64(flag) 103 } 104 } 105 106 updateUint64Opt := func(flag string, field *uint64) { 107 if flags.Changed(flag) { 108 *field = *flags.Lookup(flag).Value.(*Uint64Opt).Value() 109 } 110 } 111 112 cspec := &spec.TaskTemplate.ContainerSpec 113 task := &spec.TaskTemplate 114 115 taskResources := func() *swarm.ResourceRequirements { 116 if task.Resources == nil { 117 task.Resources = &swarm.ResourceRequirements{} 118 } 119 return task.Resources 120 } 121 122 updateString(flagName, &spec.Name) 123 updateLabels(flags, &spec.Labels) 124 updateString("image", &cspec.Image) 125 updateSlice("command", &cspec.Command) 126 updateSlice("arg", &cspec.Command) 127 updateListOpts("env", &cspec.Env) 128 updateString("workdir", &cspec.Dir) 129 updateString(flagUser, &cspec.User) 130 updateMounts(flags, &cspec.Mounts) 131 132 if flags.Changed(flagLimitCPU) || flags.Changed(flagLimitMemory) { 133 taskResources().Limits = &swarm.Resources{} 134 updateInt64Value(flagLimitCPU, &task.Resources.Limits.NanoCPUs) 135 updateInt64Value(flagLimitMemory, &task.Resources.Limits.MemoryBytes) 136 137 } 138 if flags.Changed(flagReserveCPU) || flags.Changed(flagReserveMemory) { 139 taskResources().Reservations = &swarm.Resources{} 140 updateInt64Value(flagReserveCPU, &task.Resources.Reservations.NanoCPUs) 141 updateInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes) 142 } 143 144 updateDurationOpt(flagStopGracePeriod, cspec.StopGracePeriod) 145 146 if anyChanged(flags, flagRestartCondition, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow) { 147 if task.RestartPolicy == nil { 148 task.RestartPolicy = &swarm.RestartPolicy{} 149 } 150 151 if flags.Changed(flagRestartCondition) { 152 value, _ := flags.GetString(flagRestartCondition) 153 task.RestartPolicy.Condition = swarm.RestartPolicyCondition(value) 154 } 155 updateDurationOpt(flagRestartDelay, task.RestartPolicy.Delay) 156 updateUint64Opt(flagRestartMaxAttempts, task.RestartPolicy.MaxAttempts) 157 updateDurationOpt((flagRestartWindow), task.RestartPolicy.Window) 158 } 159 160 if flags.Changed(flagConstraint) { 161 task.Placement = &swarm.Placement{} 162 updateSlice(flagConstraint, &task.Placement.Constraints) 163 } 164 165 if err := updateReplicas(flags, &spec.Mode); err != nil { 166 return err 167 } 168 169 if anyChanged(flags, flagUpdateParallelism, flagUpdateDelay) { 170 if spec.UpdateConfig == nil { 171 spec.UpdateConfig = &swarm.UpdateConfig{} 172 } 173 updateUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism) 174 updateDuration(flagUpdateDelay, &spec.UpdateConfig.Delay) 175 } 176 177 updateNetworks(flags, &spec.Networks) 178 if flags.Changed(flagEndpointMode) { 179 value, _ := flags.GetString(flagEndpointMode) 180 spec.EndpointSpec.Mode = swarm.ResolutionMode(value) 181 } 182 183 if flags.Changed(flagPublish) { 184 if spec.EndpointSpec == nil { 185 spec.EndpointSpec = &swarm.EndpointSpec{} 186 } 187 updatePorts(flags, &spec.EndpointSpec.Ports) 188 } 189 return nil 190 } 191 192 func updateLabels(flags *pflag.FlagSet, field *map[string]string) { 193 if !flags.Changed(flagLabel) { 194 return 195 } 196 197 values := flags.Lookup(flagLabel).Value.(*opts.ListOpts).GetAll() 198 199 localLabels := map[string]string{} 200 for key, value := range runconfigopts.ConvertKVStringsToMap(values) { 201 localLabels[key] = value 202 } 203 *field = localLabels 204 } 205 206 func anyChanged(flags *pflag.FlagSet, fields ...string) bool { 207 for _, flag := range fields { 208 if flags.Changed(flag) { 209 return true 210 } 211 } 212 return false 213 } 214 215 // TODO: should this override by destination path, or does swarm handle that? 216 func updateMounts(flags *pflag.FlagSet, mounts *[]swarm.Mount) { 217 if !flags.Changed(flagMount) { 218 return 219 } 220 221 *mounts = flags.Lookup(flagMount).Value.(*MountOpt).Value() 222 } 223 224 // TODO: should this override by name, or does swarm handle that? 225 func updatePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) { 226 if !flags.Changed(flagPublish) { 227 return 228 } 229 230 values := flags.Lookup(flagPublish).Value.(*opts.ListOpts).GetAll() 231 ports, portBindings, _ := nat.ParsePortSpecs(values) 232 233 var localPortConfig []swarm.PortConfig 234 for port := range ports { 235 localPortConfig = append(localPortConfig, convertPortToPortConfig(port, portBindings)...) 236 } 237 *portConfig = localPortConfig 238 } 239 240 func updateNetworks(flags *pflag.FlagSet, attachments *[]swarm.NetworkAttachmentConfig) { 241 if !flags.Changed(flagNetwork) { 242 return 243 } 244 networks, _ := flags.GetStringSlice(flagNetwork) 245 246 var localAttachments []swarm.NetworkAttachmentConfig 247 for _, network := range networks { 248 localAttachments = append(localAttachments, swarm.NetworkAttachmentConfig{Target: network}) 249 } 250 *attachments = localAttachments 251 } 252 253 func updateReplicas(flags *pflag.FlagSet, serviceMode *swarm.ServiceMode) error { 254 if !flags.Changed(flagReplicas) { 255 return nil 256 } 257 258 if serviceMode.Replicated == nil { 259 return fmt.Errorf("replicas can only be used with replicated mode") 260 } 261 serviceMode.Replicated.Replicas = flags.Lookup(flagReplicas).Value.(*Uint64Opt).Value() 262 return nil 263 }