Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cf8e46f

Browse files
committed
remove docker dependency from tests
1 parent 1dd3133 commit cf8e46f

File tree

2 files changed

+176
-77
lines changed

2 files changed

+176
-77
lines changed

coderd/workspaceagents.go

+3-8
Original file line numberDiff line numberDiff line change
@@ -765,14 +765,9 @@ func (api *API) workspaceAgentListContainers(rw http.ResponseWriter, r *http.Req
765765
}
766766

767767
// Filter in-place by labels
768-
for idx, ct := range cts.Containers {
769-
if !maputil.Subset(labels, ct.Labels) {
770-
cts.Containers = append(cts.Containers[:idx], cts.Containers[idx+1:]...)
771-
}
772-
}
773-
// filtered := slices.DeleteFunc(cts.Containers, func(ct codersdk.WorkspaceAgentDevcontainer) bool {
774-
// return !maputil.Subset(labels, ct.Labels)
775-
// })
768+
cts.Containers = slices.DeleteFunc(cts.Containers, func(ct codersdk.WorkspaceAgentDevcontainer) bool {
769+
return !maputil.Subset(labels, ct.Labels)
770+
})
776771

777772
httpapi.Write(ctx, rw, http.StatusOK, cts)
778773
}

coderd/workspaceagents_test.go

+173-69
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"maps"
88
"net"
99
"net/http"
10+
"os"
1011
"runtime"
1112
"strconv"
1213
"strings"
@@ -15,18 +16,21 @@ import (
1516
"time"
1617

1718
"github.com/go-jose/go-jose/v4/jwt"
19+
"github.com/google/go-cmp/cmp"
1820
"github.com/google/uuid"
1921
"github.com/ory/dockertest/v3"
2022
"github.com/ory/dockertest/v3/docker"
2123
"github.com/stretchr/testify/assert"
2224
"github.com/stretchr/testify/require"
25+
"go.uber.org/mock/gomock"
2326
"golang.org/x/xerrors"
2427
"google.golang.org/protobuf/types/known/timestamppb"
2528
"tailscale.com/tailcfg"
2629

2730
"cdr.dev/slog"
2831
"cdr.dev/slog/sloggers/slogtest"
2932
"github.com/coder/coder/v2/agent"
33+
"github.com/coder/coder/v2/agent/agentcontainers"
3034
"github.com/coder/coder/v2/agent/agenttest"
3135
agentproto "github.com/coder/coder/v2/agent/proto"
3236
"github.com/coder/coder/v2/coderd/coderdtest"
@@ -1058,82 +1062,182 @@ func TestWorkspaceAgentListeningPorts(t *testing.T) {
10581062
func TestWorkspaceAgentContainers(t *testing.T) {
10591063
t.Parallel()
10601064

1061-
if runtime.GOOS != "linux" {
1062-
t.Skip("this test creates containers, which is flaky on non-linux runners")
1063-
}
1065+
// This test will not normally run in CI, but is kept here as a semi-manual
1066+
// test for local development. Run it as follows:
1067+
// CODER_TEST_USE_DOCKER=1 go test -run TestWorkspaceAgentContainers/Docker ./coderd
1068+
t.Run("Docker", func(t *testing.T) {
1069+
t.Parallel()
1070+
if ctud, ok := os.LookupEnv("CODER_TEST_USE_DOCKER"); !ok || ctud != "1" {
1071+
t.Skip("Set CODER_TEST_USE_DOCKER=1 to run this test")
1072+
}
10641073

1065-
pool, err := dockertest.NewPool("")
1066-
require.NoError(t, err, "Could not connect to docker")
1067-
testLabels := map[string]string{
1068-
"com.coder.test": uuid.New().String(),
1069-
}
1070-
ct, err := pool.RunWithOptions(&dockertest.RunOptions{
1071-
Repository: "busybox",
1072-
Tag: "latest",
1073-
Cmd: []string{"sleep", "infinity"},
1074-
Labels: testLabels,
1075-
}, func(config *docker.HostConfig) {
1076-
config.AutoRemove = true
1077-
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
1078-
})
1079-
require.NoError(t, err, "Could not start test docker container")
1080-
t.Cleanup(func() {
1081-
assert.NoError(t, pool.Purge(ct), "Could not purge resource %q", ct.Container.Name)
1082-
})
1074+
pool, err := dockertest.NewPool("")
1075+
require.NoError(t, err, "Could not connect to docker")
1076+
testLabels := map[string]string{
1077+
"com.coder.test": uuid.New().String(),
1078+
}
1079+
ct, err := pool.RunWithOptions(&dockertest.RunOptions{
1080+
Repository: "busybox",
1081+
Tag: "latest",
1082+
Cmd: []string{"sleep", "infinity"},
1083+
Labels: testLabels,
1084+
}, func(config *docker.HostConfig) {
1085+
config.AutoRemove = true
1086+
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
1087+
})
1088+
require.NoError(t, err, "Could not start test docker container")
1089+
t.Cleanup(func() {
1090+
assert.NoError(t, pool.Purge(ct), "Could not purge resource %q", ct.Container.Name)
1091+
})
10831092

1084-
// Start another container which we will expect to ignore.
1085-
ct2, err := pool.RunWithOptions(&dockertest.RunOptions{
1086-
Repository: "busybox",
1087-
Tag: "latest",
1088-
Cmd: []string{"sleep", "infinity"},
1089-
Labels: map[string]string{"com.coder.test": "ignoreme"},
1090-
}, func(config *docker.HostConfig) {
1091-
config.AutoRemove = true
1092-
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
1093-
})
1094-
require.NoError(t, err, "Could not start second test docker container")
1095-
t.Cleanup(func() {
1096-
assert.NoError(t, pool.Purge(ct2), "Could not purge resource %q", ct2.Container.Name)
1093+
// Start another container which we will expect to ignore.
1094+
ct2, err := pool.RunWithOptions(&dockertest.RunOptions{
1095+
Repository: "busybox",
1096+
Tag: "latest",
1097+
Cmd: []string{"sleep", "infinity"},
1098+
Labels: map[string]string{"com.coder.test": "ignoreme"},
1099+
}, func(config *docker.HostConfig) {
1100+
config.AutoRemove = true
1101+
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
1102+
})
1103+
require.NoError(t, err, "Could not start second test docker container")
1104+
t.Cleanup(func() {
1105+
assert.NoError(t, pool.Purge(ct2), "Could not purge resource %q", ct2.Container.Name)
1106+
})
1107+
1108+
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{})
1109+
1110+
user := coderdtest.CreateFirstUser(t, client)
1111+
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
1112+
OrganizationID: user.OrganizationID,
1113+
OwnerID: user.UserID,
1114+
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
1115+
return agents
1116+
}).Do()
1117+
_ = agenttest.New(t, client.URL, r.AgentToken, func(opts *agent.Options) {
1118+
opts.ContainerLister = &agentcontainers.DockerCLILister{}
1119+
})
1120+
resources := coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).Wait()
1121+
require.Len(t, resources, 1, "expected one resource")
1122+
require.Len(t, resources[0].Agents, 1, "expected one agent")
1123+
agentID := resources[0].Agents[0].ID
1124+
1125+
ctx := testutil.Context(t, testutil.WaitLong)
1126+
1127+
// If we filter by testLabels, we should only get one container back.
1128+
res, err := client.WorkspaceAgentListContainers(ctx, agentID, testLabels)
1129+
require.NoError(t, err, "failed to list containers filtered by test label")
1130+
require.Len(t, res.Containers, 1, "expected exactly one container")
1131+
assert.Equal(t, ct.Container.ID, res.Containers[0].ID, "expected container ID to match")
1132+
assert.Equal(t, "busybox:latest", res.Containers[0].Image, "expected container image to match")
1133+
assert.Equal(t, ct.Container.Config.Labels, res.Containers[0].Labels, "expected container labels to match")
1134+
assert.Equal(t, strings.TrimPrefix(ct.Container.Name, "/"), res.Containers[0].FriendlyName, "expected container name to match")
1135+
assert.True(t, res.Containers[0].Running, "expected container to be running")
1136+
assert.Equal(t, "running", res.Containers[0].Status, "expected container status to be running")
1137+
1138+
// List all containers and ensure we get at least both (there may be more).
1139+
res, err = client.WorkspaceAgentListContainers(ctx, agentID, nil)
1140+
require.NoError(t, err, "failed to list all containers")
1141+
require.NotEmpty(t, res.Containers, "expected to find containers")
1142+
var found []string
1143+
for _, c := range res.Containers {
1144+
found = append(found, c.ID)
1145+
}
1146+
require.Contains(t, found, ct.Container.ID, "expected to find first container without label filter")
1147+
require.Contains(t, found, ct2.Container.ID, "expected to find first container without label filter")
10971148
})
10981149

1099-
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{})
1150+
// This test will normally run in CI. It uses a mock implementation of
1151+
// agentcontainers.Lister instead of introducing a hard dependency on Docker.
1152+
t.Run("Mock", func(t *testing.T) {
1153+
t.Parallel()
11001154

1101-
user := coderdtest.CreateFirstUser(t, client)
1102-
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
1103-
OrganizationID: user.OrganizationID,
1104-
OwnerID: user.UserID,
1105-
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
1106-
return agents
1107-
}).Do()
1108-
_ = agenttest.New(t, client.URL, r.AgentToken, func(_ *agent.Options) {})
1109-
resources := coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).Wait()
1110-
require.Len(t, resources, 1, "expected one resource")
1111-
require.Len(t, resources[0].Agents, 1, "expected one agent")
1112-
agentID := resources[0].Agents[0].ID
1155+
// begin test fixtures
1156+
testLabels := map[string]string{
1157+
"com.coder.test": uuid.New().String(),
1158+
}
1159+
testResponse := codersdk.WorkspaceAgentListContainersResponse{
1160+
Containers: []codersdk.WorkspaceAgentDevcontainer{
1161+
{
1162+
ID: uuid.NewString(),
1163+
CreatedAt: dbtime.Now(),
1164+
FriendlyName: testutil.GetRandomName(t),
1165+
Image: "busybox:latest",
1166+
Labels: testLabels,
1167+
Running: true,
1168+
Status: "running",
1169+
Ports: []codersdk.WorkspaceAgentListeningPort{
1170+
{
1171+
Network: "tcp",
1172+
Port: 80,
1173+
},
1174+
},
1175+
Volumes: map[string]string{
1176+
"/host": "/container",
1177+
},
1178+
},
1179+
},
1180+
}
1181+
// end test fixtures
11131182

1114-
ctx := testutil.Context(t, testutil.WaitLong)
1183+
for _, tc := range []struct {
1184+
name string
1185+
setupMock func(*agentcontainers.MockLister) (codersdk.WorkspaceAgentListContainersResponse, error)
1186+
}{
1187+
{
1188+
name: "test response",
1189+
setupMock: func(mcl *agentcontainers.MockLister) (codersdk.WorkspaceAgentListContainersResponse, error) {
1190+
mcl.EXPECT().List(gomock.Any()).Return(testResponse, nil).Times(1)
1191+
return testResponse, nil
1192+
},
1193+
},
1194+
{
1195+
name: "error response",
1196+
setupMock: func(mcl *agentcontainers.MockLister) (codersdk.WorkspaceAgentListContainersResponse, error) {
1197+
mcl.EXPECT().List(gomock.Any()).Return(codersdk.WorkspaceAgentListContainersResponse{}, assert.AnError).Times(1)
1198+
return codersdk.WorkspaceAgentListContainersResponse{}, assert.AnError
1199+
},
1200+
},
1201+
} {
1202+
tc := tc
1203+
t.Run(tc.name, func(t *testing.T) {
1204+
t.Parallel()
11151205

1116-
// If we filter by testLabels, we should only get one container back.
1117-
res, err := client.WorkspaceAgentListContainers(ctx, agentID, testLabels)
1118-
require.NoError(t, err, "failed to list containers filtered by test label")
1119-
require.Len(t, res.Containers, 1, "expected exactly one container")
1120-
assert.Equal(t, ct.Container.ID, res.Containers[0].ID, "expected container ID to match")
1121-
assert.Equal(t, "busybox:latest", res.Containers[0].Image, "expected container image to match")
1122-
assert.Equal(t, ct.Container.Config.Labels, res.Containers[0].Labels, "expected container labels to match")
1123-
assert.Equal(t, strings.TrimPrefix(ct.Container.Name, "/"), res.Containers[0].FriendlyName, "expected container name to match")
1124-
assert.True(t, res.Containers[0].Running, "expected container to be running")
1125-
assert.Equal(t, "running", res.Containers[0].Status, "expected container status to be running")
1126-
1127-
// List all containers and ensure we get at least both (there may be more).
1128-
res, err = client.WorkspaceAgentListContainers(ctx, agentID, nil)
1129-
require.NoError(t, err, "failed to list all containers")
1130-
require.NotEmpty(t, res.Containers, "expected to find containers")
1131-
var found []string
1132-
for _, c := range res.Containers {
1133-
found = append(found, c.ID)
1134-
}
1135-
require.Contains(t, found, ct.Container.ID, "expected to find first container without label filter")
1136-
require.Contains(t, found, ct2.Container.ID, "expected to find first container without label filter")
1206+
ctrl := gomock.NewController(t)
1207+
mcl := agentcontainers.NewMockLister(ctrl)
1208+
expected, expectedErr := tc.setupMock(mcl)
1209+
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{})
1210+
user := coderdtest.CreateFirstUser(t, client)
1211+
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
1212+
OrganizationID: user.OrganizationID,
1213+
OwnerID: user.UserID,
1214+
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
1215+
return agents
1216+
}).Do()
1217+
_ = agenttest.New(t, client.URL, r.AgentToken, func(opts *agent.Options) {
1218+
opts.ContainerLister = mcl
1219+
})
1220+
resources := coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).Wait()
1221+
require.Len(t, resources, 1, "expected one resource")
1222+
require.Len(t, resources[0].Agents, 1, "expected one agent")
1223+
agentID := resources[0].Agents[0].ID
1224+
1225+
ctx := testutil.Context(t, testutil.WaitLong)
1226+
1227+
// List containers and ensure we get the expected mocked response.
1228+
res, err := client.WorkspaceAgentListContainers(ctx, agentID, nil)
1229+
if expectedErr != nil {
1230+
require.Contains(t, err.Error(), expectedErr.Error(), "unexpected error")
1231+
require.Empty(t, res, "expected empty response")
1232+
} else {
1233+
require.NoError(t, err, "failed to list all containers")
1234+
if diff := cmp.Diff(expected, res); diff != "" {
1235+
t.Fatalf("unexpected response (-want +got):\n%s", diff)
1236+
}
1237+
}
1238+
})
1239+
}
1240+
})
11371241
}
11381242

11391243
func TestWorkspaceAgentAppHealth(t *testing.T) {

0 commit comments

Comments
 (0)