Skip to content

Commit

Permalink
added getting started, gc and container creation
Browse files Browse the repository at this point in the history
  • Loading branch information
Gianluca Arbezzano committed May 12, 2020
1 parent 7046559 commit 8308029
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 7 deletions.
3 changes: 1 addition & 2 deletions docs/contributing.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# Contributing

Hello

We follow the same one used by [testcontainers-java check it out](https://www.testcontainers.org/contributing/).
6 changes: 1 addition & 5 deletions docs/contributing_docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ We use the [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) t

In addition we use a [custom plugin](https:/rnorth/mkdocs-codeinclude-plugin) for inclusion of code snippets.

We publish our documentation using Netlify.
We publish our documentation using Netlify.

## Previewing rendered content

### Using Docker locally

The root of the project contains a `docker-compose.yml` file. Simply run `docker-compose up` and then access the docs at [http://localhost:8000](http://localhost:8000).

### Using Python locally

* Ensure that you have Python 3.6.0 or higher.
Expand Down
54 changes: 54 additions & 0 deletions docs/features/creating_container.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# How to create a container

When I have to describe TestContainer I say: "it is a wrapper around the docker
daemon designed for tests."

This libraries demands all the complexity of creating and managing container to
Docker to stay focused on usability in a testing environment.

You can use this library to run everything you can run with docker:

* NoSQL databases or other data stores (e.g. redis, elasticsearch, mongo)
* Web servers/proxies (e.g. nginx, apache)
* Log services (e.g. logstash, kibana)
* Other services developed by your team/organization which are already dockerized

## GenericContainer

`testcontainers.GenericContainer` identifies the ability to spin up a single
container, you can look at it as a different way to create a `docker run`
command.

```go
func TestNginxLatestReturn(t *testing.T) {
ctx := context.Background()
req := testcontainers.ContainerRequest{
Image: "nginx",
ExposedPorts: []string{"80/tcp"},
WaitingFor: wait.ForHTTP("/"),
}
nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
if err != nil {
t.Error(err)
}
defer nginxC.Terminate(ctx)
ip, err := nginxC.Host(ctx)
if err != nil {
t.Error(err)
}
port, err := nginxC.MappedPort(ctx, "80")
if err != nil {
t.Error(err)
}
resp, err := http.Get(fmt.Sprintf("http://%s:%s", ip, port.Port()))
if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status code %d. Got %d.", http.StatusOK, resp.StatusCode)
}
}
```

This test creates an Nginx container and it validates that it returns a 200 as
StatusCode.
43 changes: 43 additions & 0 deletions docs/features/garbage_collector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Garbage Collector

Usually one test creates at least one container. At the end it means a lot of
containers running. We need to have a way to keep the CI servers reliable
removing unused containers.

Containers can be unused because:

1. Test is over and the container is not needed anymore.
2. Test failed, we do not need that container anymore because next build will
create new containers.

## Terminate function

As we saw previously there are at least two way to remove unused containers.
First one is to use the `Terminate(context.Conext)` function available when a
container is created. You can call it in your test or you use `defer` .

!!!tip
Remember to `defer` as soon as possible so you won't forget. The best time
is as soon as you call `testcontainers.GenericContainer` but remember to
check for the `err` first.

## Ryuk

[https:/testcontainers/moby-ryuk](ryuk) helps you to remove
containers/networks/volumes by given filter after specified delay.

It is a project developer by TestContainer and it is used across the board for
Java, Go and any more.

When you run one tests you will see that there is not only the containers your
tests requires running, there is another one called `ryuk`, we refers to it as
`Reaper` as well in this library.

Based on container labels it removes resources created from testcontainers that
are running from more than 10 minutes.

!!!tip
This feature can be disabled when creating a container

In this way even if you do not call Terminate, something will keep your
environment clean. It will also clean itself when there is nothing left to do.
5 changes: 5 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@

## About

TestContainers is popular in Java, but there are other langauges as well. This
is the Go implementation.

The project is opensource and you can have a look at the code on
[GitHub](httsp:/testcontainers/testcontainers-go),
99 changes: 99 additions & 0 deletions docs/quickstart/gotest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
TestContainers plays well with the native `go test` framework.

The ideal use case is for integration or end to end tests. It helps you to spin
up and manage the dependencies life cycle via Docker.

This is way Docker has to be available for this library to work.

## 1. Install

We use [gomod](https://blog.golang.org/using-go-modules) and you can get it installed via:

```
go get github.com/testcontainers/testcontainers-go
```

## 2. Spin up Redis

```go
func TestWithRedis(t *testing.T) {
ctx := context.Background()
req := testcontainers.ContainerRequest{
Image: "redis:latest",
ExposedPorts: []string{"6379/tcp"},
WaitingFor: wait.ForLog("Ready to accept connections"),
}
redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
if err != nil {
t.Error(err)
}
defer redisC.Terminate(ctx)
}
```

The `testcontainers.ContainerRequest` describes how the Docker container will
look like. As you ca see it recalls to a lot of concepts related to it.

* `Image` is the docker image the container starts from.
* `ExposedPorts` lists the port that has to be exposed from the container
* `WaitingFor` is a field you can use to validate when a container is ready. It
is important to get this set because it helps to know when the container is
ready to reach any traffic. In this case we checks for the logs we know coming
from Redis, telling us that it is ready to accept requests.

When you use `ExposedPorts` you have to image yourself using `docker run -p
<port>`. When you do so dockerd maps the selected `<port>` from inside the
container to a random one available on your host.

In the previous example we expose `6379` for `tcp` traffic to the outside. This
allows Redis to be reachable from your code that runs outside the container but
it also makes parallelization possible because if you add `t.Parallel` to you
test and each of them starts a Redis container all of them will be exposed on a
different random port.

`testcontainers.GenericContainer` creates the container. In this example we are
using `Started: true`. It means that the container function will wait for the
container to be up and running. If you set the `Start` value to `false` it won'
start. Leaving to you the decision about when to start it.

All the container has to be removed at some point, otherwise they will run until
the host will overloaded. One of the way we have to clean after ourself is
defering the terminated function: `defer redisC.Terminate(ctx)`.

!!!tip
Lock at [features/garbage_collector.md] to know the other way we have to
clean after ourself.

## 3. Make your code to talk with the container

This is just an example but usually Go applications that relay on redis are
using the [redis-go](https:/go-redis/redis) client. This code gets the endpoint from the container we
just started and it configures the client.

```go
endpoint, err := redisC.Endpoint(ctx, "")
if err != nil {
t.Error(err)
}

client := redis.NewClient(&redis.Options{
Addr: endpoint,
})

_ = client
```

We expose only one port, so the `Endpoint` does not need a second argument set.

!!!tip
if you expose more than one port you an specify the one you need as second
argument

In this case it returns: `localhost:<mappedportfor-6379>`.

## 3. Run the test

You can run the test via `go test ./...`
6 changes: 6 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ markdown_extensions:
- pymdownx.snippets
nav:
- Home: index.md
- Quickstart:
- quickstart/gotest.md
- Features:
- features/creating_container.md
- features/garbage_collector.md
- Contributing:
- contributing.md
- contributing_docs.md
- Getting help: getting_help.md
extra:
latest_version: 1.14.1

0 comments on commit 8308029

Please sign in to comment.