Conversation
| } | ||
|
|
||
| if !updateOpts.QueryRegistry { | ||
| serviceSpec.TaskTemplate.ContainerSpec.Image = service.Spec.TaskTemplate.ContainerSpec.Image |
There was a problem hiding this comment.
What happens if you change the image in the compose file?
There was a problem hiding this comment.
I believe in that case image != service.Spec.Labels[convert.LabelImage] would be false, so we wouldn't enter this block? I think changing this to an else makes that more obvious ( as commented)
There was a problem hiding this comment.
The label stuff only applies when resolveImage is set to resolveImageChanged. What if resolveImage is set to "never"?
There was a problem hiding this comment.
True, there needs to be another condition here on the else if
There was a problem hiding this comment.
I initially created some code that simply did a compare between the actual image in the spec (stripping digest if the image in the docker-compose file didn't have one), then noticed it's actually using the label to compare
There was a problem hiding this comment.
in the "else" case, UpdateService looks up image information, and applies the placement information (if that's what you meant)
There was a problem hiding this comment.
I'm thinking these two blocks should look something like this (using a switch/case instead of an if/else if):
switch {
// image should be updated by the server using QueryRegistry
case resolveImage == resolveImageAlways || (resolveImage == resolveImageChanged && image != service.Spec.Labels[convert.LabelImage]):
updateOpts.QueryRegistry = true
// image hasn't change, so the serviceSpec needs to be updated to use the existing image that was
// set by QueryRegistry on the previous deploy. Otherwise this will result in an incorrect service update.
case image == service.Spec.Labels[convert.LabelImage]:
serviceSpec.TaskTemplate.ContainerSpec.Image = service.Spec.TaskTemplate.ContainerSpec.Image
}
cli/command/stack/deploy_test.go
Outdated
| }, | ||
| }, | ||
| } | ||
| deployServices(ctx, client, spec, namespace, false, resolveImageChanged) |
There was a problem hiding this comment.
This test would continue to pass if serviceUpdateFunc() was not called, even if the test was failing. I think we need to adjust the assertions.
deployServices() can return an error, so we should assert.NoError(t, err) on that error.
Maybe instead of asserting in the fake, the fake could store the arg values, and the assertions could happen here after the call to deployServices() ?
There was a problem hiding this comment.
Yes, I was indeed looking at that, but didn't have a clean solution, I'll do some more thinking to address that
| if serviceSpec.TaskTemplate.Placement == nil { | ||
| serviceSpec.TaskTemplate.Placement = &swarm.Placement{} | ||
| } | ||
| serviceSpec.TaskTemplate.Placement.Platforms = service.Spec.TaskTemplate.Placement.Platforms |
There was a problem hiding this comment.
This seems like the wrong place to set defaults.
If these values are not set in the compose file where do they come from?
There was a problem hiding this comment.
This is in the update case; these are the values taken from the current service spec (resolved during initial deploy)
There was a problem hiding this comment.
Why are they changed by the engine/swarmkit? Shouldn't it only change the image?
There was a problem hiding this comment.
These were added based on the image information; i.e. only deploy a Windows image on a Windows host, Arm on Arm etc
But there are already some issues around it, so things may change
There was a problem hiding this comment.
Does that mean we could leave these out of this PR? I'm not sure it's related to the original bug, but maybe I'm misunderstanding.
Wouldn't these be set in all cases, not just when QueryRegistry is enabled?
There was a problem hiding this comment.
I considered moving this to the ServiceUpdate function but this problem only occurs with stack deploy, because it doesn't "diff" the same way as docker service update does. docker service update takes the existing service as a starting point, and only makes changes set through the -add / -rm flags, whereas stack deploy takes the compose-file as source of truth
There was a problem hiding this comment.
I opened moby/moby#34268. I think we're going to need to fix that first
Once we have a package that is responsible for handling this logic I think at least these lines will move to that package. Possible the switch/case as well
There was a problem hiding this comment.
This placement/platforms stuff is a bug in the client/ package. Let's not hack around it here.
There was a problem hiding this comment.
If we want to fix this for 17.06 I'm not sure we have a good alternative at this moment (without the refactoring of the client package)
There was a problem hiding this comment.
If this is only a bug fix for 17.06 let's commit it to only the 17.06 branch in docker-ce and leave the hack out of master. We can open an issue for the correct fix.
| } | ||
|
|
||
| if !updateOpts.QueryRegistry { | ||
| serviceSpec.TaskTemplate.ContainerSpec.Image = service.Spec.TaskTemplate.ContainerSpec.Image |
There was a problem hiding this comment.
I think the intent here would be more clear as an else instead of checking the flag set in the previous branch.
An inline comment about why this is necessary would also be great. I'm sure I'm going to forget. Something like this:
If the image hasn't changed the serviceSpec needs to be updated to use the existing image that was set by
QueryRegistryon the previous deploy. Otherwise this will trigger an incorrect service update.
4518743 to
0ac5a39
Compare
Codecov Report
@@ Coverage Diff @@
## master #386 +/- ##
=========================================
+ Coverage 46.26% 46.47% +0.2%
=========================================
Files 193 193
Lines 16093 16100 +7
=========================================
+ Hits 7445 7482 +37
+ Misses 8259 8225 -34
- Partials 389 393 +4 |
f55daa9 to
bb2c7d1
Compare
|
@dnephin updated PTAL |
|
|
||
| if resolveImage == resolveImageAlways || (resolveImage == resolveImageChanged && image != service.Spec.Labels[convert.LabelImage]) { | ||
| updateOpts.QueryRegistry = true | ||
| } else { |
There was a problem hiding this comment.
In this case: resolveImage == resolveImageNever && image != service.Spec.Labels[convert.LabelImage] we would set the image back to the previous image, which is wrong, because the user requested an update to the image.
I think this needs to be:
switch {
// image should be updated by the server using QueryRegistry
case resolveImage == resolveImageAlways || (resolveImage == resolveImageChanged && image != service.Spec.Labels[convert.LabelImage]):
updateOpts.QueryRegistry = true
// image hasn't changed, so the serviceSpec needs to be updated to use the existing image that was
// set by QueryRegistry on the previous deploy. Otherwise this will result in an incorrect service update.
case image == service.Spec.Labels[convert.LabelImage]:
serviceSpec.TaskTemplate.ContainerSpec.Image = service.Spec.TaskTemplate.ContainerSpec.Image
}There was a problem hiding this comment.
Argh, you're right; changed it to the if/else based on your previous comment. I'm not entirely convinced using a "switch" is much clearer; wether or not to keep the existing information is based on updateOpts.QueryRegistry being true, which was why I had it like that in the first implementation
There was a problem hiding this comment.
I don't think it should be based on updateOpts.QueryRegistry. updateOpts.QueryRegistry is set by comparing the existing state to the desired state of the service. Setting the Image should be based on that same comparison. updateOpts.QueryRegistry is an output, not the source of the state.
I'm not entirely convinced using a "switch" is much clearer
I find switch/case reads much better than any if/else, but it's true you could also implement this using if/else if
| if serviceSpec.TaskTemplate.Placement == nil { | ||
| serviceSpec.TaskTemplate.Placement = &swarm.Placement{} | ||
| } | ||
| serviceSpec.TaskTemplate.Placement.Platforms = service.Spec.TaskTemplate.Placement.Platforms |
There was a problem hiding this comment.
This placement/platforms stuff is a bug in the client/ package. Let's not hack around it here.
When re-deploying a stack without re-resolving the image digest, the service's ContainerSpec was updated with the image-reference as specified in the stack/compose file. As a result, the image-digest that was resolved in a previous deploy was overwritten, causing the service to be re-deployed. This patch preserves the previously resolve image-digest by copying it from the current service spec. A unit test is also added to verify that the image information in the service spec is not updated if QueryRegistry is disabled. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
bb2c7d1 to
d0bea64
Compare
|
@dnephin updated, now only preserving the image digest / information |
| expectedQueryRegistry: false, | ||
| expectedImage: "foobar:1.2.3@sha256:deadbeef", | ||
| }, | ||
| // Image changed |
There was a problem hiding this comment.
I think this test covers 2 of the 4 cases. The other 2 are different resolveImage values.
We could add coverage for that in a follow up
[master] bump VERSION to 18.02.0-ce-dev
When using
--resolve-image change, the image that was resolved during deploy, as well asPlatforminformation were overwritten by the image specified in the docker-compose.yaml. As a result, services were always re-deployed.fixes moby/moby#34242
- What I did
If
QueryRegistryis "false", preserve theimageandPlatforminformation from the existing service.UPDATE:
removed preserving the platform information, as is discussed in moby/moby#34268
- How to verify it
The diff should be empty:
Before this change, the diff would show:
- Description for the changelog
ping @aaronlehmann @nishanttotla @dnephin PTAL