Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/go_modules/modules/clickhouse/git…
Browse files Browse the repository at this point in the history
…hub.com/ClickHouse/clickhouse-go/v2-2.18.0
  • Loading branch information
mdelapenya committed Feb 23, 2024
2 parents ba1e7b5 + 8085e2f commit 02d1aee
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 6 deletions.
53 changes: 51 additions & 2 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"net/url"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -49,6 +50,8 @@ const (
logStoppedForOutOfSyncMessage = "Stopping log consumer: Headers out of sync"
)

var createContainerFailDueToNameConflictRegex = regexp.MustCompile("Conflict. The container name .* is already in use by container .*")

// DockerContainer represents a container started using Docker
type DockerContainer struct {
// Container ID from Docker
Expand Down Expand Up @@ -1153,13 +1156,40 @@ func (p *DockerProvider) findContainerByName(ctx context.Context, name string) (
return nil, nil
}

func (p *DockerProvider) waitContainerCreation(ctx context.Context, name string) (*types.Container, error) {
var container *types.Container
return container, backoff.Retry(func() error {
c, err := p.findContainerByName(ctx, name)
if err != nil {
return err
}

if c == nil {
return fmt.Errorf("container %s not found", name)
}

container = c
return nil
}, backoff.WithContext(backoff.NewExponentialBackOff(), ctx))
}

func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req ContainerRequest) (Container, error) {
c, err := p.findContainerByName(ctx, req.Name)
if err != nil {
return nil, err
}
if c == nil {
return p.CreateContainer(ctx, req)
createdContainer, err := p.CreateContainer(ctx, req)
if err == nil {
return createdContainer, nil
}
if !createContainerFailDueToNameConflictRegex.MatchString(err.Error()) {
return nil, err
}
c, err = p.waitContainerCreation(ctx, req.Name)
if err != nil {
return nil, err
}
}

sessionID := core.SessionID()
Expand All @@ -1178,6 +1208,13 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain
}
}

// default hooks include logger hook and pre-create hook
defaultHooks := []ContainerLifecycleHooks{
DefaultLoggingHook(p.Logger),
defaultReadinessHook(),
defaultLogConsumersHook(req.LogConsumerCfg),
}

dc := &DockerContainer{
ID: c.ID,
WaitingFor: req.WaitingFor,
Expand All @@ -1187,7 +1224,19 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain
terminationSignal: termSignal,
stopLogProductionCh: nil,
logger: p.Logger,
isRunning: c.State == "running",
lifecycleHooks: []ContainerLifecycleHooks{combineContainerHooks(defaultHooks, req.LifecycleHooks)},
}

err = dc.startedHook(ctx)
if err != nil {
return nil, err
}

dc.isRunning = true

err = dc.readiedHook(ctx)
if err != nil {
return nil, err
}

return dc, nil
Expand Down
72 changes: 72 additions & 0 deletions generic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ package testcontainers
import (
"context"
"errors"
"net/http"
"os"
"os/exec"
"strings"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -117,3 +122,70 @@ func TestGenericContainerShouldReturnRefOnError(t *testing.T) {
require.NotNil(t, c)
terminateContainerOnEnd(t, context.Background(), c)
}

func TestGenericReusableContainerInSubprocess(t *testing.T) {
wg := sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()

// create containers in subprocesses, as "go test ./..." does.
output := createReuseContainerInSubprocess(t)

// check is reuse container with WaitingFor work correctly.
require.True(t, strings.Contains(output, "🚧 Waiting for container id"))
require.True(t, strings.Contains(output, "🔔 Container is ready"))
}()
}

wg.Wait()
}

func createReuseContainerInSubprocess(t *testing.T) string {
cmd := exec.Command(os.Args[0], "-test.run=TestHelperContainerStarterProcess")
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}

output, err := cmd.CombinedOutput()
require.NoError(t, err, string(output))

return string(output)
}

// TestHelperContainerStarterProcess is a helper function
// to start a container in a subprocess. It's not a real test.
func TestHelperContainerStarterProcess(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
t.Skip("Skipping helper test function. It's not a real test")
}

ctx := context.Background()

