Skip to content

Commit aa63880

Browse files
committed
move /external-agent/{agent}/credentials endpoint to enterprise
1 parent 4474448 commit aa63880

File tree

13 files changed

+214
-187
lines changed

13 files changed

+214
-187
lines changed

coderd/apidoc/docs.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1421,9 +1421,6 @@ func New(options *Options) *API {
14211421
r.Post("/", api.postWorkspaceAgentPortShare)
14221422
r.Delete("/", api.deleteWorkspaceAgentPortShare)
14231423
})
1424-
r.Route("/external-agent", func(r chi.Router) {
1425-
r.Get("/{agent}/credentials", api.workspaceExternalAgentCredentials)
1426-
})
14271424
r.Get("/timings", api.workspaceTimings)
14281425
r.Route("/acl", func(r chi.Router) {
14291426
r.Use(

coderd/workspaceagents.go

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,71 +2185,3 @@ func convertWorkspaceAgentLog(logEntry database.WorkspaceAgentLog) codersdk.Work
21852185
SourceID: logEntry.LogSourceID,
21862186
}
21872187
}
2188-
2189-
// @Summary Get workspace external agent credentials
2190-
// @ID get-workspace-external-agent-credentials
2191-
// @Security CoderSessionToken
2192-
// @Produce json
2193-
// @Tags Agents
2194-
// @Param workspace path string true "Workspace ID" format(uuid)
2195-
// @Param agent path string true "Agent name"
2196-
// @Success 200 {object} codersdk.ExternalAgentCredentials
2197-
// @Router /workspaces/{workspace}/external-agent/{agent}/credentials [get]
2198-
func (api *API) workspaceExternalAgentCredentials(rw http.ResponseWriter, r *http.Request) {
2199-
ctx := r.Context()
2200-
workspace := httpmw.WorkspaceParam(r)
2201-
agentName := chi.URLParam(r, "agent")
2202-
2203-
build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
2204-
if err != nil {
2205-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
2206-
Message: "Failed to get latest workspace build.",
2207-
Detail: err.Error(),
2208-
})
2209-
return
2210-
}
2211-
2212-
agents, err := api.Database.GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx, database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams{
2213-
WorkspaceID: workspace.ID,
2214-
BuildNumber: build.BuildNumber,
2215-
})
2216-
if err != nil {
2217-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
2218-
Message: "Failed to get workspace agents.",
2219-
Detail: err.Error(),
2220-
})
2221-
return
2222-
}
2223-
2224-
var agent *database.WorkspaceAgent
2225-
for i := range agents {
2226-
if agents[i].Name == agentName {
2227-
agent = &agents[i]
2228-
break
2229-
}
2230-
}
2231-
if agent == nil {
2232-
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
2233-
Message: fmt.Sprintf("External agent '%s' not found in workspace.", agentName),
2234-
})
2235-
return
2236-
}
2237-
2238-
if agent.AuthInstanceID.Valid {
2239-
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
2240-
Message: "External agent is authenticated with an instance ID.",
2241-
})
2242-
return
2243-
}
2244-
2245-
initScriptURL := fmt.Sprintf("%s/api/v2/init-script/%s/%s", api.AccessURL.String(), agent.OperatingSystem, agent.Architecture)
2246-
command := fmt.Sprintf("CODER_AGENT_TOKEN=%q curl -fsSL %q | sh", agent.AuthToken.String(), initScriptURL)
2247-
if agent.OperatingSystem == "windows" {
2248-
command = fmt.Sprintf("$env:CODER_AGENT_TOKEN=%q; iwr -useb %q | iex", agent.AuthToken.String(), initScriptURL)
2249-
}
2250-
2251-
httpapi.Write(ctx, rw, http.StatusOK, codersdk.ExternalAgentCredentials{
2252-
AgentToken: agent.AuthToken.String(),
2253-
Command: command,
2254-
})
2255-
}

coderd/workspaceagents_test.go

Lines changed: 0 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3056,77 +3056,3 @@ func (p *pubsubReinitSpy) Subscribe(event string, listener pubsub.Listener) (can
30563056
p.Unlock()
30573057
return cancel, err
30583058
}
3059-
3060-
func TestWorkspaceExternalAgentCredentials(t *testing.T) {
3061-
t.Parallel()
3062-
client, db := coderdtest.NewWithDatabase(t, nil)
3063-
user := coderdtest.CreateFirstUser(t, client)
3064-
3065-
t.Run("Success - linux", func(t *testing.T) {
3066-
t.Parallel()
3067-
ctx := testutil.Context(t, testutil.WaitShort)
3068-
3069-
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
3070-
OrganizationID: user.OrganizationID,
3071-
OwnerID: user.UserID,
3072-
}).WithAgent(func(a []*proto.Agent) []*proto.Agent {
3073-
a[0].Name = "test-agent"
3074-
a[0].OperatingSystem = "linux"
3075-
a[0].Architecture = "amd64"
3076-
return a
3077-
}).Do()
3078-
3079-
credentials, err := client.WorkspaceExternalAgentCredentials(
3080-
ctx, r.Workspace.ID, "test-agent")
3081-
require.NoError(t, err)
3082-
3083-
require.Equal(t, r.AgentToken, credentials.AgentToken)
3084-
expectedCommand := fmt.Sprintf("CODER_AGENT_TOKEN=%q curl -fsSL \"%s/api/v2/init-script/linux/amd64\" | sh", r.AgentToken, client.URL)
3085-
require.Equal(t, expectedCommand, credentials.Command)
3086-
})
3087-
3088-
t.Run("Success - windows", func(t *testing.T) {
3089-
t.Parallel()
3090-
ctx := testutil.Context(t, testutil.WaitShort)
3091-
3092-
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
3093-
OrganizationID: user.OrganizationID,
3094-
OwnerID: user.UserID,
3095-
}).WithAgent(func(a []*proto.Agent) []*proto.Agent {
3096-
a[0].Name = "test-agent"
3097-
a[0].OperatingSystem = "windows"
3098-
a[0].Architecture = "amd64"
3099-
return a
3100-
}).Do()
3101-
3102-
credentials, err := client.WorkspaceExternalAgentCredentials(
3103-
ctx, r.Workspace.ID, "test-agent")
3104-
require.NoError(t, err)
3105-
3106-
require.Equal(t, r.AgentToken, credentials.AgentToken)
3107-
expectedCommand := fmt.Sprintf("$env:CODER_AGENT_TOKEN=%q; iwr -useb \"%s/api/v2/init-script/windows/amd64\" | iex", r.AgentToken, client.URL)
3108-
require.Equal(t, expectedCommand, credentials.Command)
3109-
})
3110-
3111-
t.Run("WithInstanceID - should return 404", func(t *testing.T) {
3112-
t.Parallel()
3113-
ctx := testutil.Context(t, testutil.WaitShort)
3114-
3115-
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
3116-
OrganizationID: user.OrganizationID,
3117-
OwnerID: user.UserID,
3118-
}).WithAgent(func(a []*proto.Agent) []*proto.Agent {
3119-
a[0].Name = "test-agent"
3120-
a[0].Auth = &proto.Agent_InstanceId{
3121-
InstanceId: uuid.New().String(),
3122-
}
3123-
return a
3124-
}).Do()
3125-
3126-
_, err := client.WorkspaceExternalAgentCredentials(ctx, r.Workspace.ID, "test-agent")
3127-
require.Error(t, err)
3128-
var apiErr *codersdk.Error
3129-
require.ErrorAs(t, err, &apiErr)
3130-
require.Equal(t, "External agent is authenticated with an instance ID.", apiErr.Message)
3131-
})
3132-
}

codersdk/deployment.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ const (
8888
// ManagedAgentLimit is a usage period feature, so the value in the license
8989
// contains both a soft and hard limit. Refer to
9090
// enterprise/coderd/license/license.go for the license format.
91-
FeatureManagedAgentLimit FeatureName = "managed_agent_limit"
91+
FeatureManagedAgentLimit FeatureName = "managed_agent_limit"
92+
FeatureWorkspaceExternalAgent FeatureName = "workspace_external_agent"
9293
)
9394

9495
var (
@@ -115,6 +116,7 @@ var (
115116
FeatureMultipleOrganizations,
116117
FeatureWorkspacePrebuilds,
117118
FeatureManagedAgentLimit,
119+
FeatureWorkspaceExternalAgent,
118120
}
119121

120122
// FeatureNamesMap is a map of all feature names for quick lookups.
@@ -155,6 +157,7 @@ func (n FeatureName) AlwaysEnable() bool {
155157
FeatureCustomRoles: true,
156158
FeatureMultipleOrganizations: true,
157159
FeatureWorkspacePrebuilds: true,
160+
FeatureWorkspaceExternalAgent: true,
158161
}[n]
159162
}
160163

docs/reference/api/agents.md

Lines changed: 0 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/api/enterprise.md

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enterprise/coderd/coderd.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,15 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
506506
apiKeyMiddleware,
507507
httpmw.ExtractNotificationTemplateParam(options.Database),
508508
).Put("/notifications/templates/{notification_template}/method", api.updateNotificationTemplateMethod)
509+
510+
r.Route("/workspaces/{workspace}/external-agent", func(r chi.Router) {
511+
r.Use(
512+
apiKeyMiddleware,
513+
httpmw.ExtractWorkspaceParam(options.Database),
514+
api.RequireFeatureMW(codersdk.FeatureWorkspaceExternalAgent),
515+
)
516+
r.Get("/{agent}/credentials", api.workspaceExternalAgentCredentials)
517+
})
509518
})
510519

511520
if len(options.SCIMAPIKey) != 0 {

enterprise/coderd/workspaceagents.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ package coderd
22

33
import (
44
"context"
5+
"fmt"
56
"net/http"
67

8+
"github.com/go-chi/chi/v5"
9+
10+
"github.com/coder/coder/v2/coderd/database"
711
"github.com/coder/coder/v2/coderd/httpapi"
12+
"github.com/coder/coder/v2/coderd/httpmw"
813
"github.com/coder/coder/v2/codersdk"
914
)
1015

@@ -17,3 +22,71 @@ func (api *API) shouldBlockNonBrowserConnections(rw http.ResponseWriter) bool {
1722
}
1823
return false
1924
}
25+
26+
// @Summary Get workspace external agent credentials
27+
// @ID get-workspace-external-agent-credentials
28+
// @Security CoderSessionToken
29+
// @Produce json
30+
// @Tags Enterprise
31+
// @Param workspace path string true "Workspace ID" format(uuid)
32+
// @Param agent path string true "Agent name"
33+
// @Success 200 {object} codersdk.ExternalAgentCredentials
34+
// @Router /workspaces/{workspace}/external-agent/{agent}/credentials [get]
35+
func (api *API) workspaceExternalAgentCredentials(rw http.ResponseWriter, r *http.Request) {
36+
ctx := r.Context()
37+
workspace := httpmw.WorkspaceParam(r)
38+
agentName := chi.URLParam(r, "agent")
39+
40+
build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
41+
if err != nil {
42+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
43+
Message: "Failed to get latest workspace build.",
44+
Detail: err.Error(),
45+
})
46+
return
47+
}
48+
49+
agents, err := api.Database.GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx, database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams{
50+
WorkspaceID: workspace.ID,
51+
BuildNumber: build.BuildNumber,
52+
})
53+
if err != nil {
54+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
55+
Message: "Failed to get workspace agents.",
56+
Detail: err.Error(),
57+
})
58+
return
59+
}
60+
61+
var agent *database.WorkspaceAgent
62+
for i := range agents {
63+
if agents[i].Name == agentName {
64+
agent = &agents[i]
65+
break
66+
}
67+
}
68+
if agent == nil {
69+
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
70+
Message: fmt.Sprintf("External agent '%s' not found in workspace.", agentName),
71+
})
72+
return
73+
}
74+
75+
if agent.AuthInstanceID.Valid {
76+
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
77+
Message: "External agent is authenticated with an instance ID.",
78+
})
79+
return
80+
}
81+
82+
initScriptURL := fmt.Sprintf("%s/api/v2/init-script/%s/%s", api.AccessURL.String(), agent.OperatingSystem, agent.Architecture)
83+
command := fmt.Sprintf("CODER_AGENT_TOKEN=%q curl -fsSL %q | sh", agent.AuthToken.String(), initScriptURL)
84+
if agent.OperatingSystem == "windows" {
85+
command = fmt.Sprintf("$env:CODER_AGENT_TOKEN=%q; iwr -useb %q | iex", agent.AuthToken.String(), initScriptURL)
86+
}
87+
88+
httpapi.Write(ctx, rw, http.StatusOK, codersdk.ExternalAgentCredentials{
89+
AgentToken: agent.AuthToken.String(),
90+
Command: command,
91+
})
92+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy