Home > Software engineering >  How to start a clickhouse container with testcontainers?
How to start a clickhouse container with testcontainers?

Time:09-09

I'd like to use testcontainers for integration testing. I need to test against a clickhouse storage.

The docker image is yandex/clichouse-server

My code thus far (imported mostly from the official redis example on testcontainers website):

    ctx := context.Background()
    req := testcontainers.ContainerRequest{
        Image: "yandex/clickhouse-server",
        ExposedPorts: []string{"9000/tcp"},
    }
    chContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
        ContainerRequest: req,
        Started:          true,
    })
    require.NoError(t, err, "unexpected error while creating clickhouse container")

    endpoint, err := chContainer.Endpoint(ctx, "")
    require.NoError(t, err)

This throws an error port not found on getting the endpoint, and I'm not sure where to go from there.

CodePudding user response:

Have you tried using the wait APIs in Testcontainers Go? https://github.com/testcontainers/testcontainers-go/tree/main/wait

With them you'll be able to wait for multiple things (even at the same time):

  • a log entry
  • a port to be ready
  • a SQL query
  • an HTTP request
  • an exit code after running a program in the container

You can find useful examples in the repository. I.e., and example for a log entry:

ctx := context.Background()
    req := ContainerRequest{
        Image:        "docker.io/mysql:latest",
        ExposedPorts: []string{"3306/tcp", "33060/tcp"},
        Env: map[string]string{
            "MYSQL_ROOT_PASSWORD": "password",
            "MYSQL_DATABASE":      "database",
        },
        WaitingFor: wait.ForLog("test context timeout").WithStartupTimeout(1 * time.Second),
    }
    _, err := GenericContainer(ctx, GenericContainerRequest{
        ProviderType:     providerType,
        ContainerRequest: req,
        Started:          true,
    })

EDIT: a more elaborated example, including a wait strategy using HTTP requests would be:

const (
        dbName       = "crazy"
        fakeUser     = "jondoe"
        fakePassword = "bond girl"
    )

    ctx := context.Background()

    req := ContainerRequest{
        Image: "clickhouse/clickhouse-server",
        Env: map[string]string{
            "CLICKHOUSE_DB":       dbName,
            "CLICKHOUSE_USER":     fakeUser,
            "CLICKHOUSE_PASSWORD": fakePassword,
        },
        ExposedPorts: []string{
            "8123/tcp",
            "9000/tcp",
        },
        WaitingFor: wait.ForAll(
            wait.ForHTTP("/ping").WithPort("8123/tcp").WithStatusCodeMatcher(
                func(status int) bool {
                    return status == http.StatusOK
                },
            ),
        ),
    }

    clickhouseContainer, err := GenericContainer(ctx, GenericContainerRequest{
        ContainerRequest: req,
        Started:          true,
    })
    if err != nil {
        t.Fatal(err)
    }

    defer clickhouseContainer.Terminate(ctx)

CodePudding user response:

Here is what I got working after trial / errors:

const (
    dbName       = "crazy"
    fakeUser     = "jondoe"
    fakePassword = "bond girl"
)

// NewTestClickhouseDB spins up a new clickhouse container database
func NewTestClickhouseDB(t *testing.T) *sql.DB {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    t.Cleanup(cancel)
    req := testcontainers.ContainerRequest{
        Image: "clickhouse/clickhouse-server",
        Env: map[string]string{
            "CLICKHOUSE_DB":       dbName,
            "CLICKHOUSE_USER":     fakeUser,
            "CLICKHOUSE_PASSWORD": fakePassword,
        },

        ExposedPorts: []string{"9000/tcp"},
        WaitingFor:   wait.ForListeningPort("9000"),
    }
    chContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
        ContainerRequest: req,
        Started:          true,
    })
    require.NoError(t, err, "unexpected error while creating clickhouse container")

    p, err := chContainer.MappedPort(ctx, "9000")
    require.NoError(t, err, "expected mapped port to be found on clickhouse container")

    addr := fmt.Sprintf("127.0.0.1:%d", p.Int())
    conn := clickhouse.OpenDB(&clickhouse.Options{
        Addr: []string{addr},
        Auth: clickhouse.Auth{
            Database: dbName,
            Username: fakeUser,
            Password: fakePassword,
        },
    })

    for {
        if ctx.Err() != nil {
            t.Fatalf("time/out: ping db failed for 10seconds")
        }
        err := conn.Ping()
        if err != nil {
            time.Sleep(10 * time.Millisecond)
            continue
        }
        break
    }

    return conn
}

This spins up a clickhouse container and returns the sql.Db or t/o after 10 seconds.

  •  Tags:  
  • go
  • Related