nginxC, err := GenericContainer(ctx, GenericContainerRequest{
ProviderType: providerType,
ContainerRequest: ContainerRequest{
Image: nginxDelayedImage,
ExposedPorts: []string{nginxDefaultPort},
WaitingFor: wait.ForListeningPort(nginxDefaultPort), // default startupTimeout is 60s
Name: reusableContainerName,
},
Started: true,
Reuse: true,
})
require.NoError(t, err)
require.True(t, nginxC.IsRunning())

origin, err := nginxC.PortEndpoint(ctx, nginxDefaultPort, "http")
require.NoError(t, err)

// check is reuse container with WaitingFor work correctly.
req, err := http.NewRequestWithContext(ctx, http.MethodGet, origin, nil)
require.NoError(t, err)
req.Close = true

resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

require.Equal(t, http.StatusOK, resp.StatusCode)
}
3 changes: 2 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ var (
tcConfigOnce *sync.Once = new(sync.Once)
)

// Config represents the configuration for Testcontainers
// testcontainersConfig {

// Config represents the configuration for Testcontainers
type Config struct {
Host string `properties:"docker.host,default="`
TLSVerify int `properties:"docker.tls.verify,default=0"`
Expand Down
2 changes: 2 additions & 0 deletions logconsumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const StdoutLog = "STDOUT"
const StderrLog = "STDERR"

// logStruct {

// Log represents a message that was created by a process,
// LogType is either "STDOUT" or "STDERR",
// Content is the byte contents of the message itself
Expand All @@ -18,6 +19,7 @@ type Log struct {
// }

// logConsumerInterface {

// LogConsumer represents any object that can
// handle a Log, it is up to the LogConsumer instance
// what to do with the log
Expand Down
2 changes: 2 additions & 0 deletions modules/couchbase/couchbase.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

const (
// containerPorts {

MGMT_PORT = "8091"
MGMT_SSL_PORT = "18091"

Expand All @@ -40,6 +41,7 @@ const (

KV_PORT = "11210"
KV_SSL_PORT = "11207"

// }
)

Expand Down
1 change: 1 addition & 0 deletions modules/couchbase/storage_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package couchbase
type indexStorageMode string

// storageTypes {

const (
// MemoryOptimized sets the cluster-wide index storage mode to use memory optimized global
// secondary indexes which can perform index maintenance and index scan faster at in-memory speeds.
Expand Down
2 changes: 2 additions & 0 deletions modules/neo4j/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ type LabsPlugin string

const (
// labsPlugins {

Apoc LabsPlugin = "apoc"
ApocCore LabsPlugin = "apoc-core"
Bloom LabsPlugin = "bloom"
GraphDataScience LabsPlugin = "graph-data-science"
NeoSemantics LabsPlugin = "n10s"
Streams LabsPlugin = "streams"

// }
)

Expand Down
1 change: 1 addition & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func WithImage(image string) CustomizeRequestOption {
}

// imageSubstitutor {

// ImageSubstitutor represents a way to substitute container image names
type ImageSubstitutor interface {
// Description returns the name of the type and a short description of how it modifies the image.
Expand Down
3 changes: 0 additions & 3 deletions reaper.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"
"math/rand"
"net"
"regexp"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -186,8 +185,6 @@ func reuseOrCreateReaper(ctx context.Context, sessionID string, provider ReaperP
return reaperInstance, nil
}

var createContainerFailDueToNameConflictRegex = regexp.MustCompile("Conflict. The container name .* is already in use by container .*")

// reuseReaperContainer constructs a Reaper from an already running reaper
// DockerContainer.
func reuseReaperContainer(ctx context.Context, sessionID string, provider ReaperProvider, reaperContainer *DockerContainer) (*Reaper, error) {
Expand Down
1 change: 1 addition & 0 deletions testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func SkipIfDockerDesktop(t *testing.T, ctx context.Context) {
}

// exampleLogConsumer {

// StdoutLogConsumer is a LogConsumer that prints the log to stdout
type StdoutLogConsumer struct{}

Expand Down

0 comments on commit 02d1aee

Please sign in to comment.