Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix order of processing of some xx-add/xx-rm service update flags #2668

Merged
merged 1 commit into from
Aug 6, 2020

Conversation

thaJeztah
Copy link
Member

(noticed this while reviewing #2663. I thought we fixed these, but apparently not for all flags)

Combining -add and -rm flags on docker service update should be usable to explicitly replace existing options. The current order of processing did not allow this, causing the -rm flag to remove properties that were specified in -add. This behavior was inconsistent with (for example) --host-add and --host-rm.

This patch updates the behavior to first remove properties, then add new properties.

Note that there's still some improvements to make, to make the removal more granulas (e.g. to make --label-rm label=some-value only remove the label if value matches some-value); these changes are left for a follow-up.

Before this change:

Create a service with two env-vars

docker service create --env FOO=bar --env BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Env }}' test | jq .
[
  "FOO=bar",
  "BAR=baz"
]

Update the service, with the intent to replace the value of FOO for a new value

docker service update  --env-rm FOO --env-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Env }}' test | jq .
[
  "BAR=baz"
]

Create a service with two labels

docker service create --label FOO=bar --label BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "bar"
}

Update the service, with the intent to replace the value of FOO for a new value

docker service update  --label-rm FOO --label-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.Labels }}' test | jq .
{
  "BAR": "baz"
}

Create a service with two container labels

docker service create --container-label FOO=bar --container-label BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "bar"
}

Update the service, with the intent to replace the value of FOO for a new value

docker service update  --container-label-rm FOO --container-label-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Labels }}' test | jq .
{
  "BAR": "baz",
}

With this patch applied:

Create a service with two env-vars

docker service create --env FOO=bar --env BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Env }}' test | jq .
[
  "FOO=bar",
  "BAR=baz"
]

Update the service, and replace the value of FOO for a new value

docker service update  --env-rm FOO --env-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Env }}' test | jq .
[
  "BAR=baz",
  "FOO=updated-foo"
]

Create a service with two labels

docker service create --label FOO=bar --label BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "bar"
}

Update the service, and replace the value of FOO for a new value

docker service update  --label-rm FOO --label-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "updated-foo"
}

Create a service with two container labels

docker service create --container-label FOO=bar --container-label BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "bar"
}

Update the service, and replace the value of FOO for a new value

docker service update  --container-label-rm FOO --container-label-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "updated-foo"
}

- What I did

- How I did it

- How to verify it

- Description for the changelog

Fix order of processing of `--label-add/--label-rm`, `--container-label-add/--container-label-rm`, and `--env-add/--env-rm` flags on  `docker service update` to allow replacing existing values.

- A picture of a cute animal (not mandatory but encouraged)

Combining `-add` and `-rm` flags on `docker service update` should
be usable to explicitly replace existing options. The current order
of processing did not allow this, causing the `-rm` flag to remove
properties that were specified in `-add`. This behavior was inconsistent
with (for example) `--host-add` and `--host-rm`.

This patch updates the behavior to first remove properties, then
add new properties.

Note that there's still some improvements to make, to make the removal
more granulas (e.g. to make `--label-rm label=some-value` only remove
the label if value matches `some-value`); these changes are left for
a follow-up.

Before this change:
-----------------------------

Create a service with two env-vars

```bash
docker service create --env FOO=bar --env BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Env }}' test | jq .
[
  "FOO=bar",
  "BAR=baz"
]
```

Update the service, with the intent to replace the value of `FOO` for a new value

```bash
docker service update  --env-rm FOO --env-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Env }}' test | jq .
[
  "BAR=baz"
]
```

Create a service with two labels

```bash
docker service create --label FOO=bar --label BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "bar"
}
```

Update the service, with the intent to replace the value of `FOO` for a new value

```bash
docker service update  --label-rm FOO --label-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.Labels }}' test | jq .
{
  "BAR": "baz"
}
```

Create a service with two container labels

```bash
docker service create --container-label FOO=bar --container-label BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "bar"
}
```

Update the service, with the intent to replace the value of `FOO` for a new value

```bash
docker service update  --container-label-rm FOO --container-label-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Labels }}' test | jq .
{
  "BAR": "baz",
}
```

With this patch applied:
--------------------------------

Create a service with two env-vars

```bash
docker service create --env FOO=bar --env BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Env }}' test | jq .
[
  "FOO=bar",
  "BAR=baz"
]
```

Update the service, and replace the value of `FOO` for a new value

```bash
docker service update  --env-rm FOO --env-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Env }}' test | jq .
[
  "BAR=baz",
  "FOO=updated-foo"
]
```

Create a service with two labels

```bash
docker service create --label FOO=bar --label BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "bar"
}
```

Update the service, and replace the value of `FOO` for a new value

```bash
docker service update  --label-rm FOO --label-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "updated-foo"
}
```

Create a service with two container labels

```bash
docker service create --container-label FOO=bar --container-label BAR=baz  --name=test nginx:alpine
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "bar"
}
```

Update the service, and replace the value of `FOO` for a new value

```bash
docker service update  --container-label-rm FOO --container-label-add FOO=updated-foo test
docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Labels }}' test | jq .
{
  "BAR": "baz",
  "FOO": "updated-foo"
}
```

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
@thaJeztah
Copy link
Member Author

@silvin-lubecki @vdemeester ptal

@codecov-commenter
Copy link

Codecov Report

Merging #2668 into master will increase coverage by 0.03%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master    #2668      +/-   ##
==========================================
+ Coverage   58.13%   58.16%   +0.03%     
==========================================
  Files         295      295              
  Lines       21207    21207              
==========================================
+ Hits        12328    12336       +8     
+ Misses       7970     7963       -7     
+ Partials      909      908       -1     

@@ -640,6 +640,12 @@ func updatePlacementPreferences(flags *pflag.FlagSet, placement *swarm.Placement
}

func updateContainerLabels(flags *pflag.FlagSet, field *map[string]string) {
if *field != nil && flags.Changed(flagContainerLabelRemove) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: should we test field to?

if field != nil && *field != nil

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good one, I easily get confused by these 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah pointer on pointer is mind blowing 😁

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look for the extra check in a follow-up

Copy link
Contributor

@silvin-lubecki silvin-lubecki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍

@thaJeztah thaJeztah added this to the 20.03.0 milestone Aug 5, 2020
@silvin-lubecki silvin-lubecki merged commit 51d96c7 into docker:master Aug 6, 2020
@thaJeztah thaJeztah deleted the fix_add_rm_handling_order branch September 10, 2020 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants