1//go:build live
2
3package docker
4
5import (
6 "context"
7 "testing"
8 "time"
9
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12
13 "github.com/bnema/gordon/internal/domain"
14)
15
16// TestLiveRuntime_SharedMembershipAndDetachment proves cross-app traffic is
17// possible only for services explicitly enrolled in the same shared
18// network, and that unrelated services stay detached.
19func TestLiveRuntime_SharedMembershipAndDetachment(t *testing.T) {
20 runtime, ctx := liveRuntime(t)
21 netA := liveProbeNetwork(t, runtime, ctx)
22 netB := liveProbeNetwork(t, runtime, ctx)
23
24 sharedName := liveName("gordon-live-shared")
25 require.NoError(t, runtime.CreateNetwork(ctx, sharedName, domain.NetworkConfig{
26 Driver: "bridge", Labels: domain.AppSharedNetworkLabels("live-shared"),
27 }))
28 t.Cleanup(func() {
29 cleanup, cancel := context.WithTimeout(context.Background(), 30*time.Second)
30 defer cancel()
31 _ = runtime.RemoveNetwork(cleanup, sharedName)
32 })
33
34 listenerName := liveName("live-shared-listener")
35 listener := liveCreate(t, runtime, ctx, &domain.ContainerConfig{
36 Image: liveImage, Name: listenerName, NetworkMode: netA,
37 Entrypoint: []string{"sh", "-c", "while true; do nc -l -p 9100 >/dev/null 2>&1; done"},
38 AutoRemove: false,
39 })
40 require.NoError(t, runtime.ConnectContainerToNetwork(ctx, listener.ID, sharedName))
41
42 // Enrolled on the shared network from another app network.
43 enrolled := liveCreate(t, runtime, ctx, &domain.ContainerConfig{
44 Image: liveImage, Name: liveName("live-shared-enrolled"), NetworkMode: netB,
45 Entrypoint: []string{"sleep", "180"}, AutoRemove: false,
46 })
47 require.NoError(t, runtime.ConnectContainerToNetwork(ctx, enrolled.ID, sharedName))
48
49 // An unrelated service on the same app network but NOT enrolled.
50 outsider := liveCreate(t, runtime, ctx, &domain.ContainerConfig{
51 Image: liveImage, Name: liveName("live-shared-outsider"), NetworkMode: netB,
52 Entrypoint: []string{"sleep", "180"}, AutoRemove: false,
53 })
54
55 probe := []string{"sh", "-c", "nc -z -w 3 " + listenerName + " 9100"}
56
57 require.Eventually(t, func() bool {
58 result, err := runtime.ExecInContainer(ctx, enrolled.ID, probe)
59 return err == nil && result.ExitCode == 0
60 }, 60*time.Second, 2*time.Second, "an enrolled service must reach the peer through the shared network")
61
62 result, err := runtime.ExecInContainer(ctx, outsider.ID, probe)
63 require.NoError(t, err, "the exec itself must run; a transport error is not proof of isolation")
64 assert.NotZero(t, result.ExitCode, "an unrelated service must not reach a peer it is not enrolled with")
65}