mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: support multiple agents with shared instance-identity auth (#24325)
> This PR was authored by Mux on behalf of Mike. ## Summary Adds support for multiple peer root workspace agents sharing the same `auth_instance_id`, so AWS, Azure, and GCP instance-identity auth can issue the correct session token for a selected agent instead of assuming a single root agent per instance. ## Problem When a Terraform template attaches two or more `coder_agent` resources (with `auth = "aws-instance-identity"`) to a single compute instance, every agent shares the same cloud instance ID. The existing singular lookup picks whichever agent was created most recently, silently ignoring the others. ## Solution Introduce an optional pre-auth agent selector (`CODER_AGENT_NAME`) and make the server-side lookup ambiguity-aware. **Database layer:** - `GetWorkspaceAgentsByInstanceID` (`:many`): returns all matching root agents for an instance ID. - `GetWorkspaceAgentByInstanceIDAndName` (`:one`): returns the named root agent for disambiguation. **SDK and CLI:** - `agent_name` field added to AWS, Azure, and GCP request structs (`omitempty` for backward compatibility). - `CODER_AGENT_NAME` env var and `--agent-name` flag wired into the agent bootstrap before instance-identity auth runs. **Server handler (`handleAuthInstanceID`):** - When `agent_name` is present: direct lookup by (instance ID, name). - When absent: legacy lookup, then resource-scoped ambiguity check. Returns 409 with available agent names if multiple root agents match. - Whitespace-only names are trimmed and treated as unspecified. - Sub-agents remain excluded (`parent_id IS NULL` filter). **Verification template:** - `examples/templates/aws-multi-agent/` provisions one EC2 instance with two agents (`main` and `dev`), both using instance-identity auth with `CODER_AGENT_NAME` set in the cloud-init user data. ## Backward compatibility Existing single-agent deployments work unchanged. The `agent_name` field is optional with `omitempty`, and the unnamed path preserves today's behavior when only one root agent matches.
This commit is contained in:
+17
-3
@@ -86,6 +86,7 @@ const (
|
||||
envAgentTokenFile = "CODER_AGENT_TOKEN_FILE"
|
||||
envAgentURL = "CODER_AGENT_URL"
|
||||
envAgentAuth = "CODER_AGENT_AUTH"
|
||||
envAgentName = "CODER_AGENT_NAME"
|
||||
envURL = "CODER_URL"
|
||||
)
|
||||
|
||||
@@ -789,6 +790,7 @@ type AgentAuth struct {
|
||||
agentTokenFile string
|
||||
agentURL url.URL
|
||||
agentAuth string
|
||||
agentName string
|
||||
}
|
||||
|
||||
func (a *AgentAuth) AttachOptions(cmd *serpent.Command, hidden bool) {
|
||||
@@ -821,6 +823,13 @@ func (a *AgentAuth) AttachOptions(cmd *serpent.Command, hidden bool) {
|
||||
Default: "token",
|
||||
Value: serpent.StringOf(&a.agentAuth),
|
||||
Hidden: hidden,
|
||||
}, serpent.Option{
|
||||
Name: "Agent Name",
|
||||
Description: "The name of the agent to authenticate as (only applicable for instance identity).",
|
||||
Flag: "agent-name",
|
||||
Env: envAgentName,
|
||||
Value: serpent.StringOf(&a.agentName),
|
||||
Hidden: hidden,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -832,6 +841,11 @@ func (a *AgentAuth) CreateClient() (*agentsdk.Client, error) {
|
||||
return nil, xerrors.Errorf("%s must be set", envAgentURL)
|
||||
}
|
||||
|
||||
var iiOpts []agentsdk.InstanceIdentityOption
|
||||
if a.agentName != "" {
|
||||
iiOpts = append(iiOpts, agentsdk.WithInstanceIdentityAgentName(a.agentName))
|
||||
}
|
||||
|
||||
switch a.agentAuth {
|
||||
case "token":
|
||||
token := a.agentToken
|
||||
@@ -850,11 +864,11 @@ func (a *AgentAuth) CreateClient() (*agentsdk.Client, error) {
|
||||
}
|
||||
return agentsdk.New(&a.agentURL, agentsdk.WithFixedToken(token)), nil
|
||||
case "google-instance-identity":
|
||||
return agentsdk.New(&a.agentURL, agentsdk.WithGoogleInstanceIdentity("", nil)), nil
|
||||
return agentsdk.New(&a.agentURL, agentsdk.WithGoogleInstanceIdentity("", nil, iiOpts...)), nil
|
||||
case "aws-instance-identity":
|
||||
return agentsdk.New(&a.agentURL, agentsdk.WithAWSInstanceIdentity()), nil
|
||||
return agentsdk.New(&a.agentURL, agentsdk.WithAWSInstanceIdentity(iiOpts...)), nil
|
||||
case "azure-instance-identity":
|
||||
return agentsdk.New(&a.agentURL, agentsdk.WithAzureInstanceIdentity()), nil
|
||||
return agentsdk.New(&a.agentURL, agentsdk.WithAzureInstanceIdentity(iiOpts...)), nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("unknown agent auth type: %s", a.agentAuth)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user