Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(influxdb): Respect custom waitStrategy #2845

Merged
merged 11 commits into from
Nov 20, 2024
76 changes: 39 additions & 37 deletions modules/influxdb/influxdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package influxdb

import (
"context"
"encoding/json"
"fmt"
"io"
"path"
"strings"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
Expand Down Expand Up @@ -34,7 +35,7 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
"INFLUXDB_HTTP_HTTPS_ENABLED": "false",
"INFLUXDB_HTTP_AUTH_ENABLED": "false",
},
WaitingFor: wait.ForListeningPort("8086/tcp"),
WaitingFor: waitForHttpHealth(),
}
genericContainerReq := testcontainers.GenericContainerRequest{
ContainerRequest: req,
Expand All @@ -47,38 +48,6 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
}
}

hasInitDb := false

for _, f := range genericContainerReq.Files {
if f.ContainerFilePath == "/" && strings.HasSuffix(f.HostFilePath, "docker-entrypoint-initdb.d") {
// Init service in container will start influxdb, run scripts in docker-entrypoint-initdb.d and then
// terminate the influxdb server, followed by restart of influxdb. This is tricky to wait for, and
// in this case, we are assuming that data was added by init script, so we then look for an
// "Open shard" which is the last thing that happens before the server is ready to accept connections.
// This is probably different for InfluxDB 2.x, but that is left as an exercise for the reader.
strategies := []wait.Strategy{
genericContainerReq.WaitingFor,
wait.ForLog("influxdb init process in progress..."),
wait.ForLog("Server shutdown completed"),
wait.ForLog("Opened shard"),
}
genericContainerReq.WaitingFor = wait.ForAll(strategies...)
hasInitDb = true
break
}
}

if !hasInitDb {
if lastIndex := strings.LastIndex(genericContainerReq.Image, ":"); lastIndex != -1 {
tag := genericContainerReq.Image[lastIndex+1:]
if tag == "latest" || tag[0] == '2' {
genericContainerReq.WaitingFor = wait.ForLog(`Listening log_id=[0-9a-zA-Z_~]+ service=tcp-listener transport=http`).AsRegexp()
}
} else {
genericContainerReq.WaitingFor = wait.ForLog("Listening for signals")
}
}

container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
var c *InfluxDbContainer
if container != nil {
Expand Down Expand Up @@ -147,9 +116,22 @@ func WithConfigFile(configFile string) testcontainers.CustomizeRequestOption {
}
}

// WithInitDb will copy a 'docker-entrypoint-initdb.d' directory to the container.
// The secPath is the path to the directory on the host machine.
// The directory will be copied to the root of the container.
// WithInitDb copies a 'docker-entrypoint-initdb.d' directory from the specified host path to the root of the container.
// The `srcPath` parameter should point to the directory containing initialization files on the host.
//
// Initialization Process in the Container:
// 1. The copied 'docker-entrypoint-initdb.d' directory contains scripts that initialize the database.
// 2. On container start, InfluxDB runs, executes the scripts in 'docker-entrypoint-initdb.d', and then shuts down.
// 3. The InfluxDB server restarts automatically after initialization to make the new data available.
//
// Note: This approach assumes the initialization completes on startup and the data is properly added.
// This behavior may differ in InfluxDB 2.x and may require additional handling.
//
// Parameters:
// - srcPath: The host path to the directory containing initialization scripts.
//
// Returns:
// - testcontainers.CustomizeRequestOption: An option to customize the container request.
marcinmilewski93 marked this conversation as resolved.
Show resolved Hide resolved
func WithInitDb(srcPath string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) error {
cf := testcontainers.ContainerFile{
Expand All @@ -158,6 +140,26 @@ func WithInitDb(srcPath string) testcontainers.CustomizeRequestOption {
FileMode: 0o755,
}
req.Files = append(req.Files, cf)

strategies := []wait.Strategy{
wait.ForLog("Server shutdown completed"),
waitForHttpHealth(),
}
req.WaitingFor = wait.ForAll(strategies...)
marcinmilewski93 marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
}

func waitForHttpHealth() *wait.HTTPStrategy {
return wait.ForHTTP("/health").
WithResponseMatcher(func(body io.Reader) bool {
decoder := json.NewDecoder(body)
r := struct {
Status string `json:"status"`
}{}
if err := decoder.Decode(&r); err != nil {
return false
}
return r.Status == "pass"
})
}