Skip to content

Commit b877de0

Browse files
committed
feat: add support for external agents to API's and provisioner
1 parent 9e885fe commit b877de0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1315
-54
lines changed

cli/testdata/coder_list_--output_json.golden

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@
7070
"most_recently_seen": null
7171
},
7272
"template_version_preset_id": null,
73-
"has_ai_task": false
73+
"has_ai_task": false,
74+
"has_external_agent": false
7475
},
7576
"latest_app_status": null,
7677
"outdated": false,

cli/testdata/coder_provisioner_list_--output_json.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"last_seen_at": "====[timestamp]=====",
88
"name": "test-daemon",
99
"version": "v0.0.0-devel",
10-
"api_version": "1.8",
10+
"api_version": "1.9",
1111
"provisioners": [
1212
"echo"
1313
],

coderd/apidoc/docs.go

Lines changed: 90 additions & 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: 82 additions & 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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,9 @@ func New(options *Options) *API {
14301430
r.Post("/", api.postWorkspaceAgentPortShare)
14311431
r.Delete("/", api.deleteWorkspaceAgentPortShare)
14321432
})
1433+
r.Route("/external-agent", func(r chi.Router) {
1434+
r.Get("/{agent}/credentials", api.workspaceExternalAgentCredentials)
1435+
})
14331436
r.Get("/timings", api.workspaceTimings)
14341437
r.Route("/acl", func(r chi.Router) {
14351438
r.Use(
@@ -1566,6 +1569,9 @@ func New(options *Options) *API {
15661569
r.Use(apiKeyMiddleware)
15671570
r.Get("/", api.tailnetRPCConn)
15681571
})
1572+
r.Route("/init-script", func(r chi.Router) {
1573+
r.Get("/{os}/{arch}", api.initScript)
1574+
})
15691575
})
15701576

15711577
if options.SwaggerEndpoint {

coderd/coderdtest/swaggerparser.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,8 @@ func assertSecurityDefined(t *testing.T, comment SwaggerComment) {
310310
comment.router == "/" ||
311311
comment.router == "/users/login" ||
312312
comment.router == "/users/otp/request" ||
313-
comment.router == "/users/otp/change-password" {
313+
comment.router == "/users/otp/change-password" ||
314+
comment.router == "/init-script/{os}/{arch}" {
314315
return // endpoints do not require authorization
315316
}
316317
assert.Containsf(t, authorizedSecurityTags, comment.security, "@Security must be either of these options: %v", authorizedSecurityTags)
@@ -361,7 +362,8 @@ func assertProduce(t *testing.T, comment SwaggerComment) {
361362
(comment.router == "/licenses/{id}" && comment.method == "delete") ||
362363
(comment.router == "/debug/coordinator" && comment.method == "get") ||
363364
(comment.router == "/debug/tailnet" && comment.method == "get") ||
364-
(comment.router == "/workspaces/{workspace}/acl" && comment.method == "patch") {
365+
(comment.router == "/workspaces/{workspace}/acl" && comment.method == "patch") ||
366+
(comment.router == "/init-script/{os}/{arch}" && comment.method == "get") {
365367
return // Exception: HTTP 200 is returned without response entity
366368
}
367369

coderd/initscript.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package coderd
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"strings"
7+
8+
"github.com/go-chi/chi/v5"
9+
10+
"github.com/coder/coder/v2/coderd/httpapi"
11+
"github.com/coder/coder/v2/codersdk"
12+
"github.com/coder/coder/v2/provisionersdk"
13+
)
14+
15+
// @Summary Get agent init script
16+
// @ID get-agent-init-script
17+
// @Produce text/plain
18+
// @Tags InitScript
19+
// @Param os path string true "Operating system"
20+
// @Param arch path string true "Architecture"
21+
// @Success 200 "Success"
22+
// @Router /init-script/{os}/{arch} [get]
23+
func (api *API) initScript(rw http.ResponseWriter, r *http.Request) {
24+
os := strings.ToLower(chi.URLParam(r, "os"))
25+
arch := strings.ToLower(chi.URLParam(r, "arch"))
26+
27+
script, exists := provisionersdk.AgentScriptEnv()[fmt.Sprintf("CODER_AGENT_SCRIPT_%s_%s", os, arch)]
28+
if !exists {
29+
httpapi.Write(r.Context(), rw, http.StatusBadRequest, codersdk.Response{
30+
Message: fmt.Sprintf("Unknown os/arch: %s/%s", os, arch),
31+
})
32+
return
33+
}
34+
script = strings.ReplaceAll(script, "${ACCESS_URL}", api.AccessURL.String()+"/")
35+
script = strings.ReplaceAll(script, "${AUTH_TYPE}", "token")
36+
37+
rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
38+
rw.WriteHeader(http.StatusOK)
39+
_, _ = rw.Write([]byte(script))
40+
}

coderd/initscript_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package coderd_test
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/coder/coder/v2/coderd/coderdtest"
11+
"github.com/coder/coder/v2/codersdk"
12+
)
13+
14+
func TestInitScript(t *testing.T) {
15+
t.Parallel()
16+
17+
t.Run("OK Windows", func(t *testing.T) {
18+
t.Parallel()
19+
client := coderdtest.New(t, nil)
20+
script, err := client.InitScript(context.Background(), "windows", "amd64")
21+
require.NoError(t, err)
22+
require.NotEmpty(t, script)
23+
require.Contains(t, script, "$env:CODER_AGENT_AUTH = \"token\"")
24+
})
25+
26+
t.Run("OK Linux", func(t *testing.T) {
27+
t.Parallel()
28+
client := coderdtest.New(t, nil)
29+
script, err := client.InitScript(context.Background(), "linux", "amd64")
30+
require.NoError(t, err)
31+
require.NotEmpty(t, script)
32+
require.Contains(t, script, "export CODER_AGENT_AUTH=\"token\"")
33+
})
34+
35+
t.Run("BadRequest", func(t *testing.T) {
36+
t.Parallel()
37+
client := coderdtest.New(t, nil)
38+
_, err := client.InitScript(context.Background(), "darwin", "armv7")
39+
require.Error(t, err)
40+
var apiErr *codersdk.Error
41+
require.ErrorAs(t, err, &apiErr)
42+
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
43+
require.Equal(t, "Unknown os/arch: darwin/armv7", apiErr.Message)
44+
})
45+
}

0 commit comments

Comments
 (0)