feat: add workspace restart functionality to API (#25757)

This models restart as durable orchestration of existing stop and
start workspace builds instead of adding a new restart transition.
Keeping restart as two existing transitions preserves the current
build/provisioner model.

The child start build is created only after the parent stop build
succeeds, rather than being inserted immediately in a pending
state. That keeps `workspace_builds` aligned with actual
provisioner-ready work and avoids introducing a second
pending-build lifecycle that the provisioner and build acquisition
paths would need to understand.

Refs: https://linear.app/codercom/issue/PLAT-143
This commit is contained in:
George K
2026-07-07 09:18:30 -07:00
committed by GitHub
parent ba094c5706
commit 6af0f4d698
45 changed files with 4412 additions and 144 deletions
+11
View File
@@ -457,6 +457,16 @@ var (
Type: "workspace_agent_resource_monitor",
}
// ResourceWorkspaceBuildOrchestration
// Valid Actions
// - "ActionCreate" :: create a workspace build orchestration
// - "ActionDelete" :: delete a workspace build orchestration
// - "ActionRead" :: read a workspace build orchestration
// - "ActionUpdate" :: update a workspace build orchestration
ResourceWorkspaceBuildOrchestration = Object{
Type: "workspace_build_orchestration",
}
// ResourceWorkspaceDormant
// Valid Actions
// - "ActionApplicationConnect" :: connect to workspace apps via browser
@@ -536,6 +546,7 @@ func AllResources() []Objecter {
ResourceWorkspace,
ResourceWorkspaceAgentDevcontainers,
ResourceWorkspaceAgentResourceMonitor,
ResourceWorkspaceBuildOrchestration,
ResourceWorkspaceDormant,
ResourceWorkspaceProxy,
}
+8
View File
@@ -140,6 +140,14 @@ var RBACPermissions = map[string]PermissionDefinition{
ActionRead: "read and use a workspace proxy",
},
},
"workspace_build_orchestration": {
Actions: map[Action]ActionDefinition{
ActionCreate: "create a workspace build orchestration",
ActionRead: "read a workspace build orchestration",
ActionUpdate: "update a workspace build orchestration",
ActionDelete: "delete a workspace build orchestration",
},
},
"license": {
Actions: map[Action]ActionDefinition{
ActionCreate: "create a license",
+1 -1
View File
@@ -585,7 +585,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
// Org admins should not have workspace exec perms.
organizationID.String(): {
Org: append(
allPermsExcept(ResourceWorkspace, ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceAssignRole, ResourceUserSecret, ResourceBoundaryUsage, ResourceBoundaryLog, ResourceAiSeat),
allPermsExcept(ResourceWorkspace, ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceAssignRole, ResourceUserSecret, ResourceBoundaryUsage, ResourceBoundaryLog, ResourceAiSeat, ResourceWorkspaceBuildOrchestration),
Permissions(map[string][]policy.Action{
ResourceWorkspace.Type: slice.Omit(ResourceWorkspace.AvailableActions(), policy.ActionApplicationConnect, policy.ActionSSH),
ResourceWorkspaceDormant.Type: {policy.ActionRead, policy.ActionDelete, policy.ActionCreate, policy.ActionUpdate, policy.ActionWorkspaceStop, policy.ActionCreateAgent, policy.ActionDeleteAgent, policy.ActionUpdateAgent},
+9
View File
@@ -987,6 +987,15 @@ func TestRolePermissions(t *testing.T) {
false: {},
},
},
{
Name: "WorkspaceBuildOrchestration",
Actions: crud,
Resource: rbac.ResourceWorkspaceBuildOrchestration.InOrg(orgID),
AuthorizeMap: map[bool][]hasAuthSubjects{
true: {owner},
false: {setOrgNotMe, setOtherOrg, memberMe, agentsAccessUser, templateAdmin, userAdmin, orgWorkspaceAccessUser},
},
},
{
// Any owner/admin across may access any users' preferences
// Members may not access other members' preferences
+12
View File
@@ -164,6 +164,10 @@ const (
ScopeWorkspaceAgentResourceMonitorCreate ScopeName = "workspace_agent_resource_monitor:create"
ScopeWorkspaceAgentResourceMonitorRead ScopeName = "workspace_agent_resource_monitor:read"
ScopeWorkspaceAgentResourceMonitorUpdate ScopeName = "workspace_agent_resource_monitor:update"
ScopeWorkspaceBuildOrchestrationCreate ScopeName = "workspace_build_orchestration:create"
ScopeWorkspaceBuildOrchestrationDelete ScopeName = "workspace_build_orchestration:delete"
ScopeWorkspaceBuildOrchestrationRead ScopeName = "workspace_build_orchestration:read"
ScopeWorkspaceBuildOrchestrationUpdate ScopeName = "workspace_build_orchestration:update"
ScopeWorkspaceDormantApplicationConnect ScopeName = "workspace_dormant:application_connect"
ScopeWorkspaceDormantCreate ScopeName = "workspace_dormant:create"
ScopeWorkspaceDormantCreateAgent ScopeName = "workspace_dormant:create_agent"
@@ -348,6 +352,10 @@ func (e ScopeName) Valid() bool {
ScopeWorkspaceAgentResourceMonitorCreate,
ScopeWorkspaceAgentResourceMonitorRead,
ScopeWorkspaceAgentResourceMonitorUpdate,
ScopeWorkspaceBuildOrchestrationCreate,
ScopeWorkspaceBuildOrchestrationDelete,
ScopeWorkspaceBuildOrchestrationRead,
ScopeWorkspaceBuildOrchestrationUpdate,
ScopeWorkspaceDormantApplicationConnect,
ScopeWorkspaceDormantCreate,
ScopeWorkspaceDormantCreateAgent,
@@ -533,6 +541,10 @@ func AllScopeNameValues() []ScopeName {
ScopeWorkspaceAgentResourceMonitorCreate,
ScopeWorkspaceAgentResourceMonitorRead,
ScopeWorkspaceAgentResourceMonitorUpdate,
ScopeWorkspaceBuildOrchestrationCreate,
ScopeWorkspaceBuildOrchestrationDelete,
ScopeWorkspaceBuildOrchestrationRead,
ScopeWorkspaceBuildOrchestrationUpdate,
ScopeWorkspaceDormantApplicationConnect,
ScopeWorkspaceDormantCreate,
ScopeWorkspaceDormantCreateAgent,