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

Skip to content

Commit a17ca58

Browse files
committed
[ci skip] commit wip
1 parent 2648440 commit a17ca58

File tree

6 files changed

+138
-7
lines changed

6 files changed

+138
-7
lines changed

agent/containers.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,12 @@ func (*dockerCLIContainerLister) List(ctx context.Context) ([]codersdk.Workspace
133133

134134
func convertDockerCLIList(in dockerCLIList) codersdk.WorkspaceAgentContainer {
135135
out := codersdk.WorkspaceAgentContainer{
136-
FriendlyName: in.Names,
137-
ID: in.ID,
138-
Image: in.Image,
139-
Labels: map[string]string{},
136+
ID: in.ID,
137+
Image: in.Image,
138+
Labels: map[string]string{},
140139
}
140+
// Remove the leading slash from the container name
141+
out.FriendlyName = strings.TrimPrefix(in.Names, "/")
141142

142143
createdAt, err := time.Parse(dockerCreatedAtTimeFormat, in.CreatedAt)
143144
if err != nil {

coderd/coderd.go

+1
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,7 @@ func New(options *Options) *API {
12121212
r.Get("/listening-ports", api.workspaceAgentListeningPorts)
12131213
r.Get("/connection", api.workspaceAgentConnection)
12141214
r.Get("/coordinate", api.workspaceAgentClientCoordinate)
1215+
r.Get("/containers", api.workspaceAgentListContainers)
12151216

12161217
// PTY is part of workspaceAppServer.
12171218
})

coderd/workspaceagents.go

+55
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,61 @@ func (api *API) workspaceAgentListeningPorts(rw http.ResponseWriter, r *http.Req
678678
httpapi.Write(ctx, rw, http.StatusOK, portsResponse)
679679
}
680680

681+
func (api *API) workspaceAgentListContainers(rw http.ResponseWriter, r *http.Request) {
682+
ctx := r.Context()
683+
workspaceAgent := httpmw.WorkspaceAgentParam(r)
684+
685+
// If the agent is unreachable, the request will hang. Assume that if we
686+
// don't get a response after 30s that the agent is unreachable.
687+
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
688+
defer cancel()
689+
apiAgent, err := db2sdk.WorkspaceAgent(
690+
api.DERPMap(),
691+
*api.TailnetCoordinator.Load(),
692+
workspaceAgent,
693+
nil,
694+
nil,
695+
nil,
696+
api.AgentInactiveDisconnectTimeout,
697+
api.DeploymentValues.AgentFallbackTroubleshootingURL.String(),
698+
)
699+
if err != nil {
700+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
701+
Message: "Internal error reading workspace agent.",
702+
Detail: err.Error(),
703+
})
704+
return
705+
}
706+
if apiAgent.Status != codersdk.WorkspaceAgentConnected {
707+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
708+
Message: fmt.Sprintf("Agent state is %q, it must be in the %q state.", apiAgent.Status, codersdk.WorkspaceAgentConnected),
709+
})
710+
return
711+
}
712+
713+
agentConn, release, err := api.agentProvider.AgentConn(ctx, workspaceAgent.ID)
714+
if err != nil {
715+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
716+
Message: "Internal error dialing workspace agent.",
717+
Detail: err.Error(),
718+
})
719+
return
720+
}
721+
defer release()
722+
723+
// Get a list of containers that the agent is able to detect
724+
containers, err := agentConn.ListContainers(ctx)
725+
if err != nil {
726+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
727+
Message: "Internal error fetching containers.",
728+
Detail: err.Error(),
729+
})
730+
return
731+
}
732+
733+
httpapi.Write(ctx, rw, http.StatusOK, containers)
734+
}
735+
681736
// @Summary Get connection info for workspace agent
682737
// @ID get-connection-info-for-workspace-agent
683738
// @Security CoderSessionToken

coderd/workspaceagents_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616

1717
"github.com/go-jose/go-jose/v4/jwt"
1818
"github.com/google/uuid"
19+
"github.com/ory/dockertest/v3"
20+
"github.com/ory/dockertest/v3/docker"
1921
"github.com/stretchr/testify/assert"
2022
"github.com/stretchr/testify/require"
2123
"golang.org/x/xerrors"
@@ -1053,6 +1055,61 @@ func TestWorkspaceAgentListeningPorts(t *testing.T) {
10531055
})
10541056
}
10551057

1058+
func TestWorkspaceAgentContainers(t *testing.T) {
1059+
t.Parallel()
1060+
1061+
pool, err := dockertest.NewPool("")
1062+
require.NoError(t, err, "Could not connect to docker")
1063+
testLabelValue := uuid.New().String()
1064+
ct, err := pool.RunWithOptions(&dockertest.RunOptions{
1065+
Repository: "busybox",
1066+
Tag: "latest",
1067+
Cmd: []string{"sleep", "infnity"},
1068+
Labels: map[string]string{"com.coder.test": testLabelValue},
1069+
}, func(config *docker.HostConfig) {
1070+
config.AutoRemove = true
1071+
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
1072+
})
1073+
require.NoError(t, err, "Could not start test docker container")
1074+
t.Cleanup(func() {
1075+
assert.NoError(t, pool.Purge(ct), "Could not purge resource")
1076+
})
1077+
1078+
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{})
1079+
1080+
user := coderdtest.CreateFirstUser(t, client)
1081+
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
1082+
OrganizationID: user.OrganizationID,
1083+
OwnerID: user.UserID,
1084+
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
1085+
return agents
1086+
}).Do()
1087+
_ = agenttest.New(t, client.URL, r.AgentToken, func(_ *agent.Options) {})
1088+
resources := coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).Wait()
1089+
require.Len(t, resources, 1, "expected one resource")
1090+
require.Len(t, resources[0].Agents, 1, "expected one agent")
1091+
agentID := resources[0].Agents[0].ID
1092+
1093+
ctx := testutil.Context(t, testutil.WaitLong)
1094+
res, err := client.WorkspaceAgentListContainers(ctx, agentID)
1095+
require.NoError(t, err, "failed to list containers")
1096+
require.NotEmpty(t, res.Containers, "expected to find containers")
1097+
1098+
var found bool
1099+
for _, c := range res.Containers {
1100+
if c.ID == ct.Container.ID {
1101+
found = true
1102+
assert.Equal(t, ct.Container.ID, c.ID)
1103+
assert.Equal(t, "busybox:latest", c.Image)
1104+
// The container name is prefixed with a slash.
1105+
assert.Equal(t, strings.TrimPrefix(ct.Container.Name, "/"), c.FriendlyName)
1106+
assert.Equal(t, ct.Container.Config.Labels, c.Labels)
1107+
break
1108+
}
1109+
}
1110+
require.True(t, found, "expected to find container")
1111+
}
1112+
10561113
func TestWorkspaceAgentAppHealth(t *testing.T) {
10571114
t.Parallel()
10581115
client, db := coderdtest.NewWithDatabase(t, nil)

codersdk/workspaceagents.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,10 @@ type WorkspaceAgentContainer struct {
405405
Labels map[string]string `json:"labels"`
406406
}
407407

408+
// WorkspaceAgentListContainersResponse is the response to the list containers
409+
// request.
408410
type WorkspaceAgentListContainersResponse struct {
409411
Containers []WorkspaceAgentContainer `json:"containers"`
410-
Mtime time.Time `json:"mtime"`
411412
}
412413

413414
// WorkspaceAgentListContainers returns a list of containers that are currently
@@ -421,9 +422,9 @@ func (c *Client) WorkspaceAgentListContainers(ctx context.Context, agentID uuid.
421422
if res.StatusCode != http.StatusOK {
422423
return WorkspaceAgentListContainersResponse{}, ReadBodyAsError(res)
423424
}
424-
var resp WorkspaceAgentListContainersResponse
425+
var cr WorkspaceAgentListContainersResponse
425426

426-
return resp, json.NewDecoder(res.Body).Decode(&resp.Containers)
427+
return cr, json.NewDecoder(res.Body).Decode(&cr)
427428
}
428429

429430
//nolint:revive // Follow is a control flag on the server as well.

codersdk/workspacesdk/agentconn.go

+16
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,22 @@ func (c *AgentConn) PrometheusMetrics(ctx context.Context) ([]byte, error) {
336336
return bs, nil
337337
}
338338

339+
// ListContainers returns a response from the agent's containers endpoint
340+
func (c *AgentConn) ListContainers(ctx context.Context) (codersdk.WorkspaceAgentListContainersResponse, error) {
341+
ctx, span := tracing.StartSpan(ctx)
342+
defer span.End()
343+
res, err := c.apiRequest(ctx, http.MethodGet, "/api/v0/containers", nil)
344+
if err != nil {
345+
return codersdk.WorkspaceAgentListContainersResponse{}, xerrors.Errorf("do request: %w", err)
346+
}
347+
defer res.Body.Close()
348+
if res.StatusCode != http.StatusOK {
349+
return codersdk.WorkspaceAgentListContainersResponse{}, codersdk.ReadBodyAsError(res)
350+
}
351+
var resp codersdk.WorkspaceAgentListContainersResponse
352+
return resp, json.NewDecoder(res.Body).Decode(&resp.Containers)
353+
}
354+
339355
// apiRequest makes a request to the workspace agent's HTTP API server.
340356
func (c *AgentConn) apiRequest(ctx context.Context, method, path string, body io.Reader) (*http.Response, error) {
341357
ctx, span := tracing.StartSpan(ctx)

0 commit comments

Comments
 (0)