Skip to content

Commit

Permalink
Merge branch 'main' into automate-tag-creation
Browse files Browse the repository at this point in the history
* main:
  chore: update Docker labels for containers (testcontainers#813)
  fix: nil pointer dereference in HealthStrategy (testcontainers#802)
  fix: Synchronise writes to containers map (testcontainers#812)
  chore(deps): bump google.golang.org/api from 0.108.0 to 0.109.0 in /examples (testcontainers#810)
  chore(deps): bump cloud.google.com/go/spanner in /examples/spanner (testcontainers#806)
  chore: restructure Docker helper methods (testcontainers#799)
  Verify Reaper state to create new or return existing instance (testcontainers#782)
  docs: add intel as user (testcontainers#798)
  chore: bump containerd in examples (testcontainers#797)
  chore(deps): bump github.com/containerd/containerd from 1.6.15 to 1.6.16 (testcontainers#793)
  chore: extract docker host calculation to an internal package (testcontainers#796)
  chore: run "go mod tidy" automatically when creating examples (testcontainers#794)
  chore: build images with backoff retries (testcontainers#792)
  fix: use right import package for compose in docs (testcontainers#791)
  chore(deps): bump google.golang.org/grpc from 1.52.1 to 1.52.3 in /examples (testcontainers#790)
  Add devcontainer file (testcontainers#765)
  chore: check dependabot dependencies weekly (testcontainers#789)
  chore(deps): bump google.golang.org/grpc from 1.52.0 to 1.52.1 in /examples (testcontainers#783)
  chore: support for titles in examples (testcontainers#775)
  • Loading branch information
mdelapenya committed Feb 10, 2023
2 parents a17984a + ba569c0 commit dbc4c7d
Show file tree
Hide file tree
Showing 61 changed files with 685 additions and 588 deletions.
33 changes: 33 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
"name": "Go",
"image": "mcr.microsoft.com/devcontainers/go:0-1.18-bullseye",

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
// Set *default* container specific settings.json values on container create.
"settings": {
"go.toolsManagement.checkForUpdates": "local",
"go.useLanguageServer": true,
"go.gopath": "/go"
},

// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"golang.go"
]
}
},
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"postStartCommand": ["go", "build"]
}
32 changes: 16 additions & 16 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,96 +3,96 @@ updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /modules/compose
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/bigtable
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/cockroachdb
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/consul
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/datastore
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/firestore
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/mongodb
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/mysql
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/nginx
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/postgres
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/pubsub
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/pulsar
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/redis
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/spanner
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /examples/toxiproxy
schedule:
interval: daily
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Testcontainers

[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=141451032&machine=standardLinux32gb&devcontainer_path=.devcontainer%2Fdevcontainer.json&location=EastUs)

[![Main pipeline](https://github.com/testcontainers/testcontainers-go/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/testcontainers/testcontainers-go/actions/workflows/ci.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/testcontainers/testcontainers-go)](https://goreportcard.com/report/github.com/testcontainers/testcontainers-go)
[![GoDoc Reference](https://camo.githubusercontent.com/8609cfcb531fa0f5598a3d4353596fae9336cce3/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f79616e6777656e6d61692f686f772d746f2d6164642d62616467652d696e2d6769746875622d726561646d653f7374617475732e737667)](https://pkg.go.dev/github.com/testcontainers/testcontainers-go)
Expand Down
78 changes: 34 additions & 44 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"io"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
Expand All @@ -34,6 +33,9 @@ import (
specs "github.com/opencontainers/image-spec/specs-go/v1"

tcexec "github.com/testcontainers/testcontainers-go/exec"
"github.com/testcontainers/testcontainers-go/internal"
"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
"github.com/testcontainers/testcontainers-go/internal/testcontainerssession"
"github.com/testcontainers/testcontainers-go/wait"
)

Expand Down Expand Up @@ -132,7 +134,7 @@ func (c *DockerContainer) PortEndpoint(ctx context.Context, port nat.Port, proto
// Warning: this is based on your Docker host setting. Will fail if using an SSH tunnel
// You can use the "TC_HOST" env variable to set this yourself
func (c *DockerContainer) Host(ctx context.Context) (string, error) {
host, err := c.provider.daemonHost(ctx)
host, err := c.provider.DaemonHost(ctx)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -781,7 +783,7 @@ func NewDockerClient() (cli *client.Client, host string, tcConfig TestContainers

opts = append(opts, client.WithHTTPHeaders(
map[string]string{
"x-tc-sid": sessionID().String(),
"x-tc-sid": testcontainerssession.String(),
}),
)

Expand Down Expand Up @@ -894,7 +896,18 @@ func (p *DockerProvider) BuildImage(ctx context.Context, img ImageBuildInfo) (st
ForceRemove: true,
}

resp, err := p.client.ImageBuild(ctx, buildContext, buildOptions)
var resp types.ImageBuildResponse
err = backoff.Retry(func() error {
resp, err = p.client.ImageBuild(ctx, buildContext, buildOptions)
if err != nil {
if _, ok := err.(errdefs.ErrNotFound); ok {
return backoff.Permanent(err)
}
Logger.Printf("Failed to build image: %s, will retry", err)
return err
}
return nil
}, backoff.WithContext(backoff.NewExponentialBackOff(), ctx))
if err != nil {
return "", err
}
Expand Down Expand Up @@ -959,8 +972,6 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
req.Labels = make(map[string]string)
}

sessionID := sessionID()

reaperOpts := containerOptions{
ImageName: req.ReaperImage,
}
Expand All @@ -972,7 +983,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
// the reaper does not need to start a reaper for itself
isReaperContainer := strings.EqualFold(req.Image, reaperImage(reaperOpts.ImageName))
if !req.SkipReaper && !isReaperContainer {
r, err := newReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...)
r, err := reuseOrCreateReaper(context.WithValue(ctx, testcontainersdocker.DockerHostContextKey, p.host), testcontainerssession.String(), p, req.ReaperOptions...)
if err != nil {
return nil, fmt.Errorf("%w: creating reaper failed", err)
}
Expand Down Expand Up @@ -1142,7 +1153,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
WaitingFor: req.WaitingFor,
Image: tag,
imageWasBuilt: req.ShouldBuildImage(),
sessionID: sessionID,
sessionID: testcontainerssession.ID(),
provider: p,
terminationSignal: termSignal,
skipReaper: req.SkipReaper,
Expand Down Expand Up @@ -1186,10 +1197,9 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain
return p.CreateContainer(ctx, req)
}

sessionID := sessionID()
var termSignal chan bool
if !req.SkipReaper {
r, err := newReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...)
r, err := reuseOrCreateReaper(context.WithValue(ctx, testcontainersdocker.DockerHostContextKey, p.host), testcontainerssession.String(), p, req.ReaperOptions...)
if err != nil {
return nil, fmt.Errorf("%w: creating reaper failed", err)
}
Expand All @@ -1204,7 +1214,7 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain
ID: c.ID,
WaitingFor: req.WaitingFor,
Image: c.Image,
sessionID: sessionID,
sessionID: testcontainerssession.ID(),
provider: p,
terminationSignal: termSignal,
skipReaper: req.SkipReaper,
Expand Down Expand Up @@ -1271,10 +1281,14 @@ func (p *DockerProvider) Config() TestContainersConfig {
return p.config
}

// daemonHost gets the host or ip of the Docker daemon where ports are exposed on
// DaemonHost gets the host or ip of the Docker daemon where ports are exposed on
// Warning: this is based on your Docker host setting. Will fail if using an SSH tunnel
// You can use the "TC_HOST" env variable to set this yourself
func (p *DockerProvider) daemonHost(ctx context.Context) (string, error) {
func (p *DockerProvider) DaemonHost(ctx context.Context) (string, error) {
return daemonHost(ctx, p)
}

func daemonHost(ctx context.Context, p *DockerProvider) (string, error) {
if p.hostCache != "" {
return p.hostCache, nil
}
Expand All @@ -1295,11 +1309,10 @@ func (p *DockerProvider) daemonHost(ctx context.Context) (string, error) {
case "http", "https", "tcp":
p.hostCache = url.Hostname()
case "unix", "npipe":
if inAContainer() {
if testcontainersdocker.InAContainer() {
ip, err := p.GetGatewayIP(ctx)
if err != nil {
// fallback to getDefaultGatewayIP
ip, err = getDefaultGatewayIP()
ip, err = testcontainersdocker.DefaultGatewayIP()
if err != nil {
ip = "localhost"
}
Expand All @@ -1309,7 +1322,7 @@ func (p *DockerProvider) daemonHost(ctx context.Context) (string, error) {
p.hostCache = "localhost"
}
default:
return "", errors.New("Could not determine host through env or docker host")
return "", errors.New("could not determine host through env or docker host")
}

return p.hostCache, nil
Expand Down Expand Up @@ -1343,8 +1356,7 @@ func (p *DockerProvider) CreateNetwork(ctx context.Context, req NetworkRequest)

var termSignal chan bool
if !req.SkipReaper {
sessionID := sessionID()
r, err := newReaper(context.WithValue(ctx, dockerHostContextKey, p.host), sessionID.String(), p, req.ReaperOptions...)
r, err := reuseOrCreateReaper(context.WithValue(ctx, testcontainersdocker.DockerHostContextKey, p.host), testcontainerssession.String(), p, req.ReaperOptions...)
if err != nil {
return nil, fmt.Errorf("%w: creating network reaper failed", err)
}
Expand Down Expand Up @@ -1426,30 +1438,6 @@ func (p *DockerProvider) printReaperBanner(resource string) {
p.Logger.Printf(ryukDisabledMessage)
}

func inAContainer() bool {
// see https://github.com/testcontainers/testcontainers-java/blob/3ad8d80e2484864e554744a4800a81f6b7982168/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java#L15
if _, err := os.Stat("/.dockerenv"); err == nil {
return true
}
return false
}

// deprecated
// see https://github.com/testcontainers/testcontainers-java/blob/main/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java#L46
func getDefaultGatewayIP() (string, error) {
// see https://github.com/testcontainers/testcontainers-java/blob/3ad8d80e2484864e554744a4800a81f6b7982168/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java#L27
cmd := exec.Command("sh", "-c", "ip route|awk '/default/ { print $3 }'")
stdout, err := cmd.Output()
if err != nil {
return "", errors.New("Failed to detect docker host")
}
ip := strings.TrimSpace(string(stdout))
if len(ip) == 0 {
return "", errors.New("Failed to parse default gateway IP")
}
return ip, nil
}

func (p *DockerProvider) getDefaultNetwork(ctx context.Context, cli client.APIClient) (string, error) {
// Get list of available networks
networkResources, err := cli.NetworkList(ctx, types.NetworkListOptions{})
Expand Down Expand Up @@ -1477,7 +1465,9 @@ func (p *DockerProvider) getDefaultNetwork(ctx context.Context, cli client.APICl
Driver: Bridge,
Attachable: true,
Labels: map[string]string{
TestcontainerLabel: "true",
TestcontainerLabel: "true",
testcontainersdocker.LabelLang: "go",
testcontainersdocker.LabelVersion: internal.Version,
},
})

Expand Down
5 changes: 3 additions & 2 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"

"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
"github.com/testcontainers/testcontainers-go/wait"
)

Expand Down Expand Up @@ -378,7 +379,7 @@ func TestContainerStartsWithoutTheReaper(t *testing.T) {
terminateContainerOnEnd(t, ctx, container)

resp, err := client.ContainerList(ctx, types.ContainerListOptions{
Filters: filters.NewArgs(filters.Arg("label", fmt.Sprintf("%s=%s", TestcontainerLabelSessionID, container.SessionID()))),
Filters: filters.NewArgs(filters.Arg("label", fmt.Sprintf("%s=%s", testcontainersdocker.LabelSessionID, container.SessionID()))),
})
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -408,7 +409,7 @@ func TestContainerStartsWithTheReaper(t *testing.T) {
if err != nil {
t.Fatal(err)
}
filtersJSON := fmt.Sprintf(`{"label":{"%s":true}}`, TestcontainerLabelIsReaper)
filtersJSON := fmt.Sprintf(`{"label":{"%s":true}}`, testcontainersdocker.LabelReaper)
f, err := filters.FromJSON(filtersJSON)
if err != nil {
t.Fatal(err)
Expand Down
5 changes: 3 additions & 2 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,19 @@ We have provided a command line tool to generate the scaffolding for the code of
|------|------|----------|-------------|
| -name | string | Yes | Name of the example, use camel-case when needed. Only alphabetical characters are allowed. |
| -image | string | Yes | Fully-qualified name of the Docker image to be used by the example (i.e. 'docker.io/org/project:tag') |
| -title | string | No | A variant of the name supporting mixed casing (i.e. 'MongoDB'). Only alphabetical characters are allowed. |

### What is this tool not doing?

- If the example name does not contain alphabeticall characters, it will exit the generation.
- If the example name does not contain alphabetical characters, it will exit the generation.
- If the example already exists, it will exit without updating the existing files.

### How to run the tool

From the [`examples` directory]({{repo_url}}/tree/main/examples), please run:

```shell
go run . --name ${NAME_OF_YOUR_EXAMPLE} --image "${REGISTRY}/${EXAMPLE}:${TAG}"
go run . --name ${NAME_OF_YOUR_EXAMPLE} --image "${REGISTRY}/${EXAMPLE}:${TAG}" --title ${TITLE_OF_YOUR_EXAMPLE}
```

## Update Go dependencies in the examples
Expand Down
Loading

0 comments on commit dbc4c7d

Please sign in to comment.