compose: docker-compose 1.27.3 config depends_on breaks stack deploy

Description of the issue

When we apply docker-compose config to a docker-compose file with depends_on, the end result is not deployable by docker stack…

Steps to reproduce the issue

Example docker-compose file:

services:
  test:
    depends_on:
    - test2
    image: a/fakeimage:latest
  test2:
    image: a/fakeimage:latest
version: '3.4'

Use docker-compose -f <example-config> config > new_config

The result is:

services:
  test:
    depends_on:
      test2:
        condition: service_started
    image: a/fakeimage:latest
  test2:
    image: a/fakeimage:latest
version: '3.4'

Now, attempting to deploy that new compose config, docker stack deploy fails:

docker stack deploy -c configout.yml iservices
services.test.depends_on must be a list

This behavior does not occur in 1.26.x or 1.27.2

docker-ce == 19.03.5 docker-compose ==1.27.3

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 23
  • Comments: 19

Most upvoted comments

@SMFloris that is awesome, I definitely agree that functionality is needed directly from the stack command.

That sounds feasible for a longer term solution- but I would still like to know what the planned next steps are for the current 1.27.x line of docker-compose since:

  • This code changed failed unit tests, and is a regression, significantly changing expected behavior
  • This backward-compatibility breaking regression was introduced in a patch release (.3), which is not best practice.
  • This oversight essentially removed supported functionality in a way that was not formally deprecated, announced, or documented, in a release version that is supposed to be stable.

I believe this should be addressed directly within the 1.27.x line. Namely an updated patch release 1.27.4 which corrects the regression and passes originally defined unit tests covering 1.27.x.

Here’s a drop-in workaround that employs yq, the Yaml query lib inspired by the JSON equivalent jq. The output from docker-compose config will have all service.*.depends_on mapped to array-of-string service names, the compatible type for docker stack deploy. Enjoy. 😃

docker-compose config \
    | yq e '(.services[] | select(.depends_on | tag == "!!map")).depends_on |= (. | keys)' -

# .services[] - for each service map
# select(.depends_on | tag == "!!map") - keep service if depends_on is a map
#     (discards services where depends_on is null/undefined or already string[])
# (...).depends_on |= (...) - assign to the selected services' depends_on with
#     the depends_on as context for RHS
# . | keys - map depends_on (as a map of service names => condition) to list
#     of map keys (service names)

I’m glad @SMFloris put up a PR with something forward thinking but IMO, it’s a shame someone on the team wouldn’t put forth what would be just a little effort to adopt what was undefined behavior into the product to make it defined (the interaction between docker-compose config and docker stack deploy). Why the config command could not simply be included under docker stack with appropriate changes is beyond me. You get a bunch of wins:

  1. The features (env subbing, etc.) from docker-compose config are now suddenly first-class features of Swarm CLI
  2. Minimal new code to be written—probably no new code written, just some lines removed or tweaked slightly to make sure output is valid for Swarm
  3. Two separate versions of config subcommand seems reasonable if the two are expected to drift apart. If they’re in 2 separate codebases, great. If they’re in 1 codebase, then include/import/abstract your way to DRYness and call it a day. No sin here…

semantic versioning expectatons are actually incredibly hard to follow on real world project, as any change can, intentionally or not, introduce sort of regression. I only can imagine this as a “best effort” approach. And, again, you’re using config in a way it was designed for, the fact it silently removed depends_on could be considered a bug that we fixed. If you rely on a buggy behaviour, and we have to adopt strict semantic versioning, then each new release should be a major release 😛

By the way, as you understand the future direction, better embrace it and don’t focus on what’s left behind. Life’s short

What I find quite funny, and correct me if I’m wrong-- But it seems this change originally failed the existing unit tests which ensures depends_on matches type list: https://ci-next.docker.com/public/blue/organizations/jenkins/compose/detail/PR-7761/1/pipeline/#step-89-log-867

And then, in the next commit these screaming unit tests were “updated” by deleting them, or changing their validation criteria: https://github.com/docker/compose/commit/fa720787d62cf833ed7648ff55dbd248267a5b74

We have a docker-compose file to deploy an application that may be deployed either as a stack, or with docker-compose, easiest to maintain one config file for both, and consistently apply updates using other compose files docker-compose config -f <file1> -f <file2> config

Regardless our use case, this is a change that doesn’t seem like it should have been made in a patch (.3) release.

I think docker-compose should respect version specified in docker-compose.yml For 2.* it should be object map with conditions For 3.* it should be list

The future direction makes sense, however that is not the point of the bug.

The point is that this backward compatibility breaking change (as evidenced by the existing unit tests which were deleted) was improperly added in a patch release.

Per standard semantic versioning if my package requirement is docker-compose~=1.27.0 I would expect docker-compose 1.27.x to not introduce a compatibility breaking bug that would cause my already in production automation to break (using ~=1.27.0).

If you want to deprecate functionality and no longer support things as they once were, fine. But please remember to do so in a standards based (semantic versioning) fashion, preferably with some sort of deprecation notice.

Failing to do so makes docker-compose less of an appealing option to use in real production environments.