Skip to content

Commit

Permalink
feat: support generating Go modules or example modules (#826)
Browse files Browse the repository at this point in the history
* chore: create a separate module for the generator

* chore: get the root directory in the new layout

* feat: support for creating modules or examples

* fix: update dependabot and mkdocs

* chore: extract example parent dir calculation to a function

* chore: define entrypoint and container name methods

* chore: rename setupTech to startContainer

* chore: rename setup function in example modules

* chor: include modulegen in dependabot

* chore: add build script for modulegen

* chore: include modulegen tests in CI

* fix: update tests

* docs: add missing dependabot entry

* chore: update GH workflow example type

* fix: do not depend on hardcoded lengths

* chore: generate modules in its own directory in docs

* chore: add build script for modules

* docs: reuse
  • Loading branch information
mdelapenya authored Feb 14, 2023
1 parent 3d27c00 commit 566911e
Show file tree
Hide file tree
Showing 56 changed files with 579 additions and 189 deletions.
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ updates:
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /modulegen
schedule:
interval: weekly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /modules/compose
schedule:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ jobs:
if: ${{ matrix.platform == 'ubuntu-latest' }}
run: make test-unit

- name: Run example generator tests
- name: Run module generator tests
if: ${{ matrix.platform == 'ubuntu-latest' }}
run: make -C examples test-unit
run: make -C modulegen test-unit

- name: Run checker
run: |
Expand Down
38 changes: 4 additions & 34 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,12 @@
# Code examples

In this section you'll discover how to create code examples for _Testcontainers for Go_.
In this section you'll discover how to create code examples for _Testcontainers for Go_, which are almost the same as Go modules, but without exporting any public API.

## Interested in adding a new example?

We have provided a command line tool to generate the scaffolding for the code of the example you are interested in. This tool will generate:

- a Go module for the example, including:
- go.mod and go.sum files, including the current version of _Testcontainer for Go_.
- a Go package named after the example, in lowercase
- a Go file for the creation of the container, using a dedicated struct in which the image flag is set as Docker image.
- a Go test file for running a simple test for your container, consuming the above struct.
- a Makefile to run the tests in a consistent manner
- a tools.go file including the build tools (i.e. `gotestsum`) used to build/run the example.
- a markdown file in the docs/examples directory including the snippets for both the creation of the container and a simple test.
- a new Nav entry for the example in the docs site, adding it to the `mkdocs.yml` file located at the root directory of the project.
- a GitHub workflow file in the .github/workflows directory to run the tests for the example.

### Command line flags

| Flag | Type | Required | Description |
|------|------|----------|-------------|
| -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?
Their main goal is to create shareable code snippets on how to use certain technology (e.g. a database, a web server), in order to explore its usage before converting the example module into a real Go module exposing a public API.

- 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:
## Interested in adding a new example?

```shell
go run . --name ${NAME_OF_YOUR_EXAMPLE} --image "${REGISTRY}/${EXAMPLE}:${TAG}" --title ${TITLE_OF_YOUR_EXAMPLE}
```
Please refer to the documentation of the code generation tool [here](../modules/index.md).

## Update Go dependencies in the examples

Expand Down
56 changes: 56 additions & 0 deletions docs/modules/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Testcontainers for Go modules

In this section you'll discover how to create Go modules for _Testcontainers for Go_.

## Interested in adding a new module?

We have provided a command line tool to generate the scaffolding for the code of the example you are interested in. This tool will generate:

- a Go module for the example, including:
- go.mod and go.sum files, including the current version of _Testcontainer for Go_.
- a Go package named after the module, in lowercase
- a Go file for the creation of the container, using a dedicated struct in which the image flag is set as Docker image.
- a Go test file for running a simple test for your container, consuming the above struct.
- a Makefile to run the tests in a consistent manner
- a tools.go file including the build tools (i.e. `gotestsum`) used to build/run the example.
- a markdown file in the docs/modules directory including the snippets for both the creation of the container and a simple test.
- a new Nav entry for the module in the docs site, adding it to the `mkdocs.yml` file located at the root directory of the project.
- a GitHub workflow file in the .github/workflows directory to run the tests for the example.
- an entry in Dependabot's configuration file, in order to receive dependency updates.

### Command line flags

| Flag | Type | Required | Description |
|------|------|----------|-------------|
| -name | string | Yes | Name of the module, 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 module (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. |
| -as-module | bool | No | If set, the module will be generated as a Go module, under the modules directory. Otherwise, it will be generated as a subdirectory of the examples directory. |

### What is this tool not doing?

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

### How to run the tool

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

```shell
go run . --name ${NAME_OF_YOUR_MODULE} --image "${REGISTRY}/${MODULE}:${TAG}" --title ${TITLE_OF_YOUR_MODULE}
```

or for creating a Go module:

```shell
go run . --name ${NAME_OF_YOUR_MODULE} --image "${REGISTRY}/${MODULE}:${TAG}" --title ${TITLE_OF_YOUR_MODULE} --as-module
```

## Update Go dependencies in the modules

To update the Go dependencies in the modules, please run:

```shell
$ cd modules
$ make tidy-examples
```
File renamed without changes.
7 changes: 0 additions & 7 deletions examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@ include ../commons-test.mk
dependencies-scan-examples:
@find . -type f -name Makefile -execdir make dependencies-scan \;

.PHONY: test
test: test-unit
$(MAKE) -C cockroachdb test
$(MAKE) -C nginx test
$(MAKE) -C pulsar test
$(MAKE) -C redis test

.PHONY: tidy-examples
tidy-examples:
@find . -type f -name Makefile -execdir make tools-tidy \;
28 changes: 0 additions & 28 deletions examples/_template/example.go.tmpl

This file was deleted.

5 changes: 3 additions & 2 deletions examples/bigtable/bigtable.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bigtable
import (
"context"
"fmt"

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

"github.com/testcontainers/testcontainers-go"
Expand All @@ -14,8 +15,8 @@ type bigtableContainer struct {
URI string
}

// setupBigtable creates an instance of the bigtable container type
func setupBigtable(ctx context.Context) (*bigtableContainer, error) {
// startContainer creates an instance of the bigtable container type
func startContainer(ctx context.Context) (*bigtableContainer, error) {
req := testcontainers.ContainerRequest{
Image: "gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators",
ExposedPorts: []string{"9000/tcp"},
Expand Down
2 changes: 1 addition & 1 deletion examples/bigtable/bigtable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const (
func TestBigtable(t *testing.T) {
ctx := context.Background()

container, err := setupBigtable(ctx)
container, err := startContainer(ctx)
if err != nil {
t.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/cockroachdb/cockroachdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type cockroachDBContainer struct {
URI string
}

func setupCockroachDB(ctx context.Context) (*cockroachDBContainer, error) {
func startContainer(ctx context.Context) (*cockroachDBContainer, error) {
req := testcontainers.ContainerRequest{
Image: "cockroachdb/cockroach:latest-v21.1",
ExposedPorts: []string{"26257/tcp", "8080/tcp"},
Expand Down
2 changes: 1 addition & 1 deletion examples/cockroachdb/cockroachdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestIntegrationDBInsertSelect(t *testing.T) {

ctx := context.Background()

cdbContainer, err := setupCockroachDB(ctx)
cdbContainer, err := startContainer(ctx)
if err != nil {
t.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/consul/consul.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ type consulContainer struct {
endpoint string
}

// setupConsul creates an instance of the consul container type
func setupConsul(ctx context.Context) (*consulContainer, error) {
// startContainer creates an instance of the consul container type
func startContainer(ctx context.Context) (*consulContainer, error) {
req := testcontainers.ContainerRequest{
Image: "consul:latest",
ExposedPorts: []string{"8500/tcp", "8600/udp"},
Expand Down
2 changes: 1 addition & 1 deletion examples/consul/consul_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
func TestConsul(t *testing.T) {
ctx := context.Background()

container, err := setupConsul(ctx)
container, err := startContainer(ctx)
if err != nil {
t.Fatal(err)
}
Expand Down
5 changes: 3 additions & 2 deletions examples/datastore/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package datastore
import (
"context"
"fmt"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)
Expand All @@ -13,8 +14,8 @@ type datastoreContainer struct {
URI string
}

// setupDatastore creates an instance of the datastore container type
func setupDatastore(ctx context.Context) (*datastoreContainer, error) {
// startContainer creates an instance of the datastore container type
func startContainer(ctx context.Context) (*datastoreContainer, error) {
req := testcontainers.ContainerRequest{
Image: "gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators",
ExposedPorts: []string{"8081/tcp"},
Expand Down
2 changes: 1 addition & 1 deletion examples/datastore/datastore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Task struct {
func TestDatastore(t *testing.T) {
ctx := context.Background()

container, err := setupDatastore(ctx)
container, err := startContainer(ctx)
if err != nil {
t.Fatal(err)
}
Expand Down
5 changes: 3 additions & 2 deletions examples/firestore/firestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package firestore
import (
"context"
"fmt"

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

"github.com/testcontainers/testcontainers-go"
Expand All @@ -14,8 +15,8 @@ type firestoreContainer struct {
URI string
}

// setupFirestore creates an instance of the firestore container type
func setupFirestore(ctx context.Context) (*firestoreContainer, error) {
// startContainer creates an instance of the firestore container type
func startContainer(ctx context.Context) (*firestoreContainer, error) {
req := testcontainers.ContainerRequest{
Image: "gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators",
ExposedPorts: []string{"8080/tcp"},
Expand Down
2 changes: 1 addition & 1 deletion examples/firestore/firestore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (ec emulatorCreds) RequireTransportSecurity() bool {
func TestFirestore(t *testing.T) {
ctx := context.Background()

container, err := setupFirestore(ctx)
container, err := startContainer(ctx)
if err != nil {
t.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/mongodb/mongodb.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ type mongodbContainer struct {
testcontainers.Container
}

// setupMongoDB creates an instance of the mongodb container type
func setupMongoDB(ctx context.Context) (*mongodbContainer, error) {
// startContainer creates an instance of the mongodb container type
func startContainer(ctx context.Context) (*mongodbContainer, error) {
req := testcontainers.ContainerRequest{
Image: "mongo:6",
ExposedPorts: []string{"27017/tcp"},
Expand Down
2 changes: 1 addition & 1 deletion examples/mongodb/mongodb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
func TestMongoDB(t *testing.T) {
ctx := context.Background()

container, err := setupMongoDB(ctx)
container, err := startContainer(ctx)
if err != nil {
t.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ type mysqlContainer struct {
testcontainers.Container
}

// setupMysql creates an instance of the mysql container type
func setupMysql(ctx context.Context) (*mysqlContainer, error) {
// startContainer creates an instance of the mysql container type
func startContainer(ctx context.Context) (*mysqlContainer, error) {
req := testcontainers.ContainerRequest{
Image: "mysql:8",
ExposedPorts: []string{"3306/tcp", "33060/tcp"},
Expand Down
2 changes: 1 addition & 1 deletion examples/mysql/mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
func TestMysql(t *testing.T) {
ctx := context.Background()

container, err := setupMysql(ctx)
container, err := startContainer(ctx)
if err != nil {
t.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/nginx/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type nginxContainer struct {
URI string
}

func setupNginx(ctx context.Context) (*nginxContainer, error) {
func startContainer(ctx context.Context) (*nginxContainer, error) {
req := testcontainers.ContainerRequest{
Image: "nginx",
ExposedPorts: []string{"80/tcp"},
Expand Down
2 changes: 1 addition & 1 deletion examples/nginx/nginx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestIntegrationNginxLatestReturn(t *testing.T) {

ctx := context.Background()

nginxC, err := setupNginx(ctx)
nginxC, err := startContainer(ctx)
if err != nil {
t.Fatal(err)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ func WithInitialDatabase(user string, password string, dbName string) func(req *
}
}

// setupPostgres creates an instance of the postgres container type
func setupPostgres(ctx context.Context, opts ...postgresContainerOption) (*postgresContainer, error) {
// startContainer creates an instance of the postgres container type
func startContainer(ctx context.Context, opts ...postgresContainerOption) (*postgresContainer, error) {
req := testcontainers.ContainerRequest{
Image: "postgres:11-alpine",
Env: map[string]string{},
Expand Down
8 changes: 4 additions & 4 deletions examples/postgres/postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestPostgres(t *testing.T) {
port, err := nat.NewPort("tcp", "5432")
require.NoError(t, err)

container, err := setupPostgres(ctx,
container, err := startContainer(ctx,
WithPort(port.Port()),
WithInitialDatabase(user, password, dbname),
WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)),
Expand Down Expand Up @@ -77,12 +77,12 @@ func TestContainerWithWaitForSQL(t *testing.T) {
}

t.Run("default query", func(t *testing.T) {
container, err := setupPostgres(ctx, WithPort(port), WithInitialDatabase("postgres", "password", dbname), WithWaitStrategy(wait.ForSQL(nat.Port(port), "postgres", dbURL)))
container, err := startContainer(ctx, WithPort(port), WithInitialDatabase("postgres", "password", dbname), WithWaitStrategy(wait.ForSQL(nat.Port(port), "postgres", dbURL)))
require.NoError(t, err)
require.NotNil(t, container)
})
t.Run("custom query", func(t *testing.T) {
container, err := setupPostgres(
container, err := startContainer(
ctx,
WithPort(port),
WithInitialDatabase(user, password, dbname),
Expand All @@ -92,7 +92,7 @@ func TestContainerWithWaitForSQL(t *testing.T) {
require.NotNil(t, container)
})
t.Run("custom bad query", func(t *testing.T) {
container, err := setupPostgres(
container, err := startContainer(
ctx,
WithPort(port),
WithInitialDatabase(user, password, dbname),
Expand Down
Loading

0 comments on commit 566911e

Please sign in to comment.