feat(coderd): add support for external agents to API's and provisioner (#19286)

This pull request introduces support for external workspace management, allowing users to register and manage workspaces that are provisioned and managed outside of the Coder.

Depends on: https://github.com/coder/terraform-provider-coder/pull/424

* GET /api/v2/init-script - Gets the agent initialization script
  * By default, it returns a script for Linux (amd64), but with query parameters (os and arch) you can get the init script for different platforms
* GET /api/v2/workspaces/{workspace}/external-agent/{agent}/credentials - Gets credentials for an external agent **(enterprise)**
* Updated queries to filter workspaces/templates by the has_external_agent field
This commit is contained in:
Kacper Sawicki
2025-08-19 10:41:33 +02:00
committed by GitHub
parent f085c37af3
commit 9edceef0bf
53 changed files with 1616 additions and 98 deletions
+4 -1
View File
@@ -88,7 +88,8 @@ const (
// ManagedAgentLimit is a usage period feature, so the value in the license
// contains both a soft and hard limit. Refer to
// enterprise/coderd/license/license.go for the license format.
FeatureManagedAgentLimit FeatureName = "managed_agent_limit"
FeatureManagedAgentLimit FeatureName = "managed_agent_limit"
FeatureWorkspaceExternalAgent FeatureName = "workspace_external_agent"
)
var (
@@ -115,6 +116,7 @@ var (
FeatureMultipleOrganizations,
FeatureWorkspacePrebuilds,
FeatureManagedAgentLimit,
FeatureWorkspaceExternalAgent,
}
// FeatureNamesMap is a map of all feature names for quick lookups.
@@ -155,6 +157,7 @@ func (n FeatureName) AlwaysEnable() bool {
FeatureCustomRoles: true,
FeatureMultipleOrganizations: true,
FeatureWorkspacePrebuilds: true,
FeatureWorkspaceExternalAgent: true,
}[n]
}
+28
View File
@@ -0,0 +1,28 @@
package codersdk
import (
"context"
"fmt"
"io"
"net/http"
)
func (c *Client) InitScript(ctx context.Context, os, arch string) (string, error) {
url := fmt.Sprintf("/api/v2/init-script/%s/%s", os, arch)
res, err := c.Request(ctx, http.MethodGet, url, nil)
if err != nil {
return "", err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return "", ReadBodyAsError(res)
}
script, err := io.ReadAll(res.Body)
if err != nil {
return "", err
}
return string(script), nil
}
+2
View File
@@ -33,6 +33,8 @@ type TemplateVersion struct {
Warnings []TemplateVersionWarning `json:"warnings,omitempty" enums:"DEPRECATED_PARAMETERS"`
MatchedProvisioners *MatchedProvisioners `json:"matched_provisioners,omitempty"`
HasExternalAgent bool `json:"has_external_agent"`
}
type TemplateVersionExternalAuth struct {
+1
View File
@@ -90,6 +90,7 @@ type WorkspaceBuild struct {
TemplateVersionPresetID *uuid.UUID `json:"template_version_preset_id" format:"uuid"`
HasAITask *bool `json:"has_ai_task,omitempty"`
AITaskSidebarAppID *uuid.UUID `json:"ai_task_sidebar_app_id,omitempty" format:"uuid"`
HasExternalAgent *bool `json:"has_external_agent,omitempty"`
}
// WorkspaceResource describes resources used to create a workspace, for instance:
+20
View File
@@ -689,3 +689,23 @@ func (c *Client) UpdateWorkspaceACL(ctx context.Context, workspaceID uuid.UUID,
}
return nil
}
// ExternalAgentCredentials contains the credentials needed for an external agent to connect to Coder.
type ExternalAgentCredentials struct {
Command string `json:"command"`
AgentToken string `json:"agent_token"`
}
func (c *Client) WorkspaceExternalAgentCredentials(ctx context.Context, workspaceID uuid.UUID, agentName string) (ExternalAgentCredentials, error) {
path := fmt.Sprintf("/api/v2/workspaces/%s/external-agent/%s/credentials", workspaceID.String(), agentName)
res, err := c.Request(ctx, http.MethodGet, path, nil)
if err != nil {
return ExternalAgentCredentials{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return ExternalAgentCredentials{}, ReadBodyAsError(res)
}
var credentials ExternalAgentCredentials
return credentials, json.NewDecoder(res.Body).Decode(&credentials)
}