skip to Main Content

I am trying to create some tests in my microservice, I want to create a network, attach my db testcontainer (postgres) and my microservice testcontainer to that. Whatever I try i just cant get my microservice to connect to the database. My microservice is golang using fiber and gorm. I try to connect to the database in a db.go config file that looks like :

func SetupDB(port string, host string) *gorm.DB {

    dsn := "host=" + host + " user=postgres password=password dbname=prescription port=" + port + " sslmode=disable"

    db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})

    if err != nil {
        panic("error connecting to database")
    }

    db.AutoMigrate(&model.Prescription{})

    return db
}

This is what my testcontainers look like:

    prescriptionDBContainer, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{
        ContainerRequest: testcontainers.ContainerRequest{
            Image:        "postgres",
            ExposedPorts: []string{postgresPort.Port()},
            Env: map[string]string{
                "POSTGRES_USER":     "postgres",
                "POSTGRES_PASSWORD": "password",
                "POSTGRES_DB":       "prescription",
            },
            Networks: []string{network.Name},
            NetworkAliases: map[string][]string{
                network.Name: {"db-network"},
            },
            WaitingFor: wait.ForAll(
                wait.ForLog("database system is ready to accept connections"),
                wait.ForListeningPort(postgresPort),
            ),
        },
        Started: true,
    })
prescriptionContainer, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{
        ContainerRequest: testcontainers.ContainerRequest{
            FromDockerfile: testcontainers.FromDockerfile{Context: "../../../../prescription"},
            Networks:       []string{network.Name},
            NetworkAliases: map[string][]string{
                network.Name: {"db-network"},
            },
            Env: map[string]string{
                "POSTGRES_USER":     "postgres",
                "POSTGRES_PASSWORD": "password",
                "POSTGRES_DB":       "prescription",
                "HOST":              prescriptionDBHost,
                "DB_PORT":           prescriptionDBPort.Port(),
            },
            ExposedPorts: []string{pMicroPort.Port()},
            WaitingFor:   wait.ForListeningPort("8080"),
        },
        Started: true,
    })

Maybe its because i dont fundamentally understand something that goes on during networking in docker, but I am really lost, when i set the env for HOST and DB_PORT (i’ve tried every combination under the sun), it refuses to connect the microservice to the database

in the testcontainer for my microservice I’ve tried :

"HOST":              prescriptionDBHost,
"DB_PORT":           prescriptionDBPort.Port(),

prescriptionDBHost is extracted by :

prescriptionDBHost, err := prescriptionDBContainer.Name(context.Background())

that resulted in the error message :

failed to initialize database, got error failed to connect to `host=/stoic_heyrovsky user=postgres database=prescription`: dial error (dial unix /stoic_heyrovsky/.s.PGSQL.53802: connect: no such file or directory)
panic: error connecting to database

I then tried to trim the "/" from the hostname such as:

"HOST":              strings.Trim(prescriptionDBHost,"/"),
"DB_PORT":           prescriptionDBPort.Port(),

I’ve also tried :

"HOST":              "localhost",
"DB_PORT":           prescriptionDBPort.Port(),
"HOST":              "127.0.0.1",
"DB_PORT":           prescriptionDBPort.Port(),
prescriptionDBHost, err := prescriptionDBContainer.ContainerIP(context.Background())

"HOST":              prescriptionDBHost,
"DB_PORT":           prescriptionDBPort.Port(),

The last 4 examples here have all result in some sort of dial tcp error for example like:

failed to initialize database, got error failed to connect to `host=localhost user=postgres database=prescription`: dial error (dial tcp [::1]:53921: connect: cannot assign requested address)

I’ve also debugged and stopped after the testcontainer created the database container, then went to my microservice and hardcoded connecting to that container using DB_HOST=localhost and port= and that was successful so I’m really lost in what is going wrong. The only thing I can think of is that the microservice container is not being attached to the network before it tries to connect to the database? I did a docker network inspect and i can see that the database container is attached but the microservice never gets attached (but maybe that’s just because of something else?).

2

Answers


  1. You can do something like this:

    prescriptionDBContainer, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{
        ContainerRequest: testcontainers.ContainerRequest{
            Image:        "postgres",
            ExposedPorts: []string{"5432/tcp"},
            Env: map[string]string{
                "POSTGRES_USER":     "postgres",
                "POSTGRES_PASSWORD": "password",
                "POSTGRES_DB":       "prescription",
            },
            Networks:       []string{networkName},
            NetworkAliases: map[string][]string{networkName: []string{"postgres"}},
            WaitingFor: wait.ForAll(
                wait.ForLog("database system is ready to accept connections"),
                wait.ForListeningPort("5432/tcp"),
            ),
        },
        Started: true,
    })
    if err != nil {
        t.Fatal(err)
    }
    
    prescriptionContainer, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{
        ContainerRequest: testcontainers.ContainerRequest{
            FromDockerfile: testcontainers.FromDockerfile{Context: "./testapp"},
            ExposedPorts:   []string{"8080/tcp"},
            Networks:       []string{networkName},
            NetworkAliases: map[string][]string{networkName: []string{"blah"}},
            Env: map[string]string{
                "DATABASE_URL": "postgres://postgres:password@postgres:5432/prescription",
            },
            WaitingFor: wait.ForListeningPort("8080/tcp"),
        },
        Started: true,
    })
    

    Note the way NetworkAliases is configured; in your code you are setting both to db-network but, I guess, this is due to a misunderstanding. The setting configures an alias that the container can be referenced as (in this case I’m using postgres for the postgres container; that means that when connecting HOST would be postgres as per the URL used in the above example).

    As an alternative you could use port, err := prescriptionDBContainer.MappedPort(context.Background(), "5432/tcp") to get the port exposed on the host and then connect to host.docker.internal on port port.Port(). This method is frequently used when the app being tested is running on the host rather than in a container (but in that case you would connect to localhost and use the report returned from MappedPort()).

    Login or Signup to reply.
  2. You could leverage the existing postgres module and the functional option to create a network including aliases: https://golang.testcontainers.org/modules/postgres/#withnetwork

    ctx := context.Background()
    
    dbName := "users"
    dbUser := "user"
    dbPassword := "password"
    
    postgresContainer, err := postgres.RunContainer(ctx,
        testcontainers.WithImage("docker.io/postgres:15.2-alpine"),
        postgres.WithInitScripts(filepath.Join("testdata", "init-user-db.sh")),
        postgres.WithConfigFile(filepath.Join("testdata", "my-postgres.conf")),
        postgres.WithDatabase(dbName),
        postgres.WithUsername(dbUser),
        postgres.WithPassword(dbPassword),
        testcontainers.WithWaitStrategy(
            wait.ForLog("database system is ready to accept connections").
                WithOccurrence(2).
                WithStartupTimeout(5*time.Second)),
    )
    if err != nil {
        log.Fatalf("failed to start container: %s", err)
    }
    
    // Clean up the container
    defer func() {
        if err := postgresContainer.Terminate(ctx); err != nil {
            log.Fatalf("failed to terminate container: %s", err)
        }
    }()
    

    Because containers would live in the same network, they can see each other by host and known ports.

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