skip to Main Content

I want to unit-test my app which uses a postgres database inside a docker.

EDIT: based on the suggested answer I modified the Dockerfile:

FROM postgres
USER postgres
RUN sleep 2 # remark 1
RUN initdb  # remark 2
RUN sleep 3 # remark 1
RUN psql --host=localhost -l

The remarks are:

Try putting a sleep in there and see if it’s still a problem

The default postgres user and database are created in the entrypoint with initdb.

Here is the Dockerfile from the original question:

FROM postgres
COPY input.json .
RUN createdb -h localhost -p 7654 -U moish myLovelyAndTemporaryDb
#
# [ 1 ] run application on input.json
# [ 2 ] check db content after run
#

When I use the above Dockerfile I seem to be missing something:
(The errors from the edited version are the same)

$ docker build --tag host --file Dockerfile .
[+] Building 0.3s (7/7) FINISHED                                                                                                       
 => [internal] load build definition from Dockerfile                                                                              0.0s
 => => transferring dockerfile: 125B                                                                                              0.0s
 => [internal] load .dockerignore                                                                                                 0.0s
 => => transferring context: 2B                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/postgres:latest                                                                0.0s
 => [internal] load build context                                                                                                 0.0s
 => => transferring context: 40B                                                                                                  0.0s
 => CACHED [1/3] FROM docker.io/library/postgres                                                                                  0.0s
 => [2/3] COPY input.json .                                                                                                       0.0s
 => ERROR [3/3] RUN createdb -h localhost -p 7654 -U moish myLovelyAndTemporaryDb                                                                0.2s
------
 > [3/3] RUN createdb -h localhost -p 7654 -U moish myLovelyAndTemporaryDb:
#7 0.188 createdb: error: connection to server at "localhost" (127.0.0.1), port 7654 failed: Connection refused
#7 0.188    Is the server running on that host and accepting TCP/IP connections?
#7 0.188 connection to server at "localhost" (::1), port 7654 failed: Cannot assign requested address
#7 0.188    Is the server running on that host and accepting TCP/IP connections?
------

3

Answers


  1. Chosen as BEST ANSWER

    Here is a complete answer based on the concepts of slava-kuravsky and mello:

    $ docker build --tag host --file Dockerfile .
    $ docker run -d -t --name pg host
    $ docker exec pg bash run.sh
    

    The script can do what I want, currently it only lists the databases:

    $ cat run.sh # <--- copied during docker build                                 
    echo "Hello Postgres World"
    psql --host=localhost -l
    

    The Dockerfile does only initialization:

    $ cat Dockerfile 
    FROM postgres
    USER postgres
    COPY run.sh . # <--- the testing script
    RUN sleep 2   # <--- probably not needed anymore
    RUN initdb
    RUN sleep 3   # <--- probably not needed anymore
    

    When I perform the three commands above I get what I expect 🙂 :

    $ docker build --tag host --file Dockerfile .
    [+] Building 7.2s (10/10) FINISHED                                                                                                     
    # ... omitted for brevity ...
    $ docker run -d -t --name pg host       
    608ac7324e838924c9a5d0cfe65c8000e33350b86faf9df4511ef5fcf7440597
    $ docker exec pg bash run.sh                 
    Hello Postgres World
                                                    List of databases
       Name    |  Owner   | Encoding |  Collate   |   Ctype    | ICU Locale | Locale Provider |   Access privileges   
    -----------+----------+----------+------------+------------+------------+-----------------+-----------------------
     postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            | 
     template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            | =c/postgres          +
               |          |          |            |            |            |                 | postgres=CTc/postgres
     template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            | =c/postgres          +
               |          |          |            |            |            |                 | postgres=CTc/postgres
    (3 rows)
    

  2. During the build step of the postgres Docker you cannot run postgres commands. Postgres database will only be available after you run the Docker.
    As specified in the postgres Docker documentation you can add customization to your postgres instance through scripts placed in docker-entrypoint-initdb.d directory.

    Alternatively you could use a RUN directive to start the postgres database and after that run the postgres commands you want (making sure to wait for the DB to accept connections), as mentioned here.

    Another side note, I personally avoid using real databases for unit testing applications. To me, it’s always better to mock the database for unit tests, in python you can do this with unittest.mock.

    Login or Signup to reply.
  3. Postgres database starts only after you create a container based on postgres image. docker build process doesn’t start entrypoint script. You might need a bash script or CI pipeline where you firstly start postgres container and then use it in your unit tests

    $ docker run --name mypg -p 5432:5432 -e POSTGRES_PASSWORD=mypgpass -d postgres:9
    # copy a script to the mypg container
    $ docker cp run.sh mypg:/root/run.sh
    # run the script
    $ docker exec mypg bash /root/run.sh
    ...
    # use postgres client on your host to connect to mypg container
    $ PGPASSWORD="mypgpass" psql -U postgres -p 5432 -h localhost -c "select version()"
                                                                   version
    --------------------------------------------------------------------------------------------------------------------------------------
     PostgreSQL 9.6.24 on x86_64-pc-linux-gnu (Debian 9.6.24-1.pgdg90+1), compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit
    (1 row)
    

    Postgres container docs

    Postgres client authentication

    EDIT:

    By trying to run initdb, psql etc directly in Dockerfile, you are reinventing the docker-entrypoint.sh

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search