4

I have some integration tests that, in order to succesfully run, require a running postgres database, setup via docker-compose, and my go app running from main.go. Here is my docker-compose:

version: "3.9"
services:

  postgres:
    image: postgres:12.5
    user: postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: my-db
    ports:
      - "5432:5432"
    volumes:
      - data:/var/lib/postgresql/data
      - ./initdb:/docker-entrypoint-initdb.d
networks:
  default:
    driver: bridge

volumes:
  data:
    driver: local

and my Github Actions are as follows:

jobs:
  unit:
    name: Test
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:12.5
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: password
          POSTGRES_DB: my-db
        ports:
          - 5432:5432
    env:
      GOMODCACHE: "${{ github.workspace }}/.go/mod/cache"
      TEST_RACE: true 
   steps:
     - name: Initiate Database
       run: psql -f initdb/init.sql postgresql://postgres:password@localhost:5432/my-db

     - name: Set up Cloud SDK
       uses: google-github-actions/setup-gcloud@v0

     - name: Authenticate with GCP
       id: auth
       uses: "google-github-actions/auth@v0"
       with: credentials_json: ${{ secrets.GCP_ACTIONS_SECRET }}

     - name: Configure Docker
       run: |
         gcloud auth configure-docker "europe- docker.pkg.dev,gcr.io,eu.gcr.io"

     - name: Set up Docker BuildX
       uses: docker/setup-buildx-action@v1

     - name: Start App
       run: |
         VERSION=latest make images
         docker run -d -p 3000:3000 -e      POSTGRES_DB_URL='//postgres:password@localhost:5432/my-db?sslmode=disable' --name='app' image/app

     - name: Tests
       env:
        POSTGRES_DB_URL: //postgres:password@localhost:5432/my-db?sslmode=disable
      GOMODCACHE: ${{ github.workspace }}/.go/pkg/mod
       run: | 
         make test-integration
         docker stop app

My tests run just fine locally firing off the docker-compose with docker-compose up and running the app from main.go. However, in Github actions I am getting the following error:

failed to connect to `host=/tmp user=nonroot database=`: dial error (dial unix /tmp/.s.PGSQL.5432: connect: no such file or directory

What am I missing? Thanks

panza
  • 1,341
  • 7
  • 38
  • 68
  • Not quite sure this has any relation to the issue:https://github.com/jackc/pgx/issues/596 – panza Nov 01 '22 at 17:54
  • look at https://stackoverflow.com/questions/13410686/postgres-could-not-connect-to-server – kozmo Nov 03 '22 at 04:30
  • Thanks, this is more brew specific and has no mentioned of Github actions. I went through a bunch of them and applied ```-h localhost```, however the Github actions element is missing and I did not reach a solution – panza Nov 03 '22 at 07:51
  • If, on GitHub, your app is running inside a docker container then 'localhost' is local to the container. But psql is not running inside the container. You could run psql as a docker container instead and have the 2 containers use the same docker network. Now you should be able to use the name of the psql container as the hostname in your DB_URL. – Eelco Nov 03 '22 at 07:55
  • Thanks, are you suggesting to have my db initialised inside Start App? – panza Nov 03 '22 at 08:00
  • You may try to add `--network=host` to your `docker run` command. That way the docker container will bind directly to the host network and you will be able to connect to services running on the host via `localhost`. Not sure if that allowed in GitHub actions though. – Eelco Nov 03 '22 at 09:34
  • Hey @panza can you please check out my answer? I've added everything you might need to solve your problem. – mrkachariker Nov 07 '22 at 17:00

2 Answers2

4

What you are missing is setting up the actual Postgres Client inside the Github Actions server (that is why there is no psql tool to be found).

Set it up as a step.

     - name: Install PostgreSQL client
        run: |
          apt-get update
          apt-get install --yes postgresql-client

Apart from that, if you run everything through docker-compose you will need to wait for postgres to be up and running (healthy & accepting connections).

Consider the following docker-compose:

version: '3.1'

services:
  api:
    build: .
    depends_on:
      - db
    ports:
      - 8080:8080
    environment:
      - RUN_UP_MIGRATION=true
      - PSQL_CONN_STRING=postgres://gotstock_user:123@host.docker.internal:5432/gotstockapi?sslmode=disable
    command: ./entry
    
  db:
    image: postgres:9.5-alpine
    restart: always
    environment:
      - POSTGRES_USER=root
      - POSTGRES_PASSWORD=password
    ports:
      - "5432:5432"
    volumes:
      - ./db:/docker-entrypoint-initdb.d/

There are a couple of things you need to notice. First of all, in the environment section of the api we have PSQL_CONN_STRING=postgres://gotstock_user:123@host.docker.internal:5432/gotstockapi?sslmode=disable which is the connection string to the db being passed as an env variable. Notice the host is host.docker.internal.

Besides that we have command: ./entry in the api section. The entry file contains the following #!/bin/ash script:

#!/bin/ash
NOT_READY=1
while [ $NOT_READY -gt 0 ] # <- loop that waits till postgres is ready to accept connections
do 
    pg_isready --dbname=gotstockapi --host=host.docker.internal --port=5432 --username=gotstock_user
    NOT_READY=$?
    sleep 1
done;
./gotstock-api # <- actually executes the build of the api
sleep 10
go test -v ./it # <- executes the integration-tests

And finally, in order for the psql client to work in the above script, the docker file of the api is looking like this:


# syntax=docker/dockerfile:1

FROM golang:1.19-alpine3.15

RUN apk add build-base

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download && go mod verify

COPY . .

RUN apk add postgresql-client

RUN go build -o gotstock-api

EXPOSE 8080

Notice RUN apk add postgresql-client which installs the client.

Happy hacking! =)

mrkachariker
  • 411
  • 4
  • 9
  • This will not work, as the app is run in docker it will still be unable to access the databse. – Dennis van de Hoef - Xiotin Nov 05 '22 at 07:38
  • The first step he does is: `steps: - name: Initiate Database run: psql -f initdb/init.sql postgresql://postgres:password@localhost:5432/my-db ` so I was thinking maybe he would need to install the client in order to make that work (if that indeed was the issue as it is not very clear from the question). – mrkachariker Nov 05 '22 at 11:49
3

I think this code has more than one problem.

Problem one: In your code I don't see you run docker-compose up, therefore I would assume that Postgres is not running.

Problem two: is in this line: docker run -d -p 3000:3000 -e POSTGRES_DB_URL='//postgres:password@localhost:5432/my-app?sslmode=disable' --name='app' image/app

You point the host of Postgres to localhost, which on your local machine works. As there localhost is your local comuter. Though, as you use docker run you are not running this on your local machine, but in a docker container. There localhost is pointing to inside the conmtainer.

Posible solution for both

As you are already using docker-compose I suggest you to also add your test web server there.

Change your docker-compose file to:

version: "3.9"
services:

  webapp:
    build: image/app
    environment:
      POSTGRES_DB_URL='//postgres:password@postgres:5432/my-app?sslmode=disable'
    ports:
      - "3000:3000"
    depends_on:
      - "postgres"

  postgres:
    image: postgres:12.5
    user: postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: my-app
    ports:
      - "5432:5432"
    volumes:
      - data:/var/lib/postgresql/data
      - ./initdb:/docker-entrypoint-initdb.d
networks:
  default:
    driver: bridge

volumes:
  data:
    driver: local

If you now run docker-compose up, both services will be available. And it should work. Though I am not a github-actions expert, so I might have missed something. At least like this, you can run your tests locally the same way as in CI, something that I always see as a big plus.

  • Thanks, I have added the service as it is there and updated the naming across. I am indeed starting postgres in the ```services``` section – panza Nov 03 '22 at 08:59
  • I am able to stand up my db and create the schema locally with docker-compose and I run the web app independently. In Github actions, the app correctly started as well postgres and it creates all the table. The issue is with connecting to it – panza Nov 03 '22 at 09:02
  • How are you now connecting to it? As the docker run command can't work for the explained reason. – Dennis van de Hoef - Xiotin Nov 03 '22 at 09:13
  • I creating a new connection and repository inside my tests pointing to the same database string used in my Github actions. How your solution would work? I can see locally as my ```docker-compose up``` will stand up both service and database, but how can I utilised that in my Github actions? My ```services``` section will simply have the new version of the docker-compose file? Thanks – panza Nov 03 '22 at 09:20
  • Your problem is that, as long as you use docker run, the database string will not work. As you are in an isolated container and localhost is inside the container. With my proposed solution, you use docker compose and in the connection string the name of the container inside the compose network. instead of localhost. – Dennis van de Hoef - Xiotin Nov 03 '22 at 13:02