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

Lines changed: 3 additions & 8 deletions
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

Lines changed: 173 additions & 69 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy