mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
feat: show devcontainer dirty status and allow recreate (#17880)
Updates #16424
This commit is contained in:
committed by
GitHub
parent
c775ea8411
commit
98e2ec4417
@ -8,6 +8,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -36,6 +37,7 @@ import (
|
||||
"github.com/coder/coder/v2/agent"
|
||||
"github.com/coder/coder/v2/agent/agentcontainers"
|
||||
"github.com/coder/coder/v2/agent/agentcontainers/acmock"
|
||||
"github.com/coder/coder/v2/agent/agentcontainers/watcher"
|
||||
"github.com/coder/coder/v2/agent/agenttest"
|
||||
agentproto "github.com/coder/coder/v2/agent/proto"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
@ -1347,6 +1349,115 @@ func TestWorkspaceAgentContainers(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceAgentRecreateDevcontainer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Mock", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
workspaceFolder = t.TempDir()
|
||||
configFile = filepath.Join(workspaceFolder, ".devcontainer", "devcontainer.json")
|
||||
dcLabels = map[string]string{
|
||||
agentcontainers.DevcontainerLocalFolderLabel: workspaceFolder,
|
||||
agentcontainers.DevcontainerConfigFileLabel: configFile,
|
||||
}
|
||||
devContainer = codersdk.WorkspaceAgentContainer{
|
||||
ID: uuid.NewString(),
|
||||
CreatedAt: dbtime.Now(),
|
||||
FriendlyName: testutil.GetRandomName(t),
|
||||
Image: "busybox:latest",
|
||||
Labels: dcLabels,
|
||||
Running: true,
|
||||
Status: "running",
|
||||
DevcontainerDirty: true,
|
||||
}
|
||||
plainContainer = codersdk.WorkspaceAgentContainer{
|
||||
ID: uuid.NewString(),
|
||||
CreatedAt: dbtime.Now(),
|
||||
FriendlyName: testutil.GetRandomName(t),
|
||||
Image: "busybox:latest",
|
||||
Labels: map[string]string{},
|
||||
Running: true,
|
||||
Status: "running",
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
setupMock func(*acmock.MockLister, *acmock.MockDevcontainerCLI) (status int)
|
||||
}{
|
||||
{
|
||||
name: "Recreate",
|
||||
setupMock: func(mcl *acmock.MockLister, mdccli *acmock.MockDevcontainerCLI) int {
|
||||
mcl.EXPECT().List(gomock.Any()).Return(codersdk.WorkspaceAgentListContainersResponse{
|
||||
Containers: []codersdk.WorkspaceAgentContainer{devContainer},
|
||||
}, nil).Times(1)
|
||||
mdccli.EXPECT().Up(gomock.Any(), workspaceFolder, configFile, gomock.Any()).Return("someid", nil).Times(1)
|
||||
return 0
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Container does not exist",
|
||||
setupMock: func(mcl *acmock.MockLister, mdccli *acmock.MockDevcontainerCLI) int {
|
||||
mcl.EXPECT().List(gomock.Any()).Return(codersdk.WorkspaceAgentListContainersResponse{}, nil).Times(1)
|
||||
return http.StatusNotFound
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Not a devcontainer",
|
||||
setupMock: func(mcl *acmock.MockLister, mdccli *acmock.MockDevcontainerCLI) int {
|
||||
mcl.EXPECT().List(gomock.Any()).Return(codersdk.WorkspaceAgentListContainersResponse{
|
||||
Containers: []codersdk.WorkspaceAgentContainer{plainContainer},
|
||||
}, nil).Times(1)
|
||||
return http.StatusNotFound
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
mcl := acmock.NewMockLister(ctrl)
|
||||
mdccli := acmock.NewMockDevcontainerCLI(ctrl)
|
||||
wantStatus := tc.setupMock(mcl, mdccli)
|
||||
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
|
||||
return agents
|
||||
}).Do()
|
||||
_ = agenttest.New(t, client.URL, r.AgentToken, func(o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(
|
||||
o.ContainerAPIOptions,
|
||||
agentcontainers.WithLister(mcl),
|
||||
agentcontainers.WithDevcontainerCLI(mdccli),
|
||||
agentcontainers.WithWatcher(watcher.NewNoop()),
|
||||
)
|
||||
})
|
||||
resources := coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID).Wait()
|
||||
require.Len(t, resources, 1, "expected one resource")
|
||||
require.Len(t, resources[0].Agents, 1, "expected one agent")
|
||||
agentID := resources[0].Agents[0].ID
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
err := client.WorkspaceAgentRecreateDevcontainer(ctx, agentID, devContainer.ID)
|
||||
if wantStatus > 0 {
|
||||
cerr, ok := codersdk.AsError(err)
|
||||
require.True(t, ok, "expected error to be a coder error")
|
||||
assert.Equal(t, wantStatus, cerr.StatusCode())
|
||||
} else {
|
||||
require.NoError(t, err, "failed to recreate devcontainer")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceAgentAppHealth(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, db := coderdtest.NewWithDatabase(t, nil)
|
||||
|
Reference in New Issue
Block a user