Giter Club home page Giter Club logo

testcontainers / testcontainers-go Goto Github PK

View Code? Open in Web Editor NEW
3.3K 18.0 454.0 17.21 MB

Testcontainers for Go is a Go package that makes it simple to create and clean up container-based dependencies for automated integration/smoke tests. The clean, easy-to-use API enables developers to programmatically define containers that should be run as part of a test and clean up those resources when the test is done.

Home Page: https://golang.testcontainers.org

License: MIT License

Go 97.86% Shell 1.38% Makefile 0.42% Smarty 0.31% JavaScript 0.03%
testing docker automation golang go testcontainers-go testcontainers hacktoberfest

testcontainers-go's Introduction

Testcontainers

Open in GitHub Codespaces

Builds

Main pipeline

Documentation

GoDoc Reference

Social

Slack

Code quality

Go Report Card Quality Gate Status

License

License

Testcontainers for Go is a Go package that makes it simple to create and clean up container-based dependencies for automated integration/smoke tests. The clean, easy-to-use API enables developers to programmatically define containers that should be run as part of a test and clean up those resources when the test is done.

You can find more information about Testcontainers for Go at golang.testcontainers.org, which is rendered from the ./docs directory.

Using Testcontainers for Go

Please visit the quickstart guide to understand how to add the dependency to your Go project.

testcontainers-go's People

Contributors

01101101m avatar anilsenay avatar bablzz avatar bearrito avatar claytonnorthey92 avatar dependabot-preview[bot] avatar dependabot[bot] avatar eddumelendez avatar fiftin avatar franklinlindemberg avatar funvit avatar gaborszakacs avatar gflarity avatar gianarb avatar hey-xico avatar ikolomiyets avatar islishude avatar jaredpetersen avatar jespino avatar mdelapenya avatar menedev avatar mmorel-35 avatar mniak avatar mraerino avatar oriser avatar pablochacin avatar prskr avatar purpleclay avatar shashank-g172 avatar stevenh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

testcontainers-go's Issues

Allow manual start of container

The java implementation actually requires users of the library to start the container on their own or use a test framework integration.

The go lib should probably also provide a way to start the container on request rather than on creation of the testcontainer setup.

Allow Log Following/Consuming

Describe the bug
Not a bug, a feature request.
When running containers via testcontainers-go, in order to view the stdout from a container you have to call .Logs(...) on that container. I think it would be useful allow an option to forward the stdout output from the containers to some sort of log consumer. For example, you could send this to one that would log stdout of the container to stdout for the host machine. This would help with debugging.

The Java implementation uses follow logs

To Reproduce
Not bug bug, no reproduction steps.

Expected behavior
Allow an option to be passed into the ContainerRequest to forward stdout to a follower, maybe...

type LogConsumer interface {
    Accept(string logMessage) error
}
type ContainerRequest struct {
	...
        LogFollowers []LogConsumer
}
// an example consumer
type StdoutLogConsumer struct {}

func (s *StdoutLogConsumer) Accept(string logMessage) error {
    fmt.Println(logMessage)
}

Additional context
This was inspired by this comment.

Rework wait.HostPortStrategy

The current version of HostPortStrategy doesn't work anymore. I'm not sure if I made a mistake when I initially implemented it or if the behavior of docker has changed.

Currently we're only looking for an opened exposed port, however docker opens the port right away even if the port is not opend inside the container. The java version runs various executions inside the container (like docker exec) to check if the port is opend in the container as well.

I would suggest to remove the current implementation completely, as it is misleading, and create a working version instead.

Re-work exposed ip and mapped port

@rnorth I think I need to understand what's the easier approach to re-implement the "most-compatible" GetContainerAddress and GetMappedPort function. Right now I think I did it in a wrong way.

The get address returns the ip of the container and we have a function that returns the binding port in the host for a port. But as you can see this doesn't work.

Do you have some feedback about the easier fix that I can do to have something that can work?

WaitForHostPort for depends on `bash` being installed in the target service

Bug description

I noticed that the wait for host/port strategy depends on having bash installed in the target service:

exitCode, err := target.Exec(ctx, []string{"/bin/bash", "-c", command})

which could not be true in many scenarios (i.e. a very basic Alpine-based image)

Steps To Reproduce

Create a ContainerRequest from an Image without bash installed on it. Add a wait.ForListeningPort strategy. It will create an infinite loop as the exit code is not controlled, delegating the exit of the test method to the Docker client, which is not able to close the initial command.

panic: test timed out after 30s

goroutine 616 [running]:
testing.(*M).startAlarm.func1()
	/usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:1377 +0xdf
created by time.goFunc
	/usr/local/Cellar/go/1.13.4/libexec/src/time/sleep.go:168 +0x44

goroutine 1 [chan receive]:
testing.(*T).Run(0xc0000ef300, 0x164630d, 0x30, 0x16538e0, 0x1087f01)
	/usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:961 +0x377
testing.runTests.func1(0xc0000ef200)
	/usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:1202 +0x78
testing.tRunner(0xc0000ef200, 0xc0001f3dc0)
	/usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:909 +0xc9
testing.runTests(0xc000236fa0, 0x1ab15a0, 0x19, 0x19, 0x0)
	/usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:1200 +0x2a7
testing.(*M).Run(0xc0000edd00, 0x0)
	/usr/local/Cellar/go/1.13.4/libexec/src/testing/testing.go:1117 +0x176
main.main()
	_testmain.go:92 +0x135

goroutine 36 [IO wait]:
internal/poll.runtime_pollWait(0x1e51600, 0x72, 0xffffffffffffffff)
	/usr/local/Cellar/go/1.13.4/libexec/src/runtime/netpoll.go:184 +0x55
internal/poll.(*pollDesc).wait(0xc000426518, 0x72, 0x1000, 0x1000, 0xffffffffffffffff)
	/usr/local/Cellar/go/1.13.4/libexec/src/internal/poll/fd_poll_runtime.go:87 +0x45
internal/poll.(*pollDesc).waitRead(...)
	/usr/local/Cellar/go/1.13.4/libexec/src/internal/poll/fd_poll_runtime.go:92
internal/poll.(*FD).Read(0xc000426500, 0xc000440000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.13.4/libexec/src/internal/poll/fd_unix.go:169 +0x22b
net.(*netFD).Read(0xc000426500, 0xc000440000, 0x1000, 0x1000, 0x16ea940, 0xc00025fb80, 0x0)
	/usr/local/Cellar/go/1.13.4/libexec/src/net/fd_unix.go:202 +0x4f
net.(*conn).Read(0xc000612690, 0xc000440000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.13.4/libexec/src/net/net.go:184 +0x68
net/http.(*persistConn).Read(0xc0003f7c20, 0xc000440000, 0x1000, 0x1000, 0x0, 0xc0001752e8, 0x100e216)
	/usr/local/Cellar/go/1.13.4/libexec/src/net/http/transport.go:1752 +0x75
bufio.(*Reader).Read(0xc000490c60, 0xc0005be000, 0x200, 0x2000, 0x8, 0x1585b20, 0x6db7ac1ba336768e)
	/usr/local/Cellar/go/1.13.4/libexec/src/bufio/bufio.go:226 +0x26a
net/http.(*body).readLocked(0xc000388700, 0xc0005be000, 0x200, 0x2000, 0xc, 0xc000413ec8, 0xc0001753a8)
	/usr/local/Cellar/go/1.13.4/libexec/src/net/http/transfer.go:847 +0x5f
net/http.(*body).Read(0xc000388700, 0xc0005be000, 0x200, 0x2000, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.13.4/libexec/src/net/http/transfer.go:839 +0x102
net/http.(*bodyEOFSignal).Read(0xc000388740, 0xc0005be000, 0x200, 0x2000, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.13.4/libexec/src/net/http/transport.go:2576 +0xe5
io.(*LimitedReader).Read(0xc00044b520, 0xc0005be000, 0x2000, 0x2000, 0xc000434090, 0x100b07b, 0xc000012000)
	/usr/local/Cellar/go/1.13.4/libexec/src/io/io.go:448 +0x63
io/ioutil.devNull.ReadFrom(0x0, 0x16ea760, 0xc00044b520, 0x1593760, 0x1, 0x1e51770)
	/usr/local/Cellar/go/1.13.4/libexec/src/io/ioutil/ioutil.go:147 +0x92
io.copyBuffer(0x16eb5a0, 0x1ad3b00, 0x16ea760, 0xc00044b520, 0x0, 0x0, 0x0, 0x1596f80, 0x16f0900, 0x16ea980)
	/usr/local/Cellar/go/1.13.4/libexec/src/io/io.go:388 +0x2ed
io.Copy(...)
	/usr/local/Cellar/go/1.13.4/libexec/src/io/io.go:364
io.CopyN(0x16eb5a0, 0x1ad3b00, 0x16ea980, 0xc000388740, 0x200, 0xc0002df0b0, 0xc8, 0xc000406d80)
	/usr/local/Cellar/go/1.13.4/libexec/src/io/io.go:340 +0x9a
github.com/docker/docker/client.ensureReaderClosed(0x16f0980, 0xc000388740, 0xc0002df0b0, 0xc8, 0xc000406d80)
	/Users/mdelapenya/sourcecode/pkg/mod/github.com/docker/[email protected]/client/request.go:270 +0x90
github.com/docker/docker/client.(*Client).ContainerExecStart(0xc0000ede00, 0x16f6500, 0xc0003d8000, 0xc0001af840, 0x40, 0x0, 0x0, 0x0)

Expected behavior

The error level 126 (command not executable) should be managed.

docker info

Client:
 Debug Mode: false

Server:
 Containers: 2
  Running: 1
  Paused: 0
  Stopped: 1
 Images: 266
 Server Version: 19.03.5
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: true
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: b34a5c8af56e510852c35414db4c1f4fa6172339
 runc version: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
 init version: fec3683
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 4.9.184-linuxkit
 Operating System: Docker Desktop
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 7.787GiB
 Name: docker-desktop
 ID: 666N:ZRUC:5URC:56ID:255A:GL7H:ZM2Y:M25P:EW3W:YE52:URT7:5EGH
 Docker Root Dir: /var/lib/docker
 Debug Mode: true
  File Descriptors: 39
  Goroutines: 54
  System Time: 2020-01-07T10:55:22.0569905Z
  EventsListeners: 2
 HTTP Proxy: gateway.docker.internal:3128
 HTTPS Proxy: gateway.docker.internal:3129
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false
 Product License: Community Engine

Additional context

Many tests are using a pre-baked imaged menedev/delayed-nginx:1.15.2 which contains bash. I'd go with official images for this kind of tests.

I wonder if this approach, using bash is valid for Windows containers... ๐Ÿค”

Add ability to tearDown/cleanUp resources created by the container

In my project, the container I start up with TestContainers creates data on an external storage, and I'm providing a cleanUp function, which is run after the container is terminated, to remove that test data.

Do you think it is something valuable to be added at library level?

Thanks

error handling nginx sample

The example was copied and a null pointer is returned.

--- FAIL: TestNginxLatestReturn (5.82s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x10 pc=0x7ebcdb]

goroutine 19 [running]:
testing.tRunner.func1(0xc0000ef200)
        C:/Go/src/testing/testing.go:830 +0x399
panic(0x861de0, 0xc6f560)
        C:/Go/src/runtime/panic.go:522 +0x1c3
docker-image-bouncer.TestNginxLatestReturn(0xc0000ef200)
        C:/Users/320016328/dev/docker-image-bouncer/main_test.go:35 +0x50b
testing.tRunner(0xc0000ef200, 0x8f76b8)
        C:/Go/src/testing/testing.go:865 +0xc7
created by testing.(*T).Run
        C:/Go/src/testing/testing.go:916 +0x361
FAIL    docker-image-bouncer    6.386s

Failures on go get github.com/testcontainers/testcontainers-go

When using go1.11.4 and issuing go get github.com/testcontainers/testcontainers-go I get the following:

$ go get -v github.com/testcontainers/testcontainers-go
github.com/testcontainers/testcontainers-go
# github.com/testcontainers/testcontainers-go
/Users/USER/.gvm/pkgsets/go1.11.4/global/src/github.com/testcontainers/testcontainers-go/docker.go:116:32: cannot use inspect.NetworkSettings.NetworkSettingsBase.Ports (type "github.com/docker/docker/vendor/github.com/docker/go-connections/nat".PortMap) as type "github.com/docker/go-connections/nat".PortMap in return argument
/Users/USER/.gvm/pkgsets/go1.11.4/global/src/github.com/testcontainers/testcontainers-go/docker.go:197:25: multiple-value uuid.NewV4() in single-value context
/Users/USER/.gvm/pkgsets/go1.11.4/global/src/github.com/testcontainers/testcontainers-go/docker.go:219:3: cannot use exposedPortSet (type map["github.com/docker/go-connections/nat".Port]struct {}) as type "github.com/docker/docker/vendor/github.com/docker/go-connections/nat".PortSet in field value
/Users/USER/.gvm/pkgsets/go1.11.4/global/src/github.com/testcontainers/testcontainers-go/docker.go:261:3: cannot use exposedPortMap (type map["github.com/docker/go-connections/nat".Port][]"github.com/docker/go-connections/nat".PortBinding) as type "github.com/docker/docker/vendor/github.com/docker/go-connections/nat".PortMap in field value

Go fmt is not failing Travis build

Running go fmt ./... with current master (https://github.com/testcontainers/testcontainers-go/tree/0c6fcb48ff1cfa4d82a45b587e76b3588a4dc192) discovers a formatting problem in the docker_test.go file.

$  testcontainers-go git:(master) โœ— go fmt ./...   
docker_test.go
$  testcontainers-go git:(master) โœ— git diff docker_test.go 
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 modified: docker_test.go
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@ docker_test.go:928 @ func TestContainerCreationWithBindAndVolume(t *testing.T) {
            BindMounts:   map[string]string{absPath: "/hello.sh"},
            VolumeMounts: map[string]string{volumeName: "/data"},
            Cmd:          []string{"bash", "/hello.sh"},
-            WaitingFor: wait.ForLog("done"),
+            WaitingFor:   wait.ForLog("done"),
        },
        Started: true,
    })

I wonder if the Travis build should be failed in that case ๐Ÿค” I think so, as Go is very opinionated when talking about source code format.

To check that Travis is showing same information: https://travis-ci.org/testcontainers/testcontainers-go/builds/629696018#L370-L371

Thoughts?

Feature: Allow Ability to View Logs from Failed-to-Start Container

Please Note
I am not 100% sure that this is possible, but it would be useful for debugging.
based on this thread, this is possible https://testcontainers.slack.com/archives/CBWBNR76G/p1574767579003000

Describe the bug
When starting a container, you have the option to "wait" for something to occur to determine the container has "started". For example you can wait.ForLog. A problem is if the container doesn't "start" because what you were waiting for didn't occur, you may want to print the logs to see why it didn't occur (maybe there was an error, maybe you spelled something wrong in your wait.ForLog ๐Ÿ˜ฌ ). The currently functionality is: if the container fails to start, the io.ReadCloser that the .Logs function returns is nil.

I would like to propose , if possible, that we allow the reading of logs from a container that failed to start due to a context timeout. The Java port just added functionality for checking if the reason for failure was due to context timeout

I vote we:

  • check the failure to start reason
    • if it is due to a context timeout, we include the logs from that container

To Reproduce

ctx := context.Background()

req := ContainerRequest{
	Image: "alpine",

	// my container request will log out something unexpected, but I
	// may still want to know what it logged
	Cmd:        []string{"echo", "this is unexpected!"},
	WaitingFor: wait.ForLog("I expect this!").WithStartupTimeout(10 * time.Second),
}
c, err := GenericContainer(ctx, GenericContainerRequest{
	ContainerRequest: req,
	Started:          true,
})

// if the container failed to start because of a timeout waiting for the expected logs,
// there may still be value in whatever was in the logs of the failed container
if err != nil && err.Error() == "failed to start container: context deadline exceeded" {

	l, logErr := c.Logs(ctx)
	if logErr != nil {
		t.Error(err)
	}

	logBuf := new([]byte)
	// this will error due to the l being nil due to the container failing to start
	_, logErr = l.Read(*logBuf)
	if logErr != nil {
		t.Error(err)
	}

	t.Errorf("error starting container because the deadline exceeded, but here are the useful logs: %s", logBuf)
}

if err != nil {
	t.Error(err)
}

Expected behavior
I would expect the error "error starting container because the deadline exceeded, but here are the useful logs: this is unexpected!"

Additional Context
https://testcontainers.slack.com/archives/CBWBNR76G/p1574767579003000

Set up a GitHub action to create a changelog after a release

Hello
My plan was to use goreleaser to add a changelog automatically after every new tag.
The problem is that it doesn't work without anything to build (as a library we just need to changelog and we do not produce any binary).

https://stackoverflow.com/questions/57756096/goreleaser-to-only-generate-the-changelog-on-github/57760234#57760234

So, this is the command I would like to get running after every new tag and the output should populate the release page on github:

git log $(git describe --abbrev=0)...$(git describe --abbrev=0 $(git describe --abbrev=0 --tags)^) --pretty=format:'* [%s]("http://github.com/testcontainers/testcontainers-go/commit/%H")' --reverse

Any help will be super appreciated and it will make the release process easier (so more releases)

Thanks

Test library via TravisCI

I've seen that there is a PR (#12) by @gianarb on integrating a CircleCI config.

I'd like to propose integrating TravisCI since they are able to run docker inside the VM the test runs in.
Also, most open-source projects currently use Travis.

What CI would you rather like to support?

Migrate to Go modules

Since Go 1.11 we have an official way of supplying dependencies and their versions to the go tooling: Via go.mod and go.sum.

It should be easy to migrate from Gopkg to Go mod.

Do you want to go forward with that? I could probably submit a PR.

Proposal for the public API

This has been implemented in PR #36

In the past days I have been thinking about the API of this library. It is important becuase it determines the developer/user experience of this library. It is the first thing people see when using the library.

Basics

I propose to follow these principles

  • Follow Golang conventions for structure and naming
  • Hide implementation detail
  • Follow learnings of the java implementation, but not necessarily naming schemes
  • Provide high-level abstractions, but allow access to lower-level concepts

Naming

Go doesn't provide automatic support for getters and setters. There's nothing wrong with providing getters and setters yourself, and it's often appropriate to do so, but it's neither idiomatic nor necessary to put Get into the getter's name. If you have a field called owner (lower case, unexported), the getter method should be called Owner (upper case, exported), not GetOwner.
(https://golang.org/doc/effective_go.html#Getters)

Context

Programs that use Contexts should follow these rules to keep interfaces consistent across packages and enable static analysis tools to check context propagation:
Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx
(https://golang.org/pkg/context/)

Types

For the sake of hiding implementation detail, typing might not be the same as in the Docker or Kubernetes clients.
Ports might just be uint16 since that is the appropriately sized number type.

This might mean that a lot of the docker config needs to be reimplemented by this library.
For now it might be best to expose certain important features and allow access to others through a RawConfig property.

Container interface

type Container interface {
	Endpoint(context.Context) (string, error)             // get ip:port string for the first exposed port
	PortEndpoint(context.Context, uint16) (string, error) // get ip:port string for the given exposed port
	IPAddress(context.Context) (string, error)            // get IP address where the container port is exposed
	MappedPort(context.Context, uint16) (uint16, error)   // get externally mapped port for a container port
	Ports(context.Context) (map[uint16]uint16, error)     // get all exposed ports
	Start(context.Context) error                          // start the container
	StartWithParams(context.Context, interface{}) error   // start the container with special parameters
	Terminate(context.Context) error                      // terminate the container
}

ContainerProvider interface

type ContainerRequest struct {
	Image        string
	Env          map[string]string
	ExposedPort  []uint16
	Cmd          string
	Labels       map[string]string
	BindMounts   map[string]string
	RawConfig    interface{}
	RegistryCred string
	WaitingFor   wait.WaitStrategy
}

// ContainerProvider allows the creation of containers on an arbitrary system
type ContainerProvider interface {
	CreateContainer(context.Context, ContainerRequest) (Container, error)
}

Constructor functions

type ProviderType int

const (
	ProviderDocker ProviderType = iota // Docker is default = 0
)

type GenericContainerRequest struct {
	ContainerRequest              // embedded request for provider
	Started          bool         // whether to auto-start the container
	ProviderType     ProviderType // which provider to use, Docker if empty
}

func GenericContainer(ctx context.Context, req GenericContainerRequest) (Container, error) {
	// create provider and container
}

โ˜๏ธ the name for this function is taken from the java implementation and allows for more types such as database and message queue containers

Please expose ContainerID via Container interface

Hello,

I have a use case in which I want to use the container-ID in order to interact with the created container from another process.
Therefore, would it be possible to also expose the container-ID via the Container interface, as returned by e.g. the testcontainers.GenericContainer() function?

Thank you very much.

Spec 2.0 domain language with java implementation folks

The folks doing the java implementation are currently working on a 2.0 that will include several refactors and breaking API changes.

Since they are in a process of planning the updated API for their 2.0 this would be a great opportunity to work together with them to get a consistent domain language for all testcontainer implementations.

Also, ideas about waiting strategies and general docker handling could be discussed in unison.

This would make it easier for users of different languages to use testcontainers in a similar way.

go get failed

docker.go:121:32: cannot use inspect.NetworkSettings.NetworkSettingsBase.Ports (type "github.com/docker/docker/vendor/github.com/docker/go-connections/nat".PortMap) as type "github.com/docker/go-connections/nat".PortMap in return argument
/home/vchauhan/go/src/github.com/testcontainers/testcontainers-go/docker.go:222:12: assignment mismatch: 1 variable but uuid.NewV4 returns 2 values
/home/vchauhan/go/src/github.com/testcontainers/testcontainers-go/docker.go:244:3: cannot use exposedPortSet (type map["github.com/docker/go-connections/nat".Port]struct {}) as type "github.com/docker/docker/vendor/github.com/docker/go-connections/nat".PortSet in field value
/home/vchauhan/go/src/github.com/testcontainers/testcontainers-go/docker.go:286:3: cannot use exposedPortMap (type map["github.com/docker/go-connections/nat".Port][]"github.com/docker/go-connections/nat".PortBinding) as type "github.com/docker/docker/vendor/github.com/docker/go-connections/nat".PortMap in field value

Blog announcement nit

Hello! I just read about your project via https://blog.gopheracademy.com/testcontainers-go/ ... I'm quite impressed and looking forward to possibly using it in the future.

I just wanted to point out a little nit in the example code in the blog post. In the AppA/AppB (or more specifically (Redis/AppB) example, when you spin up the appB, you seem to be re-using the req variable, which specifies the Redis container, rather than one specifying ApplicationB. (Which, for my selfish purposes, I was hoping was a locally-built executable... Maybe?)

Your .dependabot/config.yaml contained invalid details

Dependabot encountered the following error when parsing your .dependabot/config.yaml:

The property '#/update_configs/0/update_schedule' value "live" did not match one of the following values: daily, weekly, monthly

Please update the config file to conform with Dependabot's specification using our docs and online validator.

You can mention @dependabot in the comments below to contact the Dependabot team.

Error response from daemon: client version 1.40 is too new

I am using latest Docker for Mac 2.0.0.3, Engine: 18.09.2.

=== RUN   TestNginxLatestReturn
--- FAIL: TestNginxLatestReturn (0.01s)
    app_test.go:23: failed to create container: creating reaper failed: Error response from daemon: client version 1.40 is too new. Maximum supported API version is 1.39
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1739281]

goroutine 36 [running]:
testing.tRunner.func1(0xc000141900)
	/usr/local/Cellar/go/1.12/libexec/src/testing/testing.go:830 +0x388
panic(0x17fc540, 0x1fdf940)
	/usr/local/Cellar/go/1.12/libexec/src/runtime/panic.go:522 +0x1b5

On the latest, container started in test does not terminate

When running the following test with the latest commit on master, the container started in the test does not terminate at the end of the test. However, when running the latest stable tag v0.0.4 the container terminates at the end of the test.

go.mod

module testcontainers-bug

go 1.12

require github.com/testcontainers/testcontainers-go v0.0.5-0.20190802071158-3ab1eb570552

testcontainersbug_test.go

package testcontainersbug

import (
	"context"
	"testing"

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

func Test_TestContainersBug(t *testing.T) {
	context := context.Background()
	nginxContainerRequest := testcontainers.ContainerRequest{
		Image:        "nginx",
		ExposedPorts: []string{"80/tcp"},
		WaitingFor:   wait.ForHTTP("/"),
	}
	nginxContainer, err := testcontainers.GenericContainer(context, testcontainers.GenericContainerRequest{
		ContainerRequest: nginxContainerRequest,
		Started:          true,
	})
	if err != nil {
		t.Error(err)
	}
	defer func() {
		err := nginxContainer.Terminate(context)
		if err != nil {
			t.Error(err)
		}
	}()
}

testcontainersbug

BuildKit to build from Dockerfile/oci

Writing testcontainers in Go is fascinating because we have a lot of opportunities to reuse and embed codes to write robust new features.

testcontainers-java supports an ImageFromDockerfile features that help you to build an image from a Dockerfile in this way you can test your application already containerized.

I think we can do something even better designing a test-friendly API using buildkit

Allow insecure TLS in wait.HTTPStrategy

In Java insecure TLS connections can be allowed via JVM-Parameter. Other than that there seems to be no way to allow an insecure TLS connection. For go this is not an option. My current implementation adds AllowInsecure and WithAllowInsecure builder. However, I've asked @kiview if he knows more.

Please retag 0.0.1 as v0.0.1

Go mod is having problems parsing the current latest tag 0.0.1, can this please be updated to use v0.0.1 (v prefix)?

Define Cmd as Slice of Strings

Describe the bug
Not a bug, a suggestion.
When creating a ContainerRequest, we give the Cmd attribute a string type. Then we parse it to make it a slice of strings

Would it be a better idea to define the Cmd attribute in the ContainerRequest struct as a slice of strings ([]string)?

The implicit parsing (splitting) is not clear to a user of the project, I think it would be good to be explicit here.

To Reproduce
Intentionally leaving blank, not a bug, a suggested change.

Expected behavior

type ContainerRequest struct {
	Image        string
	Env          map[string]string
	ExposedPorts []string // allow specifying protocol info
	Cmd          []string
	Labels       map[string]string
	BindMounts   map[string]string
	RegistryCred string
	WaitingFor   wait.Strategy
	Name         string // for specifying container name
	Privileged   bool   // for starting privileged container

	SkipReaper bool // indicates whether we skip setting up a reaper for this
}

Provide way to use multiple wait strategies

Sometimes (when the container does more than one thing) multiple wait-strategies need to be used.

I propose solving this by having a MultipleStrategy that might be used like this:

req := tc.ContainerRequest{
    ExposedPorts: []string{"80"},
    WaitingFor: wait.ForMultiple([]wait.Strategy{
        wait.ForHTTP("/health"),
        wait.ForListeningPort("80/tcp"),
    }),
}

Add WaitStrategy

WaitStrategy is essential for reliable tests. For example an integration test against a HTTP Server would fail if the container/the application in the container isn't ready to accept connections.

I'm willing to contribute on this matter. My suggestion is to closely mimic the Java API as it is proven and a similar API simplifies porting. I'm not too familiar with Go, so I don't know how idiomatic this API is for Go.

Any thoughts?

Don't pull if image is available locally

As per #4 testcontainers-go will always pull an image from registry. The java version will only pull if the image isn't already pulled. I think the java behavior is better as it allows to work offline once the image has been pulled.

Serve Kind (kubernetes)

kind is a Docker in Docker testing solution for Kubernetes. I think having a way to serve an "as a service" testing Kubernetes instance will be very helpful for who is writing Kubernetes integrations such as operator, CRD, SharedInformer.

Implement database containers

The java implementation of testcontainers provides useful preconfigured setups for certain databases.
They usually predefine an image and setup the special waiting strategy for that database.

This should be straight-forward for Go as well since there are many database clients around to do this.

I think I'll get started with a postgres setup.

Canned container, let's kick the discussion

Canned containers are a good way to have pre-built container that can be re-used and can support a deep implementation with the application they are running.

When I started to do them I thought it was a good idea but now I would like to avoid testcontainers to become full of third-party dependency to download. That's why at the moment I am not merging them.

Why I like canned containers:

  1. It is a good way to share the effort we do in order to build a great experience with the library
  2. It helps new contributors to get onboard

What I do not like:

  1. To many dependencies to download with the library

Do you have any suggestions?! I don't know how go mod works with submodules and if they are supported at all. One idea can be to have a foldering like:

canned/mysql
canned/etcd
canned/kubernetes
canned/postgres

And we should figure out a way to have the dependency only for that directory and not for the root o the project.

Affected issue/pr until now:

#108
#107
#105
#102
#98
#59

canned etcd

Lisa from Influx wrote this etcd container request for one of our tests. Probably we can make it to be a canned container as well.

	reqEtcd := ContainerRequest{
		Image: "quay.io/coreos/etcd:v3.2",
		WaitingFor: wait.ForAll(
			wait.ForLog("listening for client requests on 0.0.0.0:2379"),
		),
		ExposedPorts: []string{"2379/tcp", "4001/tcp"},
		SkipReaper:   true,
	}

Internal state of the DockerContainer is not reset after container.Terminate(ctx)

Describe the bug
I'm working on exposing the inspectContainer method, and I realised that the Terminate method does not reset the internal state of the DockerContainer instance: raw and uuid

To Reproduce
I'm sending a PR for this. First commit will be a unit test

Expected behavior
After a container is terminated, its internal state (uuid and raw) is reset

docker info
output of the command:

Client:
 Debug Mode: false

Server:
 Containers: 3
  Running: 3
  Paused: 0
  Stopped: 0
 Images: 306
 Server Version: 19.03.1
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: true
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
 runc version: 425e105d5a03fabd737a126ad93d62a9eeede87f
 init version: fec3683
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 4.9.184-linuxkit
 Operating System: Docker Desktop
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 7.787GiB
 Name: docker-desktop
 ID: BMI5:JZNA:56SZ:5U24:RYWQ:OJQB:AXJA:LQP6:ZGVF:VGNO:M63Z:6W7X
 Docker Root Dir: /var/lib/docker
 Debug Mode: true
  File Descriptors: 55
  Goroutines: 65
  System Time: 2019-09-05T07:25:03.9382341Z
  EventsListeners: 2
 HTTP Proxy: gateway.docker.internal:3128
 HTTPS Proxy: gateway.docker.internal:3129
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false
 Product License: Community Engine

Allow Passing in Context as io.ReadCloser

Description
As of now, when we allow a user to build and image from a Dockerfile, we only allow them to pass in the build context as a string, which is meant to be the path to the build context on their filesystem. Then we archive that directory before we send it to the Docker API, so it becomes a type that implements ReadCloser

I would like to allow the ability to use a ReadCloser as the context in FromDockerfile. This would allow consumers of our API to dynamically set files within the archive before sending it to the Docker API, and would be less implicit than what we do now. However, I would like to also keep our current functionality because I like the abstraction.

To Reproduce
Not a bug. No reproduction steps.

Expected behavior
I am not sure what we want the API to look like, maybe:

type FromDockerfile struct {
    Context string
    ContextArchive io.ReadCloser
    Dockerfile string
}

if the consumer sets both Context and ContextArchive we could return an error, specifying that you should only choose one or the other.

Additional context
this conversation started this idea

Integrate with Kubernetes

Someone at a meetup lately suggested it would be cool to be able to start containers on Kubernetes so blackbox tests of containers can be done using standard test libraries.

Since this library is in an early state, it would be a great opportunity to abstract the container layer in such a way that it does not matter whether the Docker or Kubernetes APIs are used.

Testcontainers project

Hi!

I'm a member of the Testcontainers project and happy to see that you're working on a Golang version.

We already have some ports in works (like .NET or Python). Do you maybe want to join the organization and continue the development as a part of it?
It will also save you a lot of time answering questions like "is your Golang implementation the official one" :D

From our side we have a lot of experience to share, and we were also planning to cover more languages (including Golang), so I think the collaboration would help both sides ๐Ÿ‘

What do you think? :)

Integrate with ryuk to guarantee container termination

The java implementation is using a sidecar container to cleanup containers after the test run, even if the test suite is suddenly interrupted. The implementation of that sidecar is available here: https://github.com/testcontainers/moby-ryuk

The things that have to be done to support this:

  • Start a goroutine when the container is started and inside that:
  • Connect to the sidecar via TCP
  • Send filter expression to mark container to clean up
  • Close connection once container is terminated (indicated via channel)

go vet fail with dependent library

Describe the bug
go vet ./...

        github.com/testcontainers/testcontainers-go imports
        github.com/docker/docker/pkg/archive imports
        github.com/Sirupsen/logrus: github.com/Sirupsen/[email protected]: parsing go.mod:
        module declares its path as: github.com/sirupsen/logrus
                but was required as: github.com/Sirupsen/logrus

sirupsen/logrus#1041

To Reproduce
Referencing github.com/testcontainers/testcontainers-go v0.0.10

Expected behavior
A clear and concise description of what you expected to happen.
The error should not appear.

Additional context
It seams that the https://github.com/docker/docker repo redirects to https://github.com/moby/moby. Maybe upgrade the docker lib dependency ?

Bump docker version

The current pinned docker version is 0.7.3 it would be great to update it. The latest upstream version is 17.03 https://github.com/moby/moby/releases

github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661

Because of the old docker version I am probably getting

# github.com/jaegertracing/jaeger/vendor/github.com/testcontainers/testcontainers-go
vendor/github.com/testcontainers/testcontainers-go/docker.go:190:8: client.NegotiateAPIVersion undefined (type *client.Client has no field or method NegotiateAPIVersion)
vendor/github.com/testcontainers/testcontainers-go/docker.go:327:32: p.client.DaemonHost undefined (type *client.Client has no field or method DaemonHost)

Add function to exec and get output

It would be nice to have a function called ExecOutput that would let the user see the output of an exec command. I've got this fully written with testing; I just want to make sure that this is a welcome addition first :)

Wait for specific console output

It'd be great if it was possible to wait for specific log output, such as

MySQL init process done. Ready for start up.

The port is available far in advance of this, causing me to have to add a time.Sleep(30*time.Second) in my test only to make sure it will have started.

Current wait strategy are weird.

Hello @MeneDev I looked again at the WaitStrategy we have right now, but I don't think they have a lot of sense. I think we should rewrite them to wait on a single port, and probably the waitingFor parameter should be an array of waiting strategies. What do you think?

Dependabot couldn't parse the config file at .dependabot/config.yaml

Dependabot couldn't parse the config file at .dependabot/config.yaml. The error raised was:

(<unknown>): did not find expected '-' indicator while parsing a block collection at line 4 column 5

Please ensure the config file is a valid YAML file. An online YAML linter is available here.

You can mention @dependabot in the comments below to contact the Dependabot team.

Docker compose support

The java implementation supports spinning up docker compose setups to test larger setups as a black box.

This is interesting for this library especially since many DevOps people are using Golang for their services anyways.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.