From 1194362e080574f07e275453294454f60fff7752 Mon Sep 17 00:00:00 2001 From: bdular Date: Mon, 31 Mar 2025 21:54:46 +0200 Subject: [PATCH 1/2] feat: influxdb v2 support --- docs/modules/influxdb.md | 23 +- modules/influxdb/examples_test.go | 60 +++++ modules/influxdb/go.mod | 4 + modules/influxdb/go.sum | 17 +- modules/influxdb/influxdb.go | 138 ++++++++++ modules/influxdb/influxdb_test.go | 410 ++++++++++++++++++++++++++++++ 6 files changed, 647 insertions(+), 5 deletions(-) diff --git a/docs/modules/influxdb.md b/docs/modules/influxdb.md index 2243f40dab..8df96babfc 100644 --- a/docs/modules/influxdb.md +++ b/docs/modules/influxdb.md @@ -4,7 +4,7 @@ Since testcontainers-go :material-tag: main + +When running the InfluxDB V2 image, you can override the default configuration by using options prefixed by `influxdb.WithV2`. +The following options are available: + +- `WithV2(org, bucket string)`: Configures organization and bucket name. This option is required to run the InfluxDB V2 image. +- `WithV2Auth(org, bucket, username, password string)`: Sets the username and password for the initial user. +- `WithV2SecretsAuth(org, bucket, usernameFile, passwordFile string)`: Sets the username and password file path. +- `WithV2Retention(retention time.Duration)`: Sets the default bucket retention policy. +- `WithV2AdminToken(token string)`: Sets the admin token for the initial user. +- `WithV2SecretsAdminToken(tokenFile string)`: Sets the admin token file path. + #### Init Scripts While the InfluxDB image will obey the `/docker-entrypoint-initdb.d` directory as is common, that directory does not diff --git a/modules/influxdb/examples_test.go b/modules/influxdb/examples_test.go index 30c892537f..ddffec142b 100644 --- a/modules/influxdb/examples_test.go +++ b/modules/influxdb/examples_test.go @@ -5,6 +5,8 @@ import ( "fmt" "log" + influxclient2 "github.com/influxdata/influxdb-client-go/v2" + "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/influxdb" ) @@ -41,3 +43,61 @@ func ExampleRun() { // Output: // true } + +func ExampleRun_v2() { + // runInfluxV2Container { + ctx := context.Background() + + username := "username" + password := "password" + org := "org" + bucket := "bucket" + token := "influxdbv2token" + + influxdbContainer, err := influxdb.Run(ctx, "influxdb:2.7.11", + influxdb.WithV2Auth(org, bucket, username, password), // Set the username and password + influxdb.WithV2AdminToken(token), // Set the admin token + ) + defer func() { + if err := testcontainers.TerminateContainer(influxdbContainer); err != nil { + log.Printf("failed to terminate container: %s", err) + } + }() + if err != nil { + log.Printf("failed to start container: %s", err) + return + } + // } + + state, err := influxdbContainer.State(ctx) + if err != nil { + log.Printf("failed to get container state: %s", err) + return + } + + fmt.Println(state.Running) + + // Query the InfluxDB API to verify the setup + url, err := influxdbContainer.ConnectionUrl(ctx) + if err != nil { + log.Printf("failed to get host: %s", err) + return + } + + // Initialize a new InfluxDB client + client := influxclient2.NewClientWithOptions(url, token, influxclient2.DefaultOptions()) + defer client.Close() + + // Get the bucket + influxBucket, err := client.BucketsAPI().FindBucketByName(ctx, bucket) + if err != nil { + log.Printf("failed to get bucket: %s", err) + return + } + + fmt.Println(influxBucket.Name) + + // Output: + // true + // bucket +} diff --git a/modules/influxdb/go.mod b/modules/influxdb/go.mod index 7ffa49aa80..4125782a02 100644 --- a/modules/influxdb/go.mod +++ b/modules/influxdb/go.mod @@ -5,6 +5,7 @@ go 1.23.0 toolchain go1.23.6 require ( + github.com/influxdata/influxdb-client-go/v2 v2.14.0 github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.36.0 @@ -14,6 +15,7 @@ require ( dario.cat/mergo v1.0.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect @@ -30,6 +32,7 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/klauspost/compress v1.17.4 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.9 // indirect @@ -40,6 +43,7 @@ require ( github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/oapi-codegen/runtime v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/modules/influxdb/go.sum b/modules/influxdb/go.sum index f7dd1dc695..e804c97fd7 100644 --- a/modules/influxdb/go.sum +++ b/modules/influxdb/go.sum @@ -6,6 +6,10 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -47,8 +51,13 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjwJdUHnwvfjMF71M1iI4= +github.com/influxdata/influxdb-client-go/v2 v2.14.0/go.mod h1:Ahpm3QXKMJslpXl3IftVLVezreAUtBOTZssDrjZEFHI= github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= @@ -75,6 +84,8 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= +github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -91,9 +102,11 @@ github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0Zqm github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -156,8 +169,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= diff --git a/modules/influxdb/influxdb.go b/modules/influxdb/influxdb.go index 5b03334db4..39f77c0485 100644 --- a/modules/influxdb/influxdb.go +++ b/modules/influxdb/influxdb.go @@ -3,9 +3,11 @@ package influxdb import ( "context" "encoding/json" + "errors" "fmt" "io" "path" + "time" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" @@ -120,6 +122,142 @@ func WithConfigFile(configFile string) testcontainers.CustomizeRequestOption { } } +// withV2 configures the influxdb container to be compatible with InfluxDB v2 +func withV2(req *testcontainers.GenericContainerRequest, org, bucket string) error { + if org == "" { + return errors.New("organization name is required") + } + + if bucket == "" { + return errors.New("bucket name is required") + } + + req.Env["DOCKER_INFLUXDB_INIT_ORG"] = org + req.Env["DOCKER_INFLUXDB_INIT_BUCKET"] = bucket + req.Env["DOCKER_INFLUXDB_INIT_MODE"] = "setup" // Always setup, we wont be migrating from v1 to v2 + return nil +} + +// WithV2 configures the influxdb container to be compatible with InfluxDB v2 +func WithV2(org, bucket string) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) error { + err := withV2(req, org, bucket) + if err != nil { + return err + } + + return nil + } +} + +const dockerSecretPath = "/run/secrets" + +func secretsPath(path string) string { + return dockerSecretPath + "/" + path +} + +// WithV2Auth configures the influxdb container to be compatible with InfluxDB v2 and sets the username and password +// for the initial user. +func WithV2Auth(org, bucket, username, password string) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) error { + if username == "" { + return errors.New("username is required") + } + + if password == "" { + return errors.New("password is required") + } + + err := withV2(req, org, bucket) + if err != nil { + return err + } + + if req.Env["DOCKER_INFLUXDB_INIT_USERNAME_FILE"] != "" || + req.Env["DOCKER_INFLUXDB_INIT_PASSWORD_FILE"] != "" { + return errors.New("username and password file already set, use either WithV2Auth or WithV2SecretsAuth") + } + + req.Env["DOCKER_INFLUXDB_INIT_USERNAME"] = username + req.Env["DOCKER_INFLUXDB_INIT_PASSWORD"] = password + return nil + } +} + +// WithV2SecretsAuth configures the container to be compatible with InfluxDB v2 and sets the username and password file path +func WithV2SecretsAuth(org, bucket, usernameFile, passwordFile string) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) error { + if usernameFile == "" { + return errors.New("username file is required") + } + + if passwordFile == "" { + return errors.New("password file is required") + } + + if req.Env["DOCKER_INFLUXDB_INIT_USERNAME"] != "" || + req.Env["DOCKER_INFLUXDB_INIT_PASSWORD"] != "" { + return errors.New("username and password already set, use either WithV2Auth or WithV2SecretsAuth") + } + + err := withV2(req, org, bucket) + if err != nil { + return err + } + + req.Env["DOCKER_INFLUXDB_INIT_USERNAME_FILE"] = secretsPath(usernameFile) + req.Env["DOCKER_INFLUXDB_INIT_PASSWORD_FILE"] = secretsPath(passwordFile) + return nil + } +} + +// WithV2Retention configures the default bucket's retention +func WithV2Retention(retention time.Duration) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) error { + if retention == 0 { + return errors.New("retention is required") + } + + req.Env["DOCKER_INFLUXDB_INIT_RETENTION"] = retention.String() + + return nil + } +} + +// WithV2AdminToken sets the admin token for the influxdb container +func WithV2AdminToken(token string) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) error { + if token == "" { + return errors.New("admin token is required") + } + + if req.Env["DOCKER_INFLUXDB_INIT_ADMIN_TOKEN_FILE"] != "" { + return errors.New("admin token file already set, use either WithV2AdminToken or WithV2SecretsAdminToken") + } + + req.Env["DOCKER_INFLUXDB_INIT_ADMIN_TOKEN"] = token + + return nil + } +} + +// WithV2SecretsAdminToken sets the admin token for the influxdb container using a file +func WithV2SecretsAdminToken(tokenFile string) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) error { + if tokenFile == "" { + return errors.New("admin token file is required") + } + + if req.Env["DOCKER_INFLUXDB_INIT_ADMIN_TOKEN"] != "" { + return errors.New("admin token already set, use either WithV2AdminToken or WithV2SecretsAdminToken") + } + + req.Env["DOCKER_INFLUXDB_INIT_ADMIN_TOKEN_FILE"] = secretsPath(tokenFile) + + return nil + } +} + // WithInitDb returns a request customizer that initialises the database using the file `docker-entrypoint-initdb.d` // located in `srcPath` directory. // diff --git a/modules/influxdb/influxdb_test.go b/modules/influxdb/influxdb_test.go index 3211c88ddc..bf11375e31 100644 --- a/modules/influxdb/influxdb_test.go +++ b/modules/influxdb/influxdb_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + influxdb2 "github.com/influxdata/influxdb-client-go/v2" influxclient "github.com/influxdata/influxdb1-client/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -44,6 +45,415 @@ func TestV2Container(t *testing.T) { require.Truef(t, state.Running, "InfluxDB container is not running") } +func TestV2Options(t *testing.T) { + // Ok base cases + t.Run("with-username-password", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2Auth("org", "bucket", "username", "password") + _ = opt(genericReq) + + require.Equal(t, map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + "DOCKER_INFLUXDB_INIT_MODE": "setup", + "DOCKER_INFLUXDB_INIT_USERNAME": "username", + "DOCKER_INFLUXDB_INIT_PASSWORD": "password", + "DOCKER_INFLUXDB_INIT_ORG": "org", + "DOCKER_INFLUXDB_INIT_BUCKET": "bucket", + }, genericReq.Env) + }) + + t.Run("with-org-bucket", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2("org", "bucket") + _ = opt(genericReq) + + require.Equal(t, map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + "DOCKER_INFLUXDB_INIT_MODE": "setup", + "DOCKER_INFLUXDB_INIT_ORG": "org", + "DOCKER_INFLUXDB_INIT_BUCKET": "bucket", + }, genericReq.Env) + }) + + t.Run("with-auth-token", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2AdminToken("token") + _ = opt(genericReq) + + require.Equal(t, map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + "DOCKER_INFLUXDB_INIT_ADMIN_TOKEN": "token", + }, genericReq.Env) + }) + + t.Run("with-retention", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2Retention(time.Hour * 24) + _ = opt(genericReq) + + require.Equal(t, map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + "DOCKER_INFLUXDB_INIT_RETENTION": "24h0m0s", + }, genericReq.Env) + }) + + t.Run("with-token-file", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2SecretsAdminToken("file") + _ = opt(genericReq) + + require.Equal(t, map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + "DOCKER_INFLUXDB_INIT_ADMIN_TOKEN_FILE": "/run/secrets/file", + }, genericReq.Env) + }) + + t.Run("with-username-and-password-file", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2SecretsAuth("org", "bucket", "file", "file2") + _ = opt(genericReq) + + require.Equal(t, map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + "DOCKER_INFLUXDB_INIT_MODE": "setup", + "DOCKER_INFLUXDB_INIT_USERNAME_FILE": "/run/secrets/file", + "DOCKER_INFLUXDB_INIT_PASSWORD_FILE": "/run/secrets/file2", + "DOCKER_INFLUXDB_INIT_ORG": "org", + "DOCKER_INFLUXDB_INIT_BUCKET": "bucket", + }, genericReq.Env) + }) + + // Empty fields provided as arguments + t.Run("with-empty-username-password", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2Auth("org", "bucket", "", "") + err := opt(genericReq) + require.Error(t, err) + }) + + t.Run("with-empty-org-bucket", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2("", "") + err := opt(genericReq) + require.Error(t, err) + }) + + t.Run("with-empty-auth-token", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2AdminToken("") + err := opt(genericReq) + require.Error(t, err) + }) + + t.Run("with-empty-retention", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2Retention(0) + err := opt(genericReq) + require.Error(t, err) + }) + + t.Run("with-empty-token-file", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2SecretsAdminToken("") + err := opt(genericReq) + require.Error(t, err) + }) + + t.Run("with-empty-username-and-password-file", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + }, + }, + } + + opt := influxdb.WithV2SecretsAuth("org", "bucket", "", "") + err := opt(genericReq) + require.Error(t, err) + }) + + // Conflicts + t.Run("with-token-already-present-conflict", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + "DOCKER_INFLUXDB_INIT_ADMIN_TOKEN": "im a token", + }, + }, + } + + opt := influxdb.WithV2SecretsAdminToken("file") + err := opt(genericReq) + require.Error(t, err) + }) + + t.Run("with-username-password-already-present-conflict", func(t *testing.T) { + genericReq := &testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "influxdb:2.7.5-alpine", + ExposedPorts: []string{"8086/tcp", "8088/tcp"}, + Env: map[string]string{ + "INFLUXDB_BIND_ADDRESS": ":8088", + "INFLUXDB_HTTP_BIND_ADDRESS": ":8086", + "INFLUXDB_REPORTING_DISABLED": "true", + "INFLUXDB_MONITOR_STORE_ENABLED": "false", + "INFLUXDB_HTTP_HTTPS_ENABLED": "false", + "INFLUXDB_HTTP_AUTH_ENABLED": "false", + "DOCKER_INFLUXDB_INIT_USERNAME": "im a username", + "DOCKER_INFLUXDB_INIT_PASSWORD": "im a password", + }, + }, + } + + opt := influxdb.WithV2SecretsAuth("org", "bucket", "file", "file2") + err := opt(genericReq) + require.Error(t, err) + }) +} + +func TestRun_V2WithOptions(t *testing.T) { + ctx := context.Background() + + username := "username" + password := "password" + org := "org" + bucket := "bucket" + token := "influxdbv2token" + + influxdbContainer, err := influxdb.Run(ctx, "influxdb:2.7.11", + // influxdbV2EnvConfig { + influxdb.WithV2Auth(org, bucket, username, password), + influxdb.WithV2AdminToken(token), + // } + ) + testcontainers.CleanupContainer(t, influxdbContainer) + require.NoError(t, err) + + state, err := influxdbContainer.State(ctx) + require.NoError(t, err) + require.True(t, state.Running) + + // Query the InfluxDB API to verify the setup + url, err := influxdbContainer.ConnectionUrl(ctx) + require.NoError(t, err) + + // Initialize a new InfluxDB client + client := influxdb2.NewClientWithOptions(url, token, influxdb2.DefaultOptions()) + defer client.Close() + + // Get the bucket + influxBucket, err := client.BucketsAPI().FindBucketByName(ctx, bucket) + require.NoError(t, err) + require.Equal(t, bucket, influxBucket.Name) + + // Try to connect without authentication + clientWithoutToken := influxdb2.NewClientWithOptions(url, "", influxdb2.DefaultOptions()) + defer clientWithoutToken.Close() + + _, err = clientWithoutToken.BucketsAPI().CreateBucketWithNameWithID(ctx, org, "example") + require.Error(t, err, "Expected error when trying to create a bucket without authentication") +} + func TestWithInitDb(t *testing.T) { ctx := context.Background() influxDBContainer, err := influxdb.Run(ctx, From 568c0d58ca46f742eec3873d1e3c67025d560eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 24 Apr 2025 13:40:56 +0200 Subject: [PATCH 2/2] chore: consistent import --- modules/influxdb/influxdb_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/influxdb/influxdb_test.go b/modules/influxdb/influxdb_test.go index bf11375e31..3e640f7e7d 100644 --- a/modules/influxdb/influxdb_test.go +++ b/modules/influxdb/influxdb_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - influxdb2 "github.com/influxdata/influxdb-client-go/v2" + influxclient2 "github.com/influxdata/influxdb-client-go/v2" influxclient "github.com/influxdata/influxdb1-client/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -438,7 +438,7 @@ func TestRun_V2WithOptions(t *testing.T) { require.NoError(t, err) // Initialize a new InfluxDB client - client := influxdb2.NewClientWithOptions(url, token, influxdb2.DefaultOptions()) + client := influxclient2.NewClientWithOptions(url, token, influxclient2.DefaultOptions()) defer client.Close() // Get the bucket @@ -447,7 +447,7 @@ func TestRun_V2WithOptions(t *testing.T) { require.Equal(t, bucket, influxBucket.Name) // Try to connect without authentication - clientWithoutToken := influxdb2.NewClientWithOptions(url, "", influxdb2.DefaultOptions()) + clientWithoutToken := influxclient2.NewClientWithOptions(url, "", influxclient2.DefaultOptions()) defer clientWithoutToken.Close() _, err = clientWithoutToken.BucketsAPI().CreateBucketWithNameWithID(ctx, org, "example")