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, } }