3

I have a container that runs a database migration (source):

FROM golang:1.12-alpine3.10 AS downloader
ARG VERSION

RUN apk add --no-cache git gcc musl-dev

WORKDIR /go/src/github.com/golang-migrate/migrate

COPY . ./

ENV GO111MODULE=on
ENV DATABASES="postgres mysql redshift cassandra spanner cockroachdb clickhouse mongodb sqlserver firebird"
ENV SOURCES="file go_bindata github github_ee aws_s3 google_cloud_storage godoc_vfs gitlab"

RUN go build -a -o build/migrate.linux-386 -ldflags="-s -w -X main.Version=${VERSION}" -tags "$DATABASES $SOURCES" ./cmd/migrate

FROM alpine:3.10

RUN apk add --no-cache ca-certificates

COPY --from=downloader /go/src/github.com/golang-migrate/migrate/build/migrate.linux-386 /migrate

ENTRYPOINT ["/migrate"]
CMD ["--help"]

I want to integrate it into a docker-compose and make it dependent on the Postgres database service. However, since I have to wait until the database is fully initialised I have to wrap the migrate command in a script and thus replace the entrypoint of the migration container. I'm using the wait-for script to poll the database, which is a pure shell (not bash) script and should thus work in an alpine container.

This is how the service is defined in the docker-compose:

services:
    database:
        # ...
    migration:
        depends_on:
            - database
        image: migrate/migrate:v4.7.0
        volumes:
            - ./scripts/migrations:/migrations
            - ./scripts/wait-for:/wait-for
        entrypoint: ["/bin/sh"]
        command: ["./wait-for database:5432", "--", "./migrate", "-path", "/migrations", "-database", "postgres://test:test@database:5432/test?sslmode=disable",  "-verbose", "up"]

Running docker-compose up on this fails with

migration_1           | /bin/sh: can't open './wait-for database:5432': No such file or directory

Running the migrate container for itself with

docker run -it --entrypoint /bin/sh -v $(pwd)/scripts/wait-for:/wait-for  migrate/migrate:v4.7.0

does work flawlessly, the script is there and can be run with /bin/sh ./wait-for.

So why does it fail as part of the docker-compose?

gmolau
  • 2,815
  • 1
  • 22
  • 45

3 Answers3

4

If you read the error message carefully, you will see that the file that cannot be found is not ./waitfor, it is ./wait-for database:5432. This is consistent with your input file, where that whole thing is given as the first element of the command list:

        command: ["./wait-for database:5432", "--", "./migrate", "-path", "/migrations", "-database", "postgres://test:test@database:5432/test?sslmode=disable",  "-verbose", "up"]

It's unclear to me what you actually want instead, since the working alternatives presented do not seem to be fully analogous, but possibly it's

        command: ["./wait-for", "database:5432", "--", "./migrate", "-path", "/migrations", "-database", "postgres://test:test@database:5432/test?sslmode=disable",  "-verbose", "up"]
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Thanks, that did indeed fix it. I never really understood this array syntax, but it seems like I should. – gmolau Oct 26 '19 at 14:41
  • @gmolau, every array element provides a separate word on the command line, as-is. These are not subject to expansion, word splitting, *etc*.. Space characters and any others that are special to the shell are not special in this context, though they have whatever significance they normally do to the command that is run. – John Bollinger Oct 26 '19 at 14:50
0

Running the migrate container for itself with does work flawlessly

When you run it like:

docker run -it --entrypoint /bin/sh -v $(pwd)/scripts/wait-for:/wait-for migrate/migrate:v4.7.0

entrypoint /bin/sh is executed.

When you run it using docker-compose:

entrypoint (/bin/sh ) + command (./wait-for database:5432) ...` is executed.

./wait-for database:5432 as whole stands for executable that will run and it can't be found, that's why you get the error No such file or directory

Try to specify an absolute path to wait-for in command: and split ./wait-for database:5432 into "./wait-for", "database:5432".

It's possible that splitting will be enough

As an alternative you can follow CMD syntax docs and use different command syntax without array: command: ./wait-for database:5432 ...

rok
  • 9,403
  • 17
  • 70
  • 126
0

ENTRYPOINT ["/bin/sh"] is not enough, you also need the -c argument.

Example (testing a docker-compose.yml with docker-compose run --rm MYSERVICENAMEFROMTHEDOCKERCOMPOSEFILE bash here):

entrypoint: ["/bin/sh"]

Throws:

/bin/sh: 0: cannot open bash: No such file
ERROR: 2

And some wrong syntax examples like

entrypoint: ["/bin/sh -c"]

(wrong!)

or

entrypoint: ["/bin/sh, -c"]

(wrong!)

throw errors:

starting container process caused: exec: "/bin/sh, -c": stat /bin/sh, -c: no such file or directory: unknown
ERROR: 1


starting container process caused: exec: "/bin/sh -c": stat /bin/sh -c: no such file or directory: unknown
ERROR: 1

In docker-compose or Dockerfile, for an entrypoint, you need the -c argument.

This is right:

entrypoint: "/bin/sh -c"

or:

entrypoint: ["/bin/sh", "-c"]

The -c is to make clear that this is a command executed in the command line, waiting for an additional command to be used in that command line. but not starting the bash /bin/sh just on its own. You can read that between the lines at What is the difference between CMD and ENTRYPOINT in a Dockerfile?.

questionto42
  • 7,175
  • 4
  • 57
  • 90