From dfe8a1aa6a5e56a8057b08765a0301b2b2a7bc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 21 Sep 2023 12:36:02 +0200 Subject: [PATCH 01/15] chore: bootstrap dapr module using modulegen go run . new module --image daprio/daprd:1.11.3 --name dapr --title Dapr --- .github/dependabot.yml | 7 + .github/workflows/ci.yml | 2 +- .vscode/.testcontainers-go.code-workspace | 4 + docs/modules/dapr.md | 47 ++++++ mkdocs.yml | 1 + modules/dapr/Makefile | 5 + modules/dapr/dapr.go | 35 ++++ modules/dapr/dapr_test.go | 26 +++ modules/dapr/examples_test.go | 37 +++++ modules/dapr/go.mod | 51 ++++++ modules/dapr/go.sum | 189 ++++++++++++++++++++++ sonar-project.properties | 2 +- 12 files changed, 404 insertions(+), 2 deletions(-) create mode 100644 docs/modules/dapr.md create mode 100644 modules/dapr/Makefile create mode 100644 modules/dapr/dapr.go create mode 100644 modules/dapr/dapr_test.go create mode 100644 modules/dapr/examples_test.go create mode 100644 modules/dapr/go.mod create mode 100644 modules/dapr/go.sum diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9791fe56e0..db1f5e26f3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -86,6 +86,13 @@ updates: day: sunday open-pull-requests-limit: 3 rebase-strategy: disabled + - package-ecosystem: gomod + directory: /modules/dapr + schedule: + interval: monthly + day: sunday + open-pull-requests-limit: 3 + rebase-strategy: disabled - package-ecosystem: gomod directory: /modules/elasticsearch schedule: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b6f69b2ad..bc658485f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: matrix: go-version: [1.20.x, 1.x] platform: [ubuntu-latest, macos-latest] - module: [artemis, clickhouse, compose, couchbase, elasticsearch, gcloud, k3s, kafka, localstack, mariadb, mongodb, mysql, nats, neo4j, postgres, pulsar, rabbitmq, redis, redpanda, vault] + module: [artemis, clickhouse, compose, couchbase, dapr, elasticsearch, gcloud, k3s, kafka, localstack, mariadb, mongodb, mysql, nats, neo4j, postgres, pulsar, rabbitmq, redis, redpanda, vault] uses: ./.github/workflows/ci-test-go.yml with: go-version: ${{ matrix.go-version }} diff --git a/.vscode/.testcontainers-go.code-workspace b/.vscode/.testcontainers-go.code-workspace index 16c84286cd..4a4fa1e016 100644 --- a/.vscode/.testcontainers-go.code-workspace +++ b/.vscode/.testcontainers-go.code-workspace @@ -37,6 +37,10 @@ "name": "module / couchbase", "path": "../modules/couchbase" }, + { + "name": "module / dapr", + "path": "../modules/dapr" + }, { "name": "module / elasticsearch", "path": "../modules/elasticsearch" diff --git a/docs/modules/dapr.md b/docs/modules/dapr.md new file mode 100644 index 0000000000..aba80533fc --- /dev/null +++ b/docs/modules/dapr.md @@ -0,0 +1,47 @@ +# Dapr + +Not available until the next release of testcontainers-go :material-tag: main + +## Introduction + +The Testcontainers module for Dapr. + +## Adding this module to your project dependencies + +Please run the following command to add the Dapr module to your Go dependencies: + +``` +go get github.com/testcontainers/testcontainers-go/modules/dapr +``` + +## Usage example + + +[Creating a Dapr container](../../modules/dapr/examples_test.go) inside_block:runDaprContainer + + +## Module reference + +The Dapr module exposes one entrypoint function to create the Dapr container, and this function receives two parameters: + +```golang +func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*DaprContainer, error) +``` + +- `context.Context`, the Go context. +- `testcontainers.ContainerCustomizer`, a variadic argument for passing options. + +### Container Options + +When starting the Dapr container, you can pass options in a variadic way to configure it. + +#### Image + +If you need to set a different Dapr Docker image, you can use `testcontainers.WithImage` with a valid Docker image +for Dapr. E.g. `testcontainers.WithImage("daprio/daprd:1.11.3")`. + +{% include "../features/common_functional_options.md" %} + +### Container Methods + +The Dapr container exposes the following methods: diff --git a/mkdocs.yml b/mkdocs.yml index 1e247f2879..c0de4413f3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,6 +66,7 @@ nav: - modules/artemis.md - modules/clickhouse.md - modules/couchbase.md + - modules/dapr.md - modules/elasticsearch.md - modules/gcloud.md - modules/k3s.md diff --git a/modules/dapr/Makefile b/modules/dapr/Makefile new file mode 100644 index 0000000000..163461e6d3 --- /dev/null +++ b/modules/dapr/Makefile @@ -0,0 +1,5 @@ +include ../../commons-test.mk + +.PHONY: test +test: + $(MAKE) test-dapr diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go new file mode 100644 index 0000000000..7313a514be --- /dev/null +++ b/modules/dapr/dapr.go @@ -0,0 +1,35 @@ +package dapr + +import ( + "context" + + "github.com/testcontainers/testcontainers-go" +) + +// DaprContainer represents the Dapr container type used in the module +type DaprContainer struct { + testcontainers.Container +} + +// RunContainer creates an instance of the Dapr container type +func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*DaprContainer, error) { + req := testcontainers.ContainerRequest{ + Image: "daprio/daprd:1.11.3", + } + + genericContainerReq := testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + } + + for _, opt := range opts { + opt.Customize(&genericContainerReq) + } + + container, err := testcontainers.GenericContainer(ctx, genericContainerReq) + if err != nil { + return nil, err + } + + return &DaprContainer{Container: container}, nil +} diff --git a/modules/dapr/dapr_test.go b/modules/dapr/dapr_test.go new file mode 100644 index 0000000000..7b79abbba1 --- /dev/null +++ b/modules/dapr/dapr_test.go @@ -0,0 +1,26 @@ +package dapr + +import ( + "context" + "testing" + + "github.com/testcontainers/testcontainers-go" +) + +func TestDapr(t *testing.T) { + ctx := context.Background() + + container, err := RunContainer(ctx, testcontainers.WithImage("daprio/daprd:1.11.3")) + if err != nil { + t.Fatal(err) + } + + // Clean up the container after the test is complete + t.Cleanup(func() { + if err := container.Terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) + + // perform assertions +} diff --git a/modules/dapr/examples_test.go b/modules/dapr/examples_test.go new file mode 100644 index 0000000000..9300771304 --- /dev/null +++ b/modules/dapr/examples_test.go @@ -0,0 +1,37 @@ +package dapr_test + +import ( + "context" + "fmt" + + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/modules/dapr" +) + +func ExampleRunContainer() { + // runDaprContainer { + ctx := context.Background() + + daprContainer, err := dapr.RunContainer(ctx, testcontainers.WithImage("daprio/daprd:1.11.3")) + if err != nil { + panic(err) + } + + // Clean up the container after + defer func() { + if err := daprContainer.Terminate(ctx); err != nil { + panic(err) + } + }() + // } + + state, err := daprContainer.State(ctx) + if err != nil { + panic(err) + } + + fmt.Println(state.Running) + + // Output: + // true +} diff --git a/modules/dapr/go.mod b/modules/dapr/go.mod new file mode 100644 index 0000000000..7218f2f461 --- /dev/null +++ b/modules/dapr/go.mod @@ -0,0 +1,51 @@ +module github.com/testcontainers/testcontainers-go/modules/dapr + +go 1.20 + +require github.com/testcontainers/testcontainers-go v0.24.1 + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.11.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/containerd/containerd v1.7.6 // indirect + github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v24.0.6+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/klauspost/compress v1.16.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/moby/patternmatcher v0.5.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc4 // indirect + github.com/opencontainers/runc v1.1.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/shirou/gopsutil/v3 v3.23.7 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/tklauser/go-sysconf v0.3.11 // indirect + github.com/tklauser/numcpus v0.6.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect + golang.org/x/mod v0.9.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/tools v0.7.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect + google.golang.org/grpc v1.57.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect +) + +replace github.com/testcontainers/testcontainers-go => ../.. diff --git a/modules/dapr/go.sum b/modules/dapr/go.sum new file mode 100644 index 0000000000..5c9de5cf1a --- /dev/null +++ b/modules/dapr/go.sum @@ -0,0 +1,189 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTBdiM= +github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= +github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= +github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= +github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= +github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= +github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= diff --git a/sonar-project.properties b/sonar-project.properties index 33c8c079bd..22d2a0ca7d 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -18,4 +18,4 @@ sonar.test.inclusions=**/*_test.go sonar.test.exclusions=**/vendor/** sonar.go.coverage.reportPaths=**/coverage.out -sonar.go.tests.reportPaths=TEST-unit.xml,examples/cockroachdb/TEST-unit.xml,examples/consul/TEST-unit.xml,examples/nginx/TEST-unit.xml,examples/toxiproxy/TEST-unit.xml,modulegen/TEST-unit.xml,modules/artemis/TEST-unit.xml,modules/clickhouse/TEST-unit.xml,modules/compose/TEST-unit.xml,modules/couchbase/TEST-unit.xml,modules/elasticsearch/TEST-unit.xml,modules/gcloud/TEST-unit.xml,modules/k3s/TEST-unit.xml,modules/kafka/TEST-unit.xml,modules/localstack/TEST-unit.xml,modules/mariadb/TEST-unit.xml,modules/mongodb/TEST-unit.xml,modules/mysql/TEST-unit.xml,modules/nats/TEST-unit.xml,modules/neo4j/TEST-unit.xml,modules/postgres/TEST-unit.xml,modules/pulsar/TEST-unit.xml,modules/rabbitmq/TEST-unit.xml,modules/redis/TEST-unit.xml,modules/redpanda/TEST-unit.xml,modules/vault/TEST-unit.xml +sonar.go.tests.reportPaths=TEST-unit.xml,examples/cockroachdb/TEST-unit.xml,examples/consul/TEST-unit.xml,examples/nginx/TEST-unit.xml,examples/toxiproxy/TEST-unit.xml,modulegen/TEST-unit.xml,modules/artemis/TEST-unit.xml,modules/clickhouse/TEST-unit.xml,modules/compose/TEST-unit.xml,modules/couchbase/TEST-unit.xml,modules/dapr/TEST-unit.xml,modules/elasticsearch/TEST-unit.xml,modules/gcloud/TEST-unit.xml,modules/k3s/TEST-unit.xml,modules/kafka/TEST-unit.xml,modules/localstack/TEST-unit.xml,modules/mariadb/TEST-unit.xml,modules/mongodb/TEST-unit.xml,modules/mysql/TEST-unit.xml,modules/nats/TEST-unit.xml,modules/neo4j/TEST-unit.xml,modules/postgres/TEST-unit.xml,modules/pulsar/TEST-unit.xml,modules/rabbitmq/TEST-unit.xml,modules/redis/TEST-unit.xml,modules/redpanda/TEST-unit.xml,modules/vault/TEST-unit.xml From bc94b3f0dddf9539fc6d1882324e2c6e529cd3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 21 Sep 2023 12:57:54 +0200 Subject: [PATCH 02/15] chore: initialise dapper image --- modules/dapr/dapr.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index 7313a514be..46338ef979 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -6,6 +6,11 @@ import ( "github.com/testcontainers/testcontainers-go" ) +const ( + defaultDaprPort string = "50001/tcp" + defaultDaprAppName string = "dapr-app" +) + // DaprContainer represents the Dapr container type used in the module type DaprContainer struct { testcontainers.Container @@ -14,7 +19,9 @@ type DaprContainer struct { // RunContainer creates an instance of the Dapr container type func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*DaprContainer, error) { req := testcontainers.ContainerRequest{ - Image: "daprio/daprd:1.11.3", + Image: "daprio/daprd:1.11.3", + ExposedPorts: []string{defaultDaprPort}, + Cmd: []string{"./daprd", "-app-id", defaultDaprAppName, "--dapr-listen-addresses=0.0.0.0", "-components-path", "/components"}, } genericContainerReq := testcontainers.GenericContainerRequest{ From 264f1dc0fbad700a7d4e45b96bf4ae669e3db86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 21 Sep 2023 13:10:31 +0200 Subject: [PATCH 03/15] feat: support for setting the app name --- modules/dapr/dapr.go | 10 ++++++++-- modules/dapr/examples_test.go | 5 ++++- modules/dapr/options.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 modules/dapr/options.go diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index 46338ef979..a48a99028c 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -14,6 +14,7 @@ const ( // DaprContainer represents the Dapr container type used in the module type DaprContainer struct { testcontainers.Container + Settings options } // RunContainer creates an instance of the Dapr container type @@ -21,7 +22,6 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize req := testcontainers.ContainerRequest{ Image: "daprio/daprd:1.11.3", ExposedPorts: []string{defaultDaprPort}, - Cmd: []string{"./daprd", "-app-id", defaultDaprAppName, "--dapr-listen-addresses=0.0.0.0", "-components-path", "/components"}, } genericContainerReq := testcontainers.GenericContainerRequest{ @@ -29,14 +29,20 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize Started: true, } + settings := defaultOptions() for _, opt := range opts { + if apply, ok := opt.(Option); ok { + apply(&settings) + } opt.Customize(&genericContainerReq) } + genericContainerReq.Cmd = []string{"./daprd", "-app-id", settings.AppName, "--dapr-listen-addresses=0.0.0.0", "-components-path", "/components"} + container, err := testcontainers.GenericContainer(ctx, genericContainerReq) if err != nil { return nil, err } - return &DaprContainer{Container: container}, nil + return &DaprContainer{Container: container, Settings: settings}, nil } diff --git a/modules/dapr/examples_test.go b/modules/dapr/examples_test.go index 9300771304..874ed2258c 100644 --- a/modules/dapr/examples_test.go +++ b/modules/dapr/examples_test.go @@ -12,7 +12,10 @@ func ExampleRunContainer() { // runDaprContainer { ctx := context.Background() - daprContainer, err := dapr.RunContainer(ctx, testcontainers.WithImage("daprio/daprd:1.11.3")) + daprContainer, err := dapr.RunContainer(ctx, + testcontainers.WithImage("daprio/daprd:1.11.3"), + dapr.WithAppName("dapr-app"), + ) if err != nil { panic(err) } diff --git a/modules/dapr/options.go b/modules/dapr/options.go new file mode 100644 index 0000000000..4f8462d33d --- /dev/null +++ b/modules/dapr/options.go @@ -0,0 +1,33 @@ +package dapr + +import ( + "github.com/testcontainers/testcontainers-go" +) + +type options struct { + AppName string +} + +func defaultOptions() options { + return options{ + AppName: defaultDaprAppName, + } +} + +// Compiler check to ensure that Option implements the testcontainers.ContainerCustomizer interface. +var _ testcontainers.ContainerCustomizer = (*Option)(nil) + +// Option is an option for the Redpanda container. +type Option func(*options) + +// Customize is a NOOP. It's defined to satisfy the testcontainers.ContainerCustomizer interface. +func (o Option) Customize(*testcontainers.GenericContainerRequest) { + // NOOP to satisfy interface. +} + +// WithAppName defines the app name added to the dapr config. +func WithAppName(name string) Option { + return func(o *options) { + o.AppName = name + } +} From ac8d5ac8c9a606ce06c1c8b35cc1e5636a317a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 21 Sep 2023 14:47:06 +0200 Subject: [PATCH 04/15] fix: do not print out when tarring a directory --- file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file.go b/file.go index 509da643d4..a86e93749a 100644 --- a/file.go +++ b/file.go @@ -41,7 +41,7 @@ func tarDir(src string, fileMode int64) (*bytes.Buffer, error) { buffer := &bytes.Buffer{} - fmt.Printf(">> creating TAR file from directory: %s\n", src) + Logger.Printf(">> creating TAR file from directory: %s\n", src) // tar > gzip > buffer zr := gzip.NewWriter(buffer) From 518688e68b3455114fc90b1c4176773551da44e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 21 Sep 2023 15:08:42 +0200 Subject: [PATCH 05/15] feat: add a method to get the gRPC port --- docs/modules/dapr.md | 4 ++++ modules/dapr/dapr.go | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/docs/modules/dapr.md b/docs/modules/dapr.md index aba80533fc..07d68a8845 100644 --- a/docs/modules/dapr.md +++ b/docs/modules/dapr.md @@ -45,3 +45,7 @@ for Dapr. E.g. `testcontainers.WithImage("daprio/daprd:1.11.3")`. ### Container Methods The Dapr container exposes the following methods: + +#### GRPCPort + +This method returns the integer representation of the exposed port for the Dapr gRPC API, which internally is `50001`, and an error if something went wrong while retrieving the port. diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index a48a99028c..9dd595732a 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -17,6 +17,16 @@ type DaprContainer struct { Settings options } +// GRPCPort returns the port used by the Dapr container +func (c *DaprContainer) GRPCPort(ctx context.Context) (int, error) { + port, err := c.MappedPort(ctx, nat.Port(defaultDaprPort)) + if err != nil { + return 0, err + } + + return port.Int(), nil +} + // RunContainer creates an instance of the Dapr container type func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*DaprContainer, error) { req := testcontainers.ContainerRequest{ From a568b3573a4ab8663101e4d47926863f85199d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 21 Sep 2023 15:10:19 +0200 Subject: [PATCH 06/15] docs: document WithAppName option --- docs/modules/dapr.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/modules/dapr.md b/docs/modules/dapr.md index 07d68a8845..3c4bb66c96 100644 --- a/docs/modules/dapr.md +++ b/docs/modules/dapr.md @@ -42,6 +42,10 @@ for Dapr. E.g. `testcontainers.WithImage("daprio/daprd:1.11.3")`. {% include "../features/common_functional_options.md" %} +#### Application Name + +It's possible to define the application name used by Dapr with the `WithAppName(name string)` functional option. If not passed, the default value is `dapr-app`. + ### Container Methods The Dapr container exposes the following methods: From 782a6fb7bffbcf8b2722bad6140cbf633277ecf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 21 Sep 2023 15:27:10 +0200 Subject: [PATCH 07/15] chore: support for adding components --- docs/modules/dapr.md | 32 +++++++++++ modules/dapr/dapr.go | 76 +++++++++++++++++++++++++- modules/dapr/examples_test.go | 5 ++ modules/dapr/mounts/component.yaml.tpl | 14 +++++ modules/dapr/options.go | 45 ++++++++++++++- 5 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 modules/dapr/mounts/component.yaml.tpl diff --git a/docs/modules/dapr.md b/docs/modules/dapr.md index 3c4bb66c96..afcc056c95 100644 --- a/docs/modules/dapr.md +++ b/docs/modules/dapr.md @@ -46,6 +46,38 @@ for Dapr. E.g. `testcontainers.WithImage("daprio/daprd:1.11.3")`. It's possible to define the application name used by Dapr with the `WithAppName(name string)` functional option. If not passed, the default value is `dapr-app`. +#### Components + +You can add components to the Dapr container with the `WithComponents(components ...Component)` functional option. If not passed, the default value is an empty map. + +The `Component` struct has the following fields: + + +[Dapr Component](../../modules/dapr/options.go) inside_block:componentStruct + + +- The key used to internally identify a Component is a string formed by the component name and the component type, separated by a colon. E.g. `my-pubsub:pubsub`. +- Metadata it's a map of strings, where the key is the metadata name and the value is the metadata value. It will be used to render a YAML file with the component configuration. + +Each component will result in a configuration file that will be uploaded to the Dapr container, under the `/components` directory. It's possible to change this file path with the `WithComponentsPath(path string)` functional option. If not passed, the default value is `/components`. + +The file will be named as the component name, and the content will be a YAML file with the following structure: + +```yaml +apiVersion: dapr.io/v1alpha1 + kind: Component + metadata: + name: statestore + spec: + type: state.in-memory + version: v1 + metadata: + - name: foo1 + value: bar1 + - name: foo2 + value: bar2 +``` + ### Container Methods The Dapr container exposes the following methods: diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index 9dd595732a..5eba99292d 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -1,14 +1,32 @@ package dapr import ( + "bytes" "context" + _ "embed" + "fmt" + "os" + "path/filepath" + "text/template" + "time" + "github.com/docker/go-connections/nat" "github.com/testcontainers/testcontainers-go" ) const ( - defaultDaprPort string = "50001/tcp" - defaultDaprAppName string = "dapr-app" + // defaultComponentsPath is the path where the components are mounted in the Dapr container + defaultComponentsPath = "/components" + defaultDaprPort string = "50001/tcp" + defaultDaprAppName string = "dapr-app" +) + +var ( + //go:embed mounts/component.yaml.tpl + componentYamlTpl string + + // componentsTmpDir is the directory where the components are created before being mounted in the container + componentsTmpDir string ) // DaprContainer represents the Dapr container type used in the module @@ -29,6 +47,17 @@ func (c *DaprContainer) GRPCPort(ctx context.Context) (int, error) { // RunContainer creates an instance of the Dapr container type func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*DaprContainer, error) { + componentsTmpDir = filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().UnixMilli()), "components") + err := os.MkdirAll(componentsTmpDir, 0o700) + if err != nil { + return nil, err + } + + // make sure the temporary components directory is removed after the container is run. + defer func() { + _ = os.Remove(componentsTmpDir) + }() + req := testcontainers.ContainerRequest{ Image: "daprio/daprd:1.11.3", ExposedPorts: []string{defaultDaprPort}, @@ -39,6 +68,8 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize Started: true, } + opts = append(opts, WithComponents(NewComponent("statestore", "state.in-memory", map[string]string{}))) + settings := defaultOptions() for _, opt := range opts { if apply, ok := opt.(Option); ok { @@ -47,7 +78,12 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize opt.Customize(&genericContainerReq) } - genericContainerReq.Cmd = []string{"./daprd", "-app-id", settings.AppName, "--dapr-listen-addresses=0.0.0.0", "-components-path", "/components"} + // Transfer the components to the container in the form of a YAML file for each component + if err := renderComponents(settings, &genericContainerReq); err != nil { + return nil, err + } + + genericContainerReq.Cmd = []string{"./daprd", "-app-id", settings.AppName, "--dapr-listen-addresses=0.0.0.0", "-components-path", settings.ComponentsPath} container, err := testcontainers.GenericContainer(ctx, genericContainerReq) if err != nil { @@ -56,3 +92,37 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize return &DaprContainer{Container: container, Settings: settings}, nil } + +// renderComponents renders the configuration file for each component, creating a temporary file for each one under a default +// temporary directory. The entire directory is then uploaded to the container. +func renderComponents(settings options, req *testcontainers.GenericContainerRequest) error { + for _, component := range settings.Components { + name := "component-" + component.Name + ".yaml" + tpl, err := template.New(name).Parse(componentYamlTpl) + if err != nil { + return fmt.Errorf("failed to parse component file template: %w", err) + } + + var componentConfig bytes.Buffer + if err := tpl.Execute(&componentConfig, component); err != nil { + return fmt.Errorf("failed to render component template: %w", err) + } + + content := componentConfig.Bytes() + + tmpComponentFile := filepath.Join(componentsTmpDir, name) + err = os.WriteFile(tmpComponentFile, content, 0o600) + if err != nil { + return err + } + + } + + req.Files = append(req.Files, testcontainers.ContainerFile{ + HostFilePath: componentsTmpDir, + ContainerFilePath: settings.ComponentsPath, + FileMode: 0o600, + }) + + return nil +} diff --git a/modules/dapr/examples_test.go b/modules/dapr/examples_test.go index 874ed2258c..295139ca1e 100644 --- a/modules/dapr/examples_test.go +++ b/modules/dapr/examples_test.go @@ -15,6 +15,11 @@ func ExampleRunContainer() { daprContainer, err := dapr.RunContainer(ctx, testcontainers.WithImage("daprio/daprd:1.11.3"), dapr.WithAppName("dapr-app"), + dapr.WithComponents( + dapr.NewComponent("pubsub", "pubsub.in-memory", map[string]string{"foo": "bar", "bar": "baz"}), + dapr.NewComponent("statestore", "statestore.in-memory", map[string]string{"baz": "qux", "quux": "quuz"}), + ), + dapr.WithComponentsPath("/components"), ) if err != nil { panic(err) diff --git a/modules/dapr/mounts/component.yaml.tpl b/modules/dapr/mounts/component.yaml.tpl new file mode 100644 index 0000000000..f2a7098bd9 --- /dev/null +++ b/modules/dapr/mounts/component.yaml.tpl @@ -0,0 +1,14 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: {{ .Name }} +spec: + type: {{ .Type }} + version: v1 + {{- if .Metadata }} + metadata: + {{- range $key, $value := .Metadata }} + - name: {{ $key }} + value: {{ $value }} + {{- end }} + {{- end }} diff --git a/modules/dapr/options.go b/modules/dapr/options.go index 4f8462d33d..17542d9cb5 100644 --- a/modules/dapr/options.go +++ b/modules/dapr/options.go @@ -5,12 +5,16 @@ import ( ) type options struct { - AppName string + AppName string + Components map[string]Component + ComponentsPath string } func defaultOptions() options { return options{ - AppName: defaultDaprAppName, + AppName: defaultDaprAppName, + Components: map[string]Component{}, + ComponentsPath: defaultComponentsPath, } } @@ -31,3 +35,40 @@ func WithAppName(name string) Option { o.AppName = name } } + +// componentStruct { +type Component struct { + Name string + Type string + Metadata map[string]string +} + +// } + +func (c Component) Key() string { + return c.Name + ":" + c.Type +} + +func NewComponent(name string, t string, metadata map[string]string) Component { + return Component{ + Name: name, + Type: t, + Metadata: metadata, + } +} + +// WithComponents defines the components added to the dapr config, using a variadic list of Component. +func WithComponents(component ...Component) Option { + return func(o *options) { + for _, c := range component { + o.Components[c.Key()] = c + } + } +} + +// WithComponentsPath defines the container path where the components will be stored. +func WithComponentsPath(path string) Option { + return func(o *options) { + o.ComponentsPath = path + } +} From d171880218a7493e0504f671035d9b7c9f93d678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 21 Sep 2023 16:43:59 +0200 Subject: [PATCH 08/15] chore: use typed string for constant --- modules/dapr/dapr.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index 5eba99292d..46b1200d22 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -16,7 +16,7 @@ import ( const ( // defaultComponentsPath is the path where the components are mounted in the Dapr container - defaultComponentsPath = "/components" + defaultComponentsPath string = "/components" defaultDaprPort string = "50001/tcp" defaultDaprAppName string = "dapr-app" ) From 8054f2accaa01a27c470d2fb089a7db1ccdf2f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 21 Sep 2023 17:03:17 +0200 Subject: [PATCH 09/15] chore: wording --- modules/dapr/examples_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dapr/examples_test.go b/modules/dapr/examples_test.go index 295139ca1e..c53e422c71 100644 --- a/modules/dapr/examples_test.go +++ b/modules/dapr/examples_test.go @@ -25,7 +25,7 @@ func ExampleRunContainer() { panic(err) } - // Clean up the container after + // Clean up the container defer func() { if err := daprContainer.Terminate(ctx); err != nil { panic(err) From 87102ac13ea799bccfd0a048e123bdb47e9782de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 25 Sep 2023 11:42:45 +0200 Subject: [PATCH 10/15] chore: use component name as key --- docs/modules/dapr.md | 2 +- modules/dapr/dapr.go | 2 +- modules/dapr/options.go | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/modules/dapr.md b/docs/modules/dapr.md index afcc056c95..63f4e6a1e3 100644 --- a/docs/modules/dapr.md +++ b/docs/modules/dapr.md @@ -56,7 +56,7 @@ The `Component` struct has the following fields: [Dapr Component](../../modules/dapr/options.go) inside_block:componentStruct -- The key used to internally identify a Component is a string formed by the component name and the component type, separated by a colon. E.g. `my-pubsub:pubsub`. +- The key used to internally identify a Component is the component name. E.g. `statestore`. - Metadata it's a map of strings, where the key is the metadata name and the value is the metadata value. It will be used to render a YAML file with the component configuration. Each component will result in a configuration file that will be uploaded to the Dapr container, under the `/components` directory. It's possible to change this file path with the `WithComponentsPath(path string)` functional option. If not passed, the default value is `/components`. diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index 46b1200d22..00b5582680 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -97,7 +97,7 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize // temporary directory. The entire directory is then uploaded to the container. func renderComponents(settings options, req *testcontainers.GenericContainerRequest) error { for _, component := range settings.Components { - name := "component-" + component.Name + ".yaml" + name := component.Name + ".yaml" tpl, err := template.New(name).Parse(componentYamlTpl) if err != nil { return fmt.Errorf("failed to parse component file template: %w", err) diff --git a/modules/dapr/options.go b/modules/dapr/options.go index 17542d9cb5..72f5222f0b 100644 --- a/modules/dapr/options.go +++ b/modules/dapr/options.go @@ -45,8 +45,9 @@ type Component struct { // } +// Key returns the component name, which must be unique. func (c Component) Key() string { - return c.Name + ":" + c.Type + return c.Name } func NewComponent(name string, t string, metadata map[string]string) Component { From a9a89131aec76b9260f7e74f19df01cb8744c767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 25 Sep 2023 12:29:58 +0200 Subject: [PATCH 11/15] chore: add unit tests for the render process of a component --- modules/dapr/dapr.go | 17 ++------- modules/dapr/options.go | 25 +++++++++++++ modules/dapr/options_test.go | 35 +++++++++++++++++++ .../component-statestore-state.in-memory.yaml | 12 +++++++ 4 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 modules/dapr/options_test.go create mode 100644 modules/dapr/testdata/component-statestore-state.in-memory.yaml diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index 00b5582680..b3fb019ba4 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -1,13 +1,11 @@ package dapr import ( - "bytes" "context" _ "embed" "fmt" "os" "path/filepath" - "text/template" "time" "github.com/docker/go-connections/nat" @@ -97,20 +95,9 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize // temporary directory. The entire directory is then uploaded to the container. func renderComponents(settings options, req *testcontainers.GenericContainerRequest) error { for _, component := range settings.Components { - name := component.Name + ".yaml" - tpl, err := template.New(name).Parse(componentYamlTpl) - if err != nil { - return fmt.Errorf("failed to parse component file template: %w", err) - } - - var componentConfig bytes.Buffer - if err := tpl.Execute(&componentConfig, component); err != nil { - return fmt.Errorf("failed to render component template: %w", err) - } - - content := componentConfig.Bytes() + content, err := component.Render() - tmpComponentFile := filepath.Join(componentsTmpDir, name) + tmpComponentFile := filepath.Join(componentsTmpDir, component.FileName()) err = os.WriteFile(tmpComponentFile, content, 0o600) if err != nil { return err diff --git a/modules/dapr/options.go b/modules/dapr/options.go index 72f5222f0b..7546aa806d 100644 --- a/modules/dapr/options.go +++ b/modules/dapr/options.go @@ -1,6 +1,10 @@ package dapr import ( + "bytes" + "fmt" + "text/template" + "github.com/testcontainers/testcontainers-go" ) @@ -50,6 +54,27 @@ func (c Component) Key() string { return c.Name } +// FileName returns the component file name, which must be unique. +func (c Component) FileName() string { + return c.Name + ".yaml" +} + +// Render returns the component configuration as a byte slice, obtained after the interpolation +// of the component template. +func (c Component) Render() ([]byte, error) { + tpl, err := template.New(c.FileName()).Parse(componentYamlTpl) + if err != nil { + return nil, fmt.Errorf("failed to parse component file template: %w", err) + } + + var componentConfig bytes.Buffer + if err := tpl.Execute(&componentConfig, c); err != nil { + return nil, fmt.Errorf("failed to render component template: %w", err) + } + + return componentConfig.Bytes(), nil +} + func NewComponent(name string, t string, metadata map[string]string) Component { return Component{ Name: name, diff --git a/modules/dapr/options_test.go b/modules/dapr/options_test.go new file mode 100644 index 0000000000..3f3874d238 --- /dev/null +++ b/modules/dapr/options_test.go @@ -0,0 +1,35 @@ +package dapr + +import ( + "io" + "os" + "path/filepath" + "testing" +) + +func TestComponent_Render(t *testing.T) { + yamlFilePath := filepath.Join("testdata", "component-statestore-state.in-memory.yaml") + yamlFile, err := os.Open(yamlFilePath) + if err != nil { + t.Fatal(err) + } + + content, err := io.ReadAll(yamlFile) + if err != nil { + t.Fatal(err) + } + + component := NewComponent("statestore", "state.in-memory", map[string]string{ + "foo1": "bar1", + "foo2": "bar2", + }) + + rendered, err := component.Render() + if err != nil { + t.Fatal(err) + } + + if string(content) != string(rendered) { + t.Fatalf("expected %s, got %s", string(content), string(rendered)) + } +} diff --git a/modules/dapr/testdata/component-statestore-state.in-memory.yaml b/modules/dapr/testdata/component-statestore-state.in-memory.yaml new file mode 100644 index 0000000000..9a91d8275b --- /dev/null +++ b/modules/dapr/testdata/component-statestore-state.in-memory.yaml @@ -0,0 +1,12 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.in-memory + version: v1 + metadata: + - name: foo1 + value: bar1 + - name: foo2 + value: bar2 From 16cf77ac0c5fbf9f6348504fb1d31a552316d2ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 25 Sep 2023 13:34:51 +0200 Subject: [PATCH 12/15] chore: create Dapr network --- modules/dapr/dapr.go | 39 ++++++++++++++++++++++++++++++++++- modules/dapr/dapr_test.go | 39 ++++++++++++++++++++++++++++++++++- modules/dapr/examples_test.go | 8 +++++++ modules/dapr/options.go | 2 ++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index b3fb019ba4..b883d37799 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -17,6 +17,9 @@ const ( defaultComponentsPath string = "/components" defaultDaprPort string = "50001/tcp" defaultDaprAppName string = "dapr-app" + // defaultDaprNetworkName is the name of the network created by the Dapr container, in which the app container is connected + // and all the components will be attached to. + defaultDaprNetworkName string = "dapr-network" ) var ( @@ -30,6 +33,7 @@ var ( // DaprContainer represents the Dapr container type used in the module type DaprContainer struct { testcontainers.Container + Network testcontainers.Network Settings options } @@ -43,6 +47,19 @@ func (c *DaprContainer) GRPCPort(ctx context.Context) (int, error) { return port.Int(), nil } +// Terminate terminates the Dapr container and removes the Dapr network +func (c *DaprContainer) Terminate(ctx context.Context) error { + if err := c.Container.Terminate(ctx); err != nil { + return err + } + + if err := c.Network.Remove(ctx); err != nil { + return err + } + + return nil +} + // RunContainer creates an instance of the Dapr container type func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*DaprContainer, error) { componentsTmpDir = filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().UnixMilli()), "components") @@ -83,12 +100,32 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize genericContainerReq.Cmd = []string{"./daprd", "-app-id", settings.AppName, "--dapr-listen-addresses=0.0.0.0", "-components-path", settings.ComponentsPath} + nw, err := testcontainers.GenericNetwork(ctx, testcontainers.GenericNetworkRequest{ + NetworkRequest: testcontainers.NetworkRequest{ + Name: settings.NetworkName, + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create Dapr network: %w", err) + } + + // attach Dapr container to the Dapr network + genericContainerReq.Networks = []string{settings.NetworkName} + // setting the network alias to the application name will make it easier to connect to the Dapr container + genericContainerReq.NetworkAliases = map[string][]string{ + settings.NetworkName: {settings.AppName}, + } + container, err := testcontainers.GenericContainer(ctx, genericContainerReq) if err != nil { return nil, err } - return &DaprContainer{Container: container, Settings: settings}, nil + return &DaprContainer{ + Container: container, + Settings: settings, + Network: nw, + }, nil } // renderComponents renders the configuration file for each component, creating a temporary file for each one under a default diff --git a/modules/dapr/dapr_test.go b/modules/dapr/dapr_test.go index 7b79abbba1..64c616f7fc 100644 --- a/modules/dapr/dapr_test.go +++ b/modules/dapr/dapr_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/testcontainers/testcontainers-go" ) @@ -22,5 +24,40 @@ func TestDapr(t *testing.T) { } }) - // perform assertions + // verify that the dapr network is created and the container is attached to it + cli, err := testcontainers.NewDockerClientWithOpts(context.Background()) + if err != nil { + t.Fatal(err) + } + + response, err := cli.NetworkList(ctx, types.NetworkListOptions{ + Filters: filters.NewArgs(filters.Arg("name", container.Settings.NetworkName)), + }) + if err != nil { + t.Fatal(err) + } + + if len(response) != 1 { + t.Fatalf("expected 1 network, got %d", len(response)) + } + + daprNetwork := response[0] + + if daprNetwork.Name != container.Settings.NetworkName { + t.Fatalf("expected network name %s, got %s", container.Settings.NetworkName, daprNetwork.Name) + } + + // verify that the container is attached to the dapr network + nws, err := container.Networks(context.Background()) + if err != nil { + t.Fatal(err) + } + + if len(nws) != 1 { + t.Fatalf("expected 1 network, got %d", len(nws)) + } + + if nws[0] != container.Settings.NetworkName { + t.Fatalf("expected network name %s, got %s", container.Settings.NetworkName, nws[0]) + } } diff --git a/modules/dapr/examples_test.go b/modules/dapr/examples_test.go index c53e422c71..60f80959a7 100644 --- a/modules/dapr/examples_test.go +++ b/modules/dapr/examples_test.go @@ -40,6 +40,14 @@ func ExampleRunContainer() { fmt.Println(state.Running) + networks, err := daprContainer.Networks(ctx) + if err != nil { + panic(err) + } + + fmt.Println(networks[0]) + // Output: // true + // dapr-network } diff --git a/modules/dapr/options.go b/modules/dapr/options.go index 7546aa806d..eac95c3b12 100644 --- a/modules/dapr/options.go +++ b/modules/dapr/options.go @@ -12,6 +12,7 @@ type options struct { AppName string Components map[string]Component ComponentsPath string + NetworkName string } func defaultOptions() options { @@ -19,6 +20,7 @@ func defaultOptions() options { AppName: defaultDaprAppName, Components: map[string]Component{}, ComponentsPath: defaultComponentsPath, + NetworkName: defaultDaprNetworkName, } } From 07d541a76e7a162c967229f53176082be012d45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 25 Sep 2023 13:41:53 +0200 Subject: [PATCH 13/15] chore: support for changing the network name --- docs/modules/dapr.md | 9 +++++++++ modules/dapr/examples_test.go | 1 + modules/dapr/options.go | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/docs/modules/dapr.md b/docs/modules/dapr.md index 63f4e6a1e3..664ead1e3a 100644 --- a/docs/modules/dapr.md +++ b/docs/modules/dapr.md @@ -31,6 +31,11 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize - `context.Context`, the Go context. - `testcontainers.ContainerCustomizer`, a variadic argument for passing options. +This entrypoint function will create: + +- a Dapr network in which the Dapr container and all its components will be attached. Default name is `dapr-network`. +- a Dapr container. + ### Container Options When starting the Dapr container, you can pass options in a variadic way to configure it. @@ -46,6 +51,10 @@ for Dapr. E.g. `testcontainers.WithImage("daprio/daprd:1.11.3")`. It's possible to define the application name used by Dapr with the `WithAppName(name string)` functional option. If not passed, the default value is `dapr-app`. +#### Network Name + +It's possible to define the network name used by Dapr with the `WithNetworkName(name string)` functional option. If not passed, the default value is `dapr-network`. + #### Components You can add components to the Dapr container with the `WithComponents(components ...Component)` functional option. If not passed, the default value is an empty map. diff --git a/modules/dapr/examples_test.go b/modules/dapr/examples_test.go index 60f80959a7..b35d8ace70 100644 --- a/modules/dapr/examples_test.go +++ b/modules/dapr/examples_test.go @@ -15,6 +15,7 @@ func ExampleRunContainer() { daprContainer, err := dapr.RunContainer(ctx, testcontainers.WithImage("daprio/daprd:1.11.3"), dapr.WithAppName("dapr-app"), + dapr.WithNetworkName("dapr-network"), dapr.WithComponents( dapr.NewComponent("pubsub", "pubsub.in-memory", map[string]string{"foo": "bar", "bar": "baz"}), dapr.NewComponent("statestore", "statestore.in-memory", map[string]string{"baz": "qux", "quux": "quuz"}), diff --git a/modules/dapr/options.go b/modules/dapr/options.go index eac95c3b12..33b834bc14 100644 --- a/modules/dapr/options.go +++ b/modules/dapr/options.go @@ -42,6 +42,13 @@ func WithAppName(name string) Option { } } +// WithNetworkName defines the network name in which the dapr container is attached. +func WithNetworkName(name string) Option { + return func(o *options) { + o.NetworkName = name + } +} + // componentStruct { type Component struct { Name string From 03f5ac9f2bff9d2f00cf83d9f2c939fdd83a993a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 25 Sep 2023 14:30:37 +0200 Subject: [PATCH 14/15] fix: initiliase default statestore properly --- modules/dapr/dapr.go | 2 -- modules/dapr/options.go | 9 +++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index b883d37799..c2a478ae58 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -83,8 +83,6 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize Started: true, } - opts = append(opts, WithComponents(NewComponent("statestore", "state.in-memory", map[string]string{}))) - settings := defaultOptions() for _, opt := range opts { if apply, ok := opt.(Option); ok { diff --git a/modules/dapr/options.go b/modules/dapr/options.go index 33b834bc14..c62cb55e79 100644 --- a/modules/dapr/options.go +++ b/modules/dapr/options.go @@ -15,10 +15,15 @@ type options struct { NetworkName string } +// defaultOptions returns the default options for the Dapr container, including an in-memory state store. func defaultOptions() options { + inMemoryStore := NewComponent("statestore", "state.in-memory", map[string]string{}) + return options{ - AppName: defaultDaprAppName, - Components: map[string]Component{}, + AppName: defaultDaprAppName, + Components: map[string]Component{ + inMemoryStore.Key(): inMemoryStore, + }, ComponentsPath: defaultComponentsPath, NetworkName: defaultDaprNetworkName, } From 56aadbde86d9ef4441a8661bb51e8b7b03a52c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Mon, 25 Sep 2023 17:29:08 +0200 Subject: [PATCH 15/15] chore: support for adding runnable components --- docs/modules/dapr.md | 5 +++ modules/dapr/dapr.go | 84 ++++++++++++++++++++++++++++------- modules/dapr/dapr_test.go | 1 + modules/dapr/examples_test.go | 3 +- modules/dapr/options.go | 31 +++++++------ 5 files changed, 94 insertions(+), 30 deletions(-) diff --git a/docs/modules/dapr.md b/docs/modules/dapr.md index 664ead1e3a..2f41cb14ef 100644 --- a/docs/modules/dapr.md +++ b/docs/modules/dapr.md @@ -35,6 +35,7 @@ This entrypoint function will create: - a Dapr network in which the Dapr container and all its components will be attached. Default name is `dapr-network`. - a Dapr container. +- A component container for each component that has a Docker image defined. See [Components](#components) for more information. ### Container Options @@ -66,8 +67,12 @@ The `Component` struct has the following fields: - The key used to internally identify a Component is the component name. E.g. `statestore`. +- The image is the Docker image used by the component. E.g. `redis:6-alpine`. - Metadata it's a map of strings, where the key is the metadata name and the value is the metadata value. It will be used to render a YAML file with the component configuration. +!!! info + Those components with a Docker image will be run as a separate container in the Dapr network, and using Dapr's container ID as the container network mode. + Each component will result in a configuration file that will be uploaded to the Dapr container, under the `/components` directory. It's possible to change this file path with the `WithComponentsPath(path string)` functional option. If not passed, the default value is `/components`. The file will be named as the component name, and the content will be a YAML file with the following structure: diff --git a/modules/dapr/dapr.go b/modules/dapr/dapr.go index c2a478ae58..8c623962b5 100644 --- a/modules/dapr/dapr.go +++ b/modules/dapr/dapr.go @@ -8,7 +8,9 @@ import ( "path/filepath" "time" + "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" + "github.com/testcontainers/testcontainers-go" ) @@ -33,8 +35,9 @@ var ( // DaprContainer represents the Dapr container type used in the module type DaprContainer struct { testcontainers.Container - Network testcontainers.Network - Settings options + Network testcontainers.Network + ComponentContainers map[string]testcontainers.Container + Settings options } // GRPCPort returns the port used by the Dapr container @@ -47,20 +50,35 @@ func (c *DaprContainer) GRPCPort(ctx context.Context) (int, error) { return port.Int(), nil } -// Terminate terminates the Dapr container and removes the Dapr network +// Terminate terminates the Dapr container and removes the component containers and the Dapr network, +// in that particular order. func (c *DaprContainer) Terminate(ctx context.Context) error { if err := c.Container.Terminate(ctx); err != nil { - return err + return fmt.Errorf("failed to terminate Dapr container %w", err) + } + + for key, componentContainer := range c.ComponentContainers { + // do not terminate the component container if it has no image defined + if c.Settings.Components[key].Image == "" { + continue + } + + if err := componentContainer.Terminate(ctx); err != nil { + return fmt.Errorf("failed to terminate component container %w", err) + } } if err := c.Network.Remove(ctx); err != nil { - return err + return fmt.Errorf("failed to terminate Dapr network %w", err) } return nil } -// RunContainer creates an instance of the Dapr container type +// RunContainer creates an instance of the Dapr container type, creating the following elements: +// - a Dapr network +// - a Dapr container +// - a component container for each component defined in the options. The component must have an image defined. func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*DaprContainer, error) { componentsTmpDir = filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().UnixMilli()), "components") err := os.MkdirAll(componentsTmpDir, 0o700) @@ -96,7 +114,7 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize return nil, err } - genericContainerReq.Cmd = []string{"./daprd", "-app-id", settings.AppName, "--dapr-listen-addresses=0.0.0.0", "-components-path", settings.ComponentsPath} + genericContainerReq.Cmd = []string{"./daprd", "-app-id", settings.AppName, "--dapr-listen-addresses=0.0.0.0", "-components-path", defaultComponentsPath} nw, err := testcontainers.GenericNetwork(ctx, testcontainers.GenericNetworkRequest{ NetworkRequest: testcontainers.NetworkRequest{ @@ -114,26 +132,62 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize settings.NetworkName: {settings.AppName}, } - container, err := testcontainers.GenericContainer(ctx, genericContainerReq) + daprContainer, err := testcontainers.GenericContainer(ctx, genericContainerReq) if err != nil { return nil, err } + // we must start the component containers in container mode, so that they can connect to the Dapr container + networkMode := fmt.Sprintf("container:%v", daprContainer.GetContainerID()) + + componentContainers := map[string]testcontainers.Container{} + for _, component := range settings.Components { + if component.Image == "" { + continue + } + + componentContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: component.Image, + Networks: []string{settings.NetworkName}, + NetworkAliases: map[string][]string{ + settings.NetworkName: {component.Name}, + }, + HostConfigModifier: func(hc *container.HostConfig) { + hc.NetworkMode = container.NetworkMode(networkMode) + }, + }, + Started: true, + }) + if err != nil { + return nil, err + } + + componentContainers[component.Key()] = componentContainer + } + return &DaprContainer{ - Container: container, - Settings: settings, - Network: nw, + Container: daprContainer, + Settings: settings, + ComponentContainers: componentContainers, + Network: nw, }, nil } // renderComponents renders the configuration file for each component, creating a temporary file for each one under a default -// temporary directory. The entire directory is then uploaded to the container. +// temporary directory. The entire directory is then uploaded to the container, including the +// right permissions (0o777) for Dapr to access the files. func renderComponents(settings options, req *testcontainers.GenericContainerRequest) error { + execPermissions := os.FileMode(0o777) + for _, component := range settings.Components { content, err := component.Render() + if err != nil { + return err + } tmpComponentFile := filepath.Join(componentsTmpDir, component.FileName()) - err = os.WriteFile(tmpComponentFile, content, 0o600) + err = os.WriteFile(tmpComponentFile, content, execPermissions) if err != nil { return err } @@ -142,8 +196,8 @@ func renderComponents(settings options, req *testcontainers.GenericContainerRequ req.Files = append(req.Files, testcontainers.ContainerFile{ HostFilePath: componentsTmpDir, - ContainerFilePath: settings.ComponentsPath, - FileMode: 0o600, + ContainerFilePath: defaultComponentsPath, + FileMode: int64(execPermissions), }) return nil diff --git a/modules/dapr/dapr_test.go b/modules/dapr/dapr_test.go index 64c616f7fc..e7072efbe5 100644 --- a/modules/dapr/dapr_test.go +++ b/modules/dapr/dapr_test.go @@ -6,6 +6,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/testcontainers/testcontainers-go" ) diff --git a/modules/dapr/examples_test.go b/modules/dapr/examples_test.go index b35d8ace70..5570483422 100644 --- a/modules/dapr/examples_test.go +++ b/modules/dapr/examples_test.go @@ -18,9 +18,8 @@ func ExampleRunContainer() { dapr.WithNetworkName("dapr-network"), dapr.WithComponents( dapr.NewComponent("pubsub", "pubsub.in-memory", map[string]string{"foo": "bar", "bar": "baz"}), - dapr.NewComponent("statestore", "statestore.in-memory", map[string]string{"baz": "qux", "quux": "quuz"}), + dapr.NewComponentWithImage("statestore", "state.redis", "redis:6-alpine", map[string]string{"baz": "qux", "quux": "quuz"}), ), - dapr.WithComponentsPath("/components"), ) if err != nil { panic(err) diff --git a/modules/dapr/options.go b/modules/dapr/options.go index c62cb55e79..08e39b0266 100644 --- a/modules/dapr/options.go +++ b/modules/dapr/options.go @@ -9,10 +9,9 @@ import ( ) type options struct { - AppName string - Components map[string]Component - ComponentsPath string - NetworkName string + AppName string + Components map[string]Component + NetworkName string } // defaultOptions returns the default options for the Dapr container, including an in-memory state store. @@ -24,8 +23,7 @@ func defaultOptions() options { Components: map[string]Component{ inMemoryStore.Key(): inMemoryStore, }, - ComponentsPath: defaultComponentsPath, - NetworkName: defaultDaprNetworkName, + NetworkName: defaultDaprNetworkName, } } @@ -58,6 +56,7 @@ func WithNetworkName(name string) Option { type Component struct { Name string Type string + Image string Metadata map[string]string } @@ -89,6 +88,8 @@ func (c Component) Render() ([]byte, error) { return componentConfig.Bytes(), nil } +// NewComponentWithImage returns a new Component without its Docker image. +// Those components without a Docker image won't be run as a separate container in the Dapr network. func NewComponent(name string, t string, metadata map[string]string) Component { return Component{ Name: name, @@ -97,6 +98,17 @@ func NewComponent(name string, t string, metadata map[string]string) Component { } } +// NewComponentWithImage returns a new Component including its Docker image. +// Those components with a Docker image will be run as a separate container in the Dapr network, +// and using Dapr's container ID as the container network mode. +func NewComponentWithImage(name string, t string, image string, metadata map[string]string) Component { + c := NewComponent(name, t, metadata) + + c.Image = image + + return c +} + // WithComponents defines the components added to the dapr config, using a variadic list of Component. func WithComponents(component ...Component) Option { return func(o *options) { @@ -105,10 +117,3 @@ func WithComponents(component ...Component) Option { } } } - -// WithComponentsPath defines the container path where the components will be stored. -func WithComponentsPath(path string) Option { - return func(o *options) { - o.ComponentsPath = path - } -}