// Copyright IBM Corp. 2020, 2026 // SPDX-License-Identifier: BUSL-1.1 package infra import ( "fmt" "io" "net/url" "os/exec" "strings" "testing" "github.com/ory/dockertest/v3" "github.com/ory/dockertest/v3/docker" "github.com/stretchr/testify/require" ) type RedisContainerInfo struct { Hostname string Port string Username string Password string } type redisConfig struct { User string Password string NetworkAlias string } // SetupRedisContainer starts a Redis container and returns its connection info func SetupRedisContainer(t *testing.T) *RedisContainerInfo { pool, err := dockertest.NewPool("") require.NoError(t, err) network, err := pool.NetworksByName("e2e_cluster") require.NoError(t, err, "Failed to get e2e_cluster network") c := startRedis(t, pool, &network[0], "redis", "latest") require.NotNil(t, c, "Redis container should not be nil") t.Cleanup(func() { if err := pool.Purge(c.Resource); err != nil { t.Logf("Failed to purge Redis container: %v", err) } }) u, err := url.Parse(c.UriNetwork) t.Log(u) require.NoError(t, err, "Failed to parse Redis URL") user, hostname, port := u.User.Username(), u.Hostname(), u.Port() pw, pwSet := u.User.Password() t.Logf("Redis info: user=%s, host=%s, port=%s, password-set:%t", user, hostname, port, pwSet) return &RedisContainerInfo{ Hostname: hostname, Port: port, Username: user, Password: pw, } } // startRedis starts a Redis database in a docker container. // Returns information about the container func startRedis(t testing.TB, pool *dockertest.Pool, network *dockertest.Network, repository, tag string) *Container { t.Log("Starting Redis database...") c, err := LoadConfig() require.NoError(t, err) err = pool.Client.PullImage(docker.PullImageOptions{ Repository: fmt.Sprintf("%s/%s", c.DockerMirror, repository), Tag: tag, }, docker.AuthConfiguration{}) require.NoError(t, err) config := redisConfig{ User: "e2eboundary", Password: "e2eboundary", NetworkAlias: "e2eredis", } resource, err := pool.RunWithOptions(&dockertest.RunOptions{ Repository: fmt.Sprintf("%s/%s", c.DockerMirror, repository), Tag: tag, ExposedPorts: []string{"6379/tcp"}, Name: config.NetworkAlias, Networks: []*dockertest.Network{network}, }) require.NoError(t, err) err = pool.Retry(func() error { cmd := exec.Command("docker", "exec", config.NetworkAlias, "redis-cli", "PING") output, cmdErr := cmd.CombinedOutput() if cmdErr != nil { return fmt.Errorf("failed to connect to Redis container '%s': %v\nOutput: %s", config.NetworkAlias, cmdErr, string(output)) } return nil }) require.NoError(t, err, "Redis container did not start in time or is not healthy") // configure redis users // e2e user err = exec.Command( "docker", "exec", config.NetworkAlias, "redis-cli", "ACL", "SETUSER", config.User, "on", fmt.Sprintf(">%s", config.Password), "+@all", "allkeys", ).Run() require.NoError(t, err) // default user err = exec.Command( "docker", "exec", config.NetworkAlias, "redis-cli", "ACL", "SETUSER", "default", fmt.Sprintf(">%s", config.Password), ).Run() require.NoError(t, err) return &Container{ Resource: resource, UriLocalhost: fmt.Sprintf( "redis://%s:%s@localhost:6379", config.User, config.Password, ), UriNetwork: fmt.Sprintf( "redis://%s:%s@%s:6379", config.User, config.Password, config.NetworkAlias, ), } } func SendRedisCommand(stdin io.WriteCloser, stdout io.ReadCloser, cmdStr string) (string, error) { _, err := stdin.Write([]byte(cmdStr)) if err != nil { return "", err } buf := make([]byte, 1024) n, err := stdout.Read(buf) if err != nil { return "", err } return strings.TrimSpace(string(buf[:n])), nil }