Added support for generic resource update

Signed-off-by: Renaud Gaubert <renaud.gaubert@gmail.com>
This commit is contained in:
Renaud Gaubert 2017-11-17 23:05:44 +01:00 committed by Renaud Gaubert
parent 4a6da88f7a
commit 20a6ff32ee
6 changed files with 158 additions and 0 deletions

View File

@ -77,6 +77,8 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, opts *serviceOptions
return err return err
} }
fmt.Printf("%v\n", service.TaskTemplate.Resources)
specifiedSecrets := opts.secrets.Value() specifiedSecrets := opts.secrets.Value()
if len(specifiedSecrets) > 0 { if len(specifiedSecrets) > 0 {
// parse and validate secrets // parse and validate secrets

View File

@ -74,3 +74,32 @@ func genericResourcesFromGRPC(genericRes []*swarmapi.GenericResource) []swarm.Ge
return generic return generic
} }
func buildGenericResourceMap(genericRes []swarm.GenericResource) (map[string]swarm.GenericResource, error) {
m := make(map[string]swarm.GenericResource)
for _, res := range genericRes {
if res.DiscreteResourceSpec == nil {
return nil, fmt.Errorf("invalid generic-resource `%+v` for service task", res)
}
_, ok := m[res.DiscreteResourceSpec.Kind]
if ok {
return nil, fmt.Errorf("duplicate generic-resource `%+v` for service task", res.DiscreteResourceSpec.Kind)
}
m[res.DiscreteResourceSpec.Kind] = res
}
return m, nil
}
func buildGenericResourceList(genericRes map[string]swarm.GenericResource) []swarm.GenericResource {
var l []swarm.GenericResource
for _, res := range genericRes {
l = append(l, res)
}
return l
}

View File

@ -830,6 +830,8 @@ const (
flagEnvFile = "env-file" flagEnvFile = "env-file"
flagEnvRemove = "env-rm" flagEnvRemove = "env-rm"
flagEnvAdd = "env-add" flagEnvAdd = "env-add"
flagGenericResourcesRemove = "generic-resource-rm"
flagGenericResourcesAdd = "generic-resource-add"
flagGroup = "group" flagGroup = "group"
flagGroupAdd = "group-add" flagGroupAdd = "group-add"
flagGroupRemove = "group-rm" flagGroupRemove = "group-rm"

View File

@ -95,6 +95,12 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
flags.Var(&options.hosts, flagHostAdd, "Add a custom host-to-IP mapping (host:ip)") flags.Var(&options.hosts, flagHostAdd, "Add a custom host-to-IP mapping (host:ip)")
flags.SetAnnotation(flagHostAdd, "version", []string{"1.25"}) flags.SetAnnotation(flagHostAdd, "version", []string{"1.25"})
// Add needs parsing, Remove only needs the key
flags.Var(newListOptsVar(), flagGenericResourcesRemove, "Remove a Generic resource")
flags.SetAnnotation(flagHostAdd, "version", []string{"1.32"})
flags.Var(newListOptsVarWithValidator(ValidateSingleGenericResource), flagGenericResourcesAdd, "Add a Generic resource")
flags.SetAnnotation(flagHostAdd, "version", []string{"1.32"})
return cmd return cmd
} }
@ -102,6 +108,10 @@ func newListOptsVar() *opts.ListOpts {
return opts.NewListOptsRef(&[]string{}, nil) return opts.NewListOptsRef(&[]string{}, nil)
} }
func newListOptsVarWithValidator(validator opts.ValidatorFctType) *opts.ListOpts {
return opts.NewListOptsRef(&[]string{}, validator)
}
// nolint: gocyclo // nolint: gocyclo
func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, options *serviceOptions, serviceID string) error { func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, options *serviceOptions, serviceID string) error {
apiClient := dockerCli.Client() apiClient := dockerCli.Client()
@ -314,6 +324,14 @@ func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags
updateInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes) updateInt64Value(flagReserveMemory, &task.Resources.Reservations.MemoryBytes)
} }
if err := addGenericResources(flags, task); err != nil {
return err
}
if err := removeGenericResources(flags, task); err != nil {
return err
}
updateDurationOpt(flagStopGracePeriod, &cspec.StopGracePeriod) updateDurationOpt(flagStopGracePeriod, &cspec.StopGracePeriod)
if anyChanged(flags, flagRestartCondition, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow) { if anyChanged(flags, flagRestartCondition, flagRestartDelay, flagRestartMaxAttempts, flagRestartWindow) {
@ -470,6 +488,72 @@ func anyChanged(flags *pflag.FlagSet, fields ...string) bool {
return false return false
} }
func addGenericResources(flags *pflag.FlagSet, spec *swarm.TaskSpec) error {
if !flags.Changed(flagGenericResourcesAdd) {
return nil
}
if spec.Resources == nil {
spec.Resources = &swarm.ResourceRequirements{}
}
if spec.Resources.Reservations == nil {
spec.Resources.Reservations = &swarm.Resources{}
}
values := flags.Lookup(flagGenericResourcesAdd).Value.(*opts.ListOpts).GetAll()
generic, err := ParseGenericResources(values)
if err != nil {
return err
}
m, err := buildGenericResourceMap(spec.Resources.Reservations.GenericResources)
if err != nil {
return err
}
for _, toAddRes := range generic {
m[toAddRes.DiscreteResourceSpec.Kind] = toAddRes
}
spec.Resources.Reservations.GenericResources = buildGenericResourceList(m)
return nil
}
func removeGenericResources(flags *pflag.FlagSet, spec *swarm.TaskSpec) error {
// Can only be Discrete Resources
if !flags.Changed(flagGenericResourcesRemove) {
return nil
}
if spec.Resources == nil {
spec.Resources = &swarm.ResourceRequirements{}
}
if spec.Resources.Reservations == nil {
spec.Resources.Reservations = &swarm.Resources{}
}
values := flags.Lookup(flagGenericResourcesRemove).Value.(*opts.ListOpts).GetAll()
m, err := buildGenericResourceMap(spec.Resources.Reservations.GenericResources)
if err != nil {
return err
}
for _, toRemoveRes := range values {
if _, ok := m[toRemoveRes]; !ok {
return fmt.Errorf("could not find generic-resource `%s` to remove it", toRemoveRes)
}
delete(m, toRemoveRes)
}
spec.Resources.Reservations.GenericResources = buildGenericResourceList(m)
return nil
}
func updatePlacementConstraints(flags *pflag.FlagSet, placement *swarm.Placement) { func updatePlacementConstraints(flags *pflag.FlagSet, placement *swarm.Placement) {
if flags.Changed(flagConstraintAdd) { if flags.Changed(flagConstraintAdd) {
values := flags.Lookup(flagConstraintAdd).Value.(*opts.ListOpts).GetAll() values := flags.Lookup(flagConstraintAdd).Value.(*opts.ListOpts).GetAll()

View File

@ -547,3 +547,42 @@ func TestUpdateIsolationInvalid(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, container.Isolation("test"), spec.TaskTemplate.ContainerSpec.Isolation) assert.Equal(t, container.Isolation("test"), spec.TaskTemplate.ContainerSpec.Isolation)
} }
func TestAddGenericResources(t *testing.T) {
task := &swarm.TaskSpec{}
flags := newUpdateCommand(nil).Flags()
assert.Nil(t, addGenericResources(flags, task))
flags.Set(flagGenericResourcesAdd, "foo=1")
assert.NoError(t, addGenericResources(flags, task))
assert.Len(t, task.Resources.Reservations.GenericResources, 1)
// Checks that foo isn't added a 2nd time
flags = newUpdateCommand(nil).Flags()
flags.Set(flagGenericResourcesAdd, "bar=1")
assert.NoError(t, addGenericResources(flags, task))
assert.Len(t, task.Resources.Reservations.GenericResources, 2)
}
func TestRemoveGenericResources(t *testing.T) {
task := &swarm.TaskSpec{}
flags := newUpdateCommand(nil).Flags()
assert.Nil(t, removeGenericResources(flags, task))
flags.Set(flagGenericResourcesRemove, "foo")
assert.Error(t, removeGenericResources(flags, task))
flags = newUpdateCommand(nil).Flags()
flags.Set(flagGenericResourcesAdd, "foo=1")
addGenericResources(flags, task)
flags = newUpdateCommand(nil).Flags()
flags.Set(flagGenericResourcesAdd, "bar=1")
addGenericResources(flags, task)
flags = newUpdateCommand(nil).Flags()
flags.Set(flagGenericResourcesRemove, "foo")
assert.NoError(t, removeGenericResources(flags, task))
assert.Len(t, task.Resources.Reservations.GenericResources, 1)
}

View File

@ -41,6 +41,8 @@ Options:
--env-add list Add or update an environment variable --env-add list Add or update an environment variable
--env-rm list Remove an environment variable --env-rm list Remove an environment variable
--force Force update even if no changes require it --force Force update even if no changes require it
--generic-resource-add list Add an additional generic resource to the service's resources requirements
--generic-resource-rm list Remove a previously added generic resource to the service's resources requirements
--group-add list Add an additional supplementary user group to the container --group-add list Add an additional supplementary user group to the container
--group-rm list Remove a previously added supplementary user group from the container --group-rm list Remove a previously added supplementary user group from the container
--health-cmd string Command to run to check health --health-cmd string Command to run to check health