diff --git a/agent/agentcontainers/containers.go b/agent/agentcontainers/containers.go index 8578f03337..4f03f35ed5 100644 --- a/agent/agentcontainers/containers.go +++ b/agent/agentcontainers/containers.go @@ -140,3 +140,10 @@ type Lister interface { // This should include running and stopped containers. List(ctx context.Context) (codersdk.WorkspaceAgentListContainersResponse, error) } + +// NoopLister is a Lister interface that never returns any containers. +type NoopLister struct{} + +func (NoopLister) List(_ context.Context) (codersdk.WorkspaceAgentListContainersResponse, error) { + return codersdk.WorkspaceAgentListContainersResponse{}, nil +} diff --git a/cli/agent.go b/cli/agent.go index fc96aa6d32..e8a46a84e0 100644 --- a/cli/agent.go +++ b/cli/agent.go @@ -25,6 +25,7 @@ import ( "cdr.dev/slog/sloggers/slogjson" "cdr.dev/slog/sloggers/slogstackdriver" "github.com/coder/coder/v2/agent" + "github.com/coder/coder/v2/agent/agentcontainers" "github.com/coder/coder/v2/agent/agentexec" "github.com/coder/coder/v2/agent/agentssh" "github.com/coder/coder/v2/agent/reaper" @@ -37,21 +38,22 @@ import ( func (r *RootCmd) workspaceAgent() *serpent.Command { var ( - auth string - logDir string - scriptDataDir string - pprofAddress string - noReap bool - sshMaxTimeout time.Duration - tailnetListenPort int64 - prometheusAddress string - debugAddress string - slogHumanPath string - slogJSONPath string - slogStackdriverPath string - blockFileTransfer bool - agentHeaderCommand string - agentHeader []string + auth string + logDir string + scriptDataDir string + pprofAddress string + noReap bool + sshMaxTimeout time.Duration + tailnetListenPort int64 + prometheusAddress string + debugAddress string + slogHumanPath string + slogJSONPath string + slogStackdriverPath string + blockFileTransfer bool + agentHeaderCommand string + agentHeader []string + devcontainersEnabled bool ) cmd := &serpent.Command{ Use: "agent", @@ -314,6 +316,15 @@ func (r *RootCmd) workspaceAgent() *serpent.Command { return xerrors.Errorf("create agent execer: %w", err) } + var containerLister agentcontainers.Lister + if !devcontainersEnabled { + logger.Info(ctx, "agent devcontainer detection not enabled") + containerLister = &agentcontainers.NoopLister{} + } else { + logger.Info(ctx, "agent devcontainer detection enabled") + containerLister = agentcontainers.NewDocker(execer) + } + agnt := agent.New(agent.Options{ Client: client, Logger: logger, @@ -339,6 +350,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command { PrometheusRegistry: prometheusRegistry, BlockFileTransfer: blockFileTransfer, Execer: execer, + ContainerLister: containerLister, }) promHandler := agent.PrometheusMetricsHandler(prometheusRegistry, logger) @@ -461,6 +473,13 @@ func (r *RootCmd) workspaceAgent() *serpent.Command { Description: fmt.Sprintf("Block file transfer using known applications: %s.", strings.Join(agentssh.BlockedFileTransferCommands, ",")), Value: serpent.BoolOf(&blockFileTransfer), }, + { + Flag: "devcontainers-enable", + Default: "false", + Env: "CODER_AGENT_DEVCONTAINERS_ENABLE", + Description: "Allow the agent to automatically detect running devcontainers.", + Value: serpent.BoolOf(&devcontainersEnabled), + }, } return cmd diff --git a/cli/testdata/coder_agent_--help.golden b/cli/testdata/coder_agent_--help.golden index 3394b43a9e..6548a2fadb 100644 --- a/cli/testdata/coder_agent_--help.golden +++ b/cli/testdata/coder_agent_--help.golden @@ -33,6 +33,9 @@ OPTIONS: --debug-address string, $CODER_AGENT_DEBUG_ADDRESS (default: 127.0.0.1:2113) The bind address to serve a debug HTTP server. + --devcontainers-enable bool, $CODER_AGENT_DEVCONTAINERS_ENABLE (default: false) + Allow the agent to automatically detect running devcontainers. + --log-dir string, $CODER_AGENT_LOG_DIR (default: /tmp) Specify the location for the agent log files. diff --git a/cli/testdata/coder_provisioner_list_--help.golden b/cli/testdata/coder_provisioner_list_--help.golden index a9943cb9da..111eb8315b 100644 --- a/cli/testdata/coder_provisioner_list_--help.golden +++ b/cli/testdata/coder_provisioner_list_--help.golden @@ -11,7 +11,7 @@ OPTIONS: -O, --org string, $CODER_ORGANIZATION Select which organization (uuid or name) to use. - -c, --column [id|organization id|created at|last seen at|name|version|api version|tags|key name|status|current job id|current job status|previous job id|previous job status|organization] (default: name,organization,status,key name,created at,last seen at,version,tags) + -c, --column [id|organization id|created at|last seen at|name|version|api version|tags|key name|status|current job id|current job status|current job template name|current job template icon|current job template display name|previous job id|previous job status|previous job template name|previous job template icon|previous job template display name|organization] (default: name,organization,status,key name,created at,last seen at,version,tags) Columns to display in table output. -o, --output table|json (default: table) diff --git a/cli/testdata/coder_provisioner_list_--output_json.golden b/cli/testdata/coder_provisioner_list_--output_json.golden index cd0c085a8c..d6983d11e5 100644 --- a/cli/testdata/coder_provisioner_list_--output_json.golden +++ b/cli/testdata/coder_provisioner_list_--output_json.golden @@ -20,7 +20,10 @@ "current_job": null, "previous_job": { "id": "======[workspace build job ID]======", - "status": "succeeded" + "status": "succeeded", + "template_name": "", + "template_icon": "", + "template_display_name": "" }, "organization_name": "Coder" } diff --git a/cli/testdata/coder_tokens_remove_--help.golden b/cli/testdata/coder_tokens_remove_--help.golden index 30440e8ef2..63caab0c7e 100644 --- a/cli/testdata/coder_tokens_remove_--help.golden +++ b/cli/testdata/coder_tokens_remove_--help.golden @@ -1,7 +1,7 @@ coder v0.0.0-devel USAGE: - coder tokens remove + coder tokens remove Delete a token diff --git a/cli/tokens.go b/cli/tokens.go index 2488a687a0..d132547576 100644 --- a/cli/tokens.go +++ b/cli/tokens.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "os" + "strings" "time" "golang.org/x/exp/slices" @@ -223,7 +224,7 @@ func (r *RootCmd) listTokens() *serpent.Command { func (r *RootCmd) removeToken() *serpent.Command { client := new(codersdk.Client) cmd := &serpent.Command{ - Use: "remove ", + Use: "remove ", Aliases: []string{"delete"}, Short: "Delete a token", Middleware: serpent.Chain( @@ -233,7 +234,12 @@ func (r *RootCmd) removeToken() *serpent.Command { Handler: func(inv *serpent.Invocation) error { token, err := client.APIKeyByName(inv.Context(), codersdk.Me, inv.Args[0]) if err != nil { - return xerrors.Errorf("fetch api key by name %s: %w", inv.Args[0], err) + // If it's a token, we need to extract the ID + maybeID := strings.Split(inv.Args[0], "-")[0] + token, err = client.APIKeyByID(inv.Context(), codersdk.Me, maybeID) + if err != nil { + return xerrors.Errorf("fetch api key by name or id: %w", err) + } } err = client.DeleteAPIKey(inv.Context(), codersdk.Me, token.ID) diff --git a/cli/tokens_test.go b/cli/tokens_test.go index 7c024f3ad1..0c717bb890 100644 --- a/cli/tokens_test.go +++ b/cli/tokens_test.go @@ -93,7 +93,7 @@ func TestTokens(t *testing.T) { require.Contains(t, res, secondTokenID) // Test creating a token for third user from second user's (non-admin) session - inv, root = clitest.New(t, "tokens", "create", "--name", "token-two", "--user", thirdUser.ID.String()) + inv, root = clitest.New(t, "tokens", "create", "--name", "failed-token", "--user", thirdUser.ID.String()) clitest.SetupConfig(t, secondUserClient, root) buf = new(bytes.Buffer) inv.Stdout = buf @@ -113,6 +113,7 @@ func TestTokens(t *testing.T) { require.Len(t, tokens, 1) require.Equal(t, id, tokens[0].ID) + // Delete by name inv, root = clitest.New(t, "tokens", "rm", "token-one") clitest.SetupConfig(t, client, root) buf = new(bytes.Buffer) @@ -122,4 +123,37 @@ func TestTokens(t *testing.T) { res = buf.String() require.NotEmpty(t, res) require.Contains(t, res, "deleted") + + // Delete by ID + inv, root = clitest.New(t, "tokens", "rm", secondTokenID) + clitest.SetupConfig(t, client, root) + buf = new(bytes.Buffer) + inv.Stdout = buf + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + res = buf.String() + require.NotEmpty(t, res) + require.Contains(t, res, "deleted") + + // Create third token + inv, root = clitest.New(t, "tokens", "create", "--name", "token-three") + clitest.SetupConfig(t, client, root) + buf = new(bytes.Buffer) + inv.Stdout = buf + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + res = buf.String() + require.NotEmpty(t, res) + fourthToken := res + + // Delete by token + inv, root = clitest.New(t, "tokens", "rm", fourthToken) + clitest.SetupConfig(t, client, root) + buf = new(bytes.Buffer) + inv.Stdout = buf + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + res = buf.String() + require.NotEmpty(t, res) + require.Contains(t, res, "deleted") } diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 59e04bb35e..af4587483d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -13175,6 +13175,15 @@ const docTemplate = `{ "$ref": "#/definitions/codersdk.ProvisionerJobStatus" } ] + }, + "template_display_name": { + "type": "string" + }, + "template_icon": { + "type": "string" + }, + "template_name": { + "type": "string" } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 625aa64aab..2167ebb8cc 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -11900,6 +11900,15 @@ "$ref": "#/definitions/codersdk.ProvisionerJobStatus" } ] + }, + "template_display_name": { + "type": "string" + }, + "template_icon": { + "type": "string" + }, + "template_name": { + "type": "string" } } }, diff --git a/coderd/coderd.go b/coderd/coderd.go index d503ebb408..8ff8c05ee7 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -788,6 +788,7 @@ func New(options *Options) *API { httpmw.AttachRequestID, httpmw.ExtractRealIP(api.RealIPConfig), httpmw.Logger(api.Logger), + singleSlashMW, rolestore.CustomRoleMW, prometheusMW, // Build-Version is helpful for debugging. @@ -1732,3 +1733,31 @@ func ReadExperiments(log slog.Logger, raw []string) codersdk.Experiments { } return exps } + +var multipleSlashesRe = regexp.MustCompile(`/+`) + +func singleSlashMW(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + var path string + rctx := chi.RouteContext(r.Context()) + if rctx != nil && rctx.RoutePath != "" { + path = rctx.RoutePath + } else { + path = r.URL.Path + } + + // Normalize multiple slashes to a single slash + newPath := multipleSlashesRe.ReplaceAllString(path, "/") + + // Apply the cleaned path + // The approach is consistent with: https://github.com/go-chi/chi/blob/e846b8304c769c4f1a51c9de06bebfaa4576bd88/middleware/strip.go#L24-L28 + if rctx != nil { + rctx.RoutePath = newPath + } else { + r.URL.Path = newPath + } + + next.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) +} diff --git a/coderd/coderd_internal_test.go b/coderd/coderd_internal_test.go new file mode 100644 index 0000000000..34f5738bf9 --- /dev/null +++ b/coderd/coderd_internal_test.go @@ -0,0 +1,69 @@ +package coderd + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/go-chi/chi/v5" + "github.com/stretchr/testify/assert" +) + +func TestStripSlashesMW(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + inputPath string + wantPath string + }{ + {"No changes", "/api/v1/buildinfo", "/api/v1/buildinfo"}, + {"Double slashes", "/api//v2//buildinfo", "/api/v2/buildinfo"}, + {"Triple slashes", "/api///v2///buildinfo", "/api/v2/buildinfo"}, + {"Leading slashes", "///api/v2/buildinfo", "/api/v2/buildinfo"}, + {"Root path", "/", "/"}, + {"Double slashes root", "//", "/"}, + {"Only slashes", "/////", "/"}, + } + + handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + for _, tt := range tests { + tt := tt + + t.Run("chi/"+tt.name, func(t *testing.T) { + t.Parallel() + req := httptest.NewRequest("GET", tt.inputPath, nil) + rec := httptest.NewRecorder() + + // given + rctx := chi.NewRouteContext() + rctx.RoutePath = tt.inputPath + req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) + + // when + singleSlashMW(handler).ServeHTTP(rec, req) + updatedCtx := chi.RouteContext(req.Context()) + + // then + assert.Equal(t, tt.inputPath, req.URL.Path) + assert.Equal(t, tt.wantPath, updatedCtx.RoutePath) + }) + + t.Run("stdlib/"+tt.name, func(t *testing.T) { + t.Parallel() + req := httptest.NewRequest("GET", tt.inputPath, nil) + rec := httptest.NewRecorder() + + // when + singleSlashMW(handler).ServeHTTP(rec, req) + + // then + assert.Equal(t, tt.wantPath, req.URL.Path) + assert.Nil(t, chi.RouteContext(req.Context())) + }) + } +} diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 6030f4a60f..2d7fe83296 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -5742,7 +5742,10 @@ SELECT current_job.id AS current_job_id, current_job.job_status AS current_job_status, previous_job.id AS previous_job_id, - previous_job.job_status AS previous_job_status + previous_job.job_status AS previous_job_status, + COALESCE(tmpl.name, ''::text) AS current_job_template_name, + COALESCE(tmpl.display_name, ''::text) AS current_job_template_display_name, + COALESCE(tmpl.icon, ''::text) AS current_job_template_icon FROM provisioner_daemons pd JOIN @@ -5767,6 +5770,10 @@ LEFT JOIN LIMIT 1 ) ) +LEFT JOIN + template_versions version ON version.id = (current_job.input->>'template_version_id')::uuid +LEFT JOIN + templates tmpl ON tmpl.id = version.template_id WHERE pd.organization_id = $2::uuid AND (COALESCE(array_length($3::uuid[], 1), 0) = 0 OR pd.id = ANY($3::uuid[])) @@ -5783,13 +5790,16 @@ type GetProvisionerDaemonsWithStatusByOrganizationParams struct { } type GetProvisionerDaemonsWithStatusByOrganizationRow struct { - ProvisionerDaemon ProvisionerDaemon `db:"provisioner_daemon" json:"provisioner_daemon"` - Status ProvisionerDaemonStatus `db:"status" json:"status"` - KeyName string `db:"key_name" json:"key_name"` - CurrentJobID uuid.NullUUID `db:"current_job_id" json:"current_job_id"` - CurrentJobStatus NullProvisionerJobStatus `db:"current_job_status" json:"current_job_status"` - PreviousJobID uuid.NullUUID `db:"previous_job_id" json:"previous_job_id"` - PreviousJobStatus NullProvisionerJobStatus `db:"previous_job_status" json:"previous_job_status"` + ProvisionerDaemon ProvisionerDaemon `db:"provisioner_daemon" json:"provisioner_daemon"` + Status ProvisionerDaemonStatus `db:"status" json:"status"` + KeyName string `db:"key_name" json:"key_name"` + CurrentJobID uuid.NullUUID `db:"current_job_id" json:"current_job_id"` + CurrentJobStatus NullProvisionerJobStatus `db:"current_job_status" json:"current_job_status"` + PreviousJobID uuid.NullUUID `db:"previous_job_id" json:"previous_job_id"` + PreviousJobStatus NullProvisionerJobStatus `db:"previous_job_status" json:"previous_job_status"` + CurrentJobTemplateName string `db:"current_job_template_name" json:"current_job_template_name"` + CurrentJobTemplateDisplayName string `db:"current_job_template_display_name" json:"current_job_template_display_name"` + CurrentJobTemplateIcon string `db:"current_job_template_icon" json:"current_job_template_icon"` } func (q *sqlQuerier) GetProvisionerDaemonsWithStatusByOrganization(ctx context.Context, arg GetProvisionerDaemonsWithStatusByOrganizationParams) ([]GetProvisionerDaemonsWithStatusByOrganizationRow, error) { @@ -5824,6 +5834,9 @@ func (q *sqlQuerier) GetProvisionerDaemonsWithStatusByOrganization(ctx context.C &i.CurrentJobStatus, &i.PreviousJobID, &i.PreviousJobStatus, + &i.CurrentJobTemplateName, + &i.CurrentJobTemplateDisplayName, + &i.CurrentJobTemplateIcon, ); err != nil { return nil, err } diff --git a/coderd/database/queries/provisionerdaemons.sql b/coderd/database/queries/provisionerdaemons.sql index abf490c9ab..b003153ee9 100644 --- a/coderd/database/queries/provisionerdaemons.sql +++ b/coderd/database/queries/provisionerdaemons.sql @@ -44,7 +44,10 @@ SELECT current_job.id AS current_job_id, current_job.job_status AS current_job_status, previous_job.id AS previous_job_id, - previous_job.job_status AS previous_job_status + previous_job.job_status AS previous_job_status, + COALESCE(tmpl.name, ''::text) AS current_job_template_name, + COALESCE(tmpl.display_name, ''::text) AS current_job_template_display_name, + COALESCE(tmpl.icon, ''::text) AS current_job_template_icon FROM provisioner_daemons pd JOIN @@ -69,6 +72,10 @@ LEFT JOIN LIMIT 1 ) ) +LEFT JOIN + template_versions version ON version.id = (current_job.input->>'template_version_id')::uuid +LEFT JOIN + templates tmpl ON tmpl.id = version.template_id WHERE pd.organization_id = @organization_id::uuid AND (COALESCE(array_length(@ids::uuid[], 1), 0) = 0 OR pd.id = ANY(@ids::uuid[])) diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index 30add82e3e..bf4dfb6c4d 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -59,8 +59,11 @@ func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) { var currentJob, previousJob *codersdk.ProvisionerDaemonJob if dbDaemon.CurrentJobID.Valid { currentJob = &codersdk.ProvisionerDaemonJob{ - ID: dbDaemon.CurrentJobID.UUID, - Status: codersdk.ProvisionerJobStatus(dbDaemon.CurrentJobStatus.ProvisionerJobStatus), + ID: dbDaemon.CurrentJobID.UUID, + Status: codersdk.ProvisionerJobStatus(dbDaemon.CurrentJobStatus.ProvisionerJobStatus), + TemplateName: dbDaemon.CurrentJobTemplateName, + TemplateIcon: dbDaemon.CurrentJobTemplateIcon, + TemplateDisplayName: dbDaemon.CurrentJobTemplateDisplayName, } } if dbDaemon.PreviousJobID.Valid { diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index 9c8f131cca..f6130f3b82 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -69,8 +69,11 @@ type ProvisionerDaemon struct { } type ProvisionerDaemonJob struct { - ID uuid.UUID `json:"id" format:"uuid" table:"id"` - Status ProvisionerJobStatus `json:"status" enums:"pending,running,succeeded,canceling,canceled,failed" table:"status"` + ID uuid.UUID `json:"id" format:"uuid" table:"id"` + Status ProvisionerJobStatus `json:"status" enums:"pending,running,succeeded,canceling,canceled,failed" table:"status"` + TemplateName string `json:"template_name" table:"template name"` + TemplateIcon string `json:"template_icon" table:"template icon"` + TemplateDisplayName string `json:"template_display_name" table:"template display name"` } // MatchedProvisioners represents the number of provisioner daemons diff --git a/docs/reference/api/debug.md b/docs/reference/api/debug.md index 63fd1aeda8..93fd3e7b63 100644 --- a/docs/reference/api/debug.md +++ b/docs/reference/api/debug.md @@ -309,7 +309,10 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "created_at": "2019-08-24T14:15:22Z", "current_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "key_id": "1e779c8a-6786-4c89-b7c3-a6666f5fd6b5", @@ -319,7 +322,10 @@ curl -X GET http://coder-server:8080/api/v2/debug/health \ "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "previous_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "provisioners": [ "string" diff --git a/docs/reference/api/enterprise.md b/docs/reference/api/enterprise.md index a1a61f4a5b..282cf20ab2 100644 --- a/docs/reference/api/enterprise.md +++ b/docs/reference/api/enterprise.md @@ -1629,7 +1629,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi "created_at": "2019-08-24T14:15:22Z", "current_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "key_id": "1e779c8a-6786-4c89-b7c3-a6666f5fd6b5", @@ -1639,7 +1642,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "previous_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "provisioners": [ "string" @@ -1676,34 +1682,37 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi Status Code **200** -| Name | Type | Required | Restrictions | Description | -|----------------------|--------------------------------------------------------------------------------|----------|--------------|------------------| -| `[array item]` | array | false | | | -| `» daemons` | array | false | | | -| `»» api_version` | string | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» current_job` | [codersdk.ProvisionerDaemonJob](schemas.md#codersdkprovisionerdaemonjob) | false | | | -| `»»» id` | string(uuid) | false | | | -| `»»» status` | [codersdk.ProvisionerJobStatus](schemas.md#codersdkprovisionerjobstatus) | false | | | -| `»» id` | string(uuid) | false | | | -| `»» key_id` | string(uuid) | false | | | -| `»» key_name` | string | false | | Optional fields. | -| `»» last_seen_at` | string(date-time) | false | | | -| `»» name` | string | false | | | -| `»» organization_id` | string(uuid) | false | | | -| `»» previous_job` | [codersdk.ProvisionerDaemonJob](schemas.md#codersdkprovisionerdaemonjob) | false | | | -| `»» provisioners` | array | false | | | -| `»» status` | [codersdk.ProvisionerDaemonStatus](schemas.md#codersdkprovisionerdaemonstatus) | false | | | -| `»» tags` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» version` | string | false | | | -| `» key` | [codersdk.ProvisionerKey](schemas.md#codersdkprovisionerkey) | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» id` | string(uuid) | false | | | -| `»» name` | string | false | | | -| `»» organization` | string(uuid) | false | | | -| `»» tags` | [codersdk.ProvisionerKeyTags](schemas.md#codersdkprovisionerkeytags) | false | | | -| `»»» [any property]` | string | false | | | +| Name | Type | Required | Restrictions | Description | +|-----------------------------|--------------------------------------------------------------------------------|----------|--------------|------------------| +| `[array item]` | array | false | | | +| `» daemons` | array | false | | | +| `»» api_version` | string | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» current_job` | [codersdk.ProvisionerDaemonJob](schemas.md#codersdkprovisionerdaemonjob) | false | | | +| `»»» id` | string(uuid) | false | | | +| `»»» status` | [codersdk.ProvisionerJobStatus](schemas.md#codersdkprovisionerjobstatus) | false | | | +| `»»» template_display_name` | string | false | | | +| `»»» template_icon` | string | false | | | +| `»»» template_name` | string | false | | | +| `»» id` | string(uuid) | false | | | +| `»» key_id` | string(uuid) | false | | | +| `»» key_name` | string | false | | Optional fields. | +| `»» last_seen_at` | string(date-time) | false | | | +| `»» name` | string | false | | | +| `»» organization_id` | string(uuid) | false | | | +| `»» previous_job` | [codersdk.ProvisionerDaemonJob](schemas.md#codersdkprovisionerdaemonjob) | false | | | +| `»» provisioners` | array | false | | | +| `»» status` | [codersdk.ProvisionerDaemonStatus](schemas.md#codersdkprovisionerdaemonstatus) | false | | | +| `»» tags` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» version` | string | false | | | +| `» key` | [codersdk.ProvisionerKey](schemas.md#codersdkprovisionerkey) | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» name` | string | false | | | +| `»» organization` | string(uuid) | false | | | +| `»» tags` | [codersdk.ProvisionerKeyTags](schemas.md#codersdkprovisionerkeytags) | false | | | +| `»»» [any property]` | string | false | | | #### Enumerated Values diff --git a/docs/reference/api/provisioning.md b/docs/reference/api/provisioning.md index bf3c36269f..a8f7fd7e83 100644 --- a/docs/reference/api/provisioning.md +++ b/docs/reference/api/provisioning.md @@ -31,7 +31,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi "created_at": "2019-08-24T14:15:22Z", "current_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "key_id": "1e779c8a-6786-4c89-b7c3-a6666f5fd6b5", @@ -41,7 +44,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "previous_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "provisioners": [ "string" @@ -66,26 +72,29 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi Status Code **200** -| Name | Type | Required | Restrictions | Description | -|---------------------|--------------------------------------------------------------------------------|----------|--------------|------------------| -| `[array item]` | array | false | | | -| `» api_version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» current_job` | [codersdk.ProvisionerDaemonJob](schemas.md#codersdkprovisionerdaemonjob) | false | | | -| `»» id` | string(uuid) | false | | | -| `»» status` | [codersdk.ProvisionerJobStatus](schemas.md#codersdkprovisionerjobstatus) | false | | | -| `» id` | string(uuid) | false | | | -| `» key_id` | string(uuid) | false | | | -| `» key_name` | string | false | | Optional fields. | -| `» last_seen_at` | string(date-time) | false | | | -| `» name` | string | false | | | -| `» organization_id` | string(uuid) | false | | | -| `» previous_job` | [codersdk.ProvisionerDaemonJob](schemas.md#codersdkprovisionerdaemonjob) | false | | | -| `» provisioners` | array | false | | | -| `» status` | [codersdk.ProvisionerDaemonStatus](schemas.md#codersdkprovisionerdaemonstatus) | false | | | -| `» tags` | object | false | | | -| `»» [any property]` | string | false | | | -| `» version` | string | false | | | +| Name | Type | Required | Restrictions | Description | +|----------------------------|--------------------------------------------------------------------------------|----------|--------------|------------------| +| `[array item]` | array | false | | | +| `» api_version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» current_job` | [codersdk.ProvisionerDaemonJob](schemas.md#codersdkprovisionerdaemonjob) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» status` | [codersdk.ProvisionerJobStatus](schemas.md#codersdkprovisionerjobstatus) | false | | | +| `»» template_display_name` | string | false | | | +| `»» template_icon` | string | false | | | +| `»» template_name` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» key_id` | string(uuid) | false | | | +| `» key_name` | string | false | | Optional fields. | +| `» last_seen_at` | string(date-time) | false | | | +| `» name` | string | false | | | +| `» organization_id` | string(uuid) | false | | | +| `» previous_job` | [codersdk.ProvisionerDaemonJob](schemas.md#codersdkprovisionerdaemonjob) | false | | | +| `» provisioners` | array | false | | | +| `» status` | [codersdk.ProvisionerDaemonStatus](schemas.md#codersdkprovisionerdaemonstatus) | false | | | +| `» tags` | object | false | | | +| `»» [any property]` | string | false | | | +| `» version` | string | false | | | #### Enumerated Values diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index ec9d8e0caf..a6ae7287b3 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -4530,7 +4530,10 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith "created_at": "2019-08-24T14:15:22Z", "current_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "key_id": "1e779c8a-6786-4c89-b7c3-a6666f5fd6b5", @@ -4540,7 +4543,10 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "previous_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "provisioners": [ "string" @@ -4587,16 +4593,22 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith ```json { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -|----------|----------------------------------------------------------------|----------|--------------|-------------| -| `id` | string | false | | | -| `status` | [codersdk.ProvisionerJobStatus](#codersdkprovisionerjobstatus) | false | | | +| Name | Type | Required | Restrictions | Description | +|-------------------------|----------------------------------------------------------------|----------|--------------|-------------| +| `id` | string | false | | | +| `status` | [codersdk.ProvisionerJobStatus](#codersdkprovisionerjobstatus) | false | | | +| `template_display_name` | string | false | | | +| `template_icon` | string | false | | | +| `template_name` | string | false | | | #### Enumerated Values @@ -4852,7 +4864,10 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith "created_at": "2019-08-24T14:15:22Z", "current_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "key_id": "1e779c8a-6786-4c89-b7c3-a6666f5fd6b5", @@ -4862,7 +4877,10 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "previous_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "provisioners": [ "string" @@ -9870,7 +9888,10 @@ Zero means unspecified. There might be a limit, but the client need not try to r "created_at": "2019-08-24T14:15:22Z", "current_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "key_id": "1e779c8a-6786-4c89-b7c3-a6666f5fd6b5", @@ -9880,7 +9901,10 @@ Zero means unspecified. There might be a limit, but the client need not try to r "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "previous_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "provisioners": [ "string" @@ -10006,7 +10030,10 @@ Zero means unspecified. There might be a limit, but the client need not try to r "created_at": "2019-08-24T14:15:22Z", "current_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "key_id": "1e779c8a-6786-4c89-b7c3-a6666f5fd6b5", @@ -10016,7 +10043,10 @@ Zero means unspecified. There might be a limit, but the client need not try to r "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "previous_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "provisioners": [ "string" @@ -10073,7 +10103,10 @@ Zero means unspecified. There might be a limit, but the client need not try to r "created_at": "2019-08-24T14:15:22Z", "current_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "key_id": "1e779c8a-6786-4c89-b7c3-a6666f5fd6b5", @@ -10083,7 +10116,10 @@ Zero means unspecified. There might be a limit, but the client need not try to r "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "previous_job": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "status": "pending" + "status": "pending", + "template_display_name": "string", + "template_icon": "string", + "template_name": "string" }, "provisioners": [ "string" diff --git a/docs/reference/cli/provisioner_list.md b/docs/reference/cli/provisioner_list.md index 11abd7dcc3..93718ddd01 100644 --- a/docs/reference/cli/provisioner_list.md +++ b/docs/reference/cli/provisioner_list.md @@ -26,10 +26,10 @@ Select which organization (uuid or name) to use. ### -c, --column -| | | -|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Type | [id\|organization id\|created at\|last seen at\|name\|version\|api version\|tags\|key name\|status\|current job id\|current job status\|previous job id\|previous job status\|organization] | -| Default | name,organization,status,key name,created at,last seen at,version,tags | +| | | +|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Type | [id\|organization id\|created at\|last seen at\|name\|version\|api version\|tags\|key name\|status\|current job id\|current job status\|current job template name\|current job template icon\|current job template display name\|previous job id\|previous job status\|previous job template name\|previous job template icon\|previous job template display name\|organization] | +| Default | name,organization,status,key name,created at,last seen at,version,tags | Columns to display in table output. diff --git a/docs/reference/cli/tokens_remove.md b/docs/reference/cli/tokens_remove.md index 8825040f5e..ae443f6ad0 100644 --- a/docs/reference/cli/tokens_remove.md +++ b/docs/reference/cli/tokens_remove.md @@ -11,5 +11,5 @@ Aliases: ## Usage ```console -coder tokens remove +coder tokens remove ``` diff --git a/dogfood/contents/main.tf b/dogfood/contents/main.tf index 34adf3dcc6..ecd0925c49 100644 --- a/dogfood/contents/main.tf +++ b/dogfood/contents/main.tf @@ -374,6 +374,7 @@ resource "docker_container" "workspace" { "CODER_PROC_PRIO_MGMT=1", "CODER_PROC_OOM_SCORE=10", "CODER_PROC_NICE_SCORE=1", + "CODER_AGENT_DEVCONTAINERS_ENABLE=1", ] host { host = "host.docker.internal" diff --git a/enterprise/cli/testdata/coder_provisioner_list_--help.golden b/enterprise/cli/testdata/coder_provisioner_list_--help.golden index a9943cb9da..111eb8315b 100644 --- a/enterprise/cli/testdata/coder_provisioner_list_--help.golden +++ b/enterprise/cli/testdata/coder_provisioner_list_--help.golden @@ -11,7 +11,7 @@ OPTIONS: -O, --org string, $CODER_ORGANIZATION Select which organization (uuid or name) to use. - -c, --column [id|organization id|created at|last seen at|name|version|api version|tags|key name|status|current job id|current job status|previous job id|previous job status|organization] (default: name,organization,status,key name,created at,last seen at,version,tags) + -c, --column [id|organization id|created at|last seen at|name|version|api version|tags|key name|status|current job id|current job status|current job template name|current job template icon|current job template display name|previous job id|previous job status|previous job template name|previous job template icon|previous job template display name|organization] (default: name,organization,status,key name,created at,last seen at,version,tags) Columns to display in table output. -o, --output table|json (default: table) diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts index 49cad287c8..d00d94c71b 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -762,7 +762,7 @@ export const createServer = async ( async function waitForPort( port: number, host = "0.0.0.0", - timeout = 30000, + timeout = 60_000, ): Promise { const start = Date.now(); while (Date.now() - start < timeout) { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 99310a611b..5ef38e89ec 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1605,6 +1605,9 @@ export interface ProvisionerDaemon { export interface ProvisionerDaemonJob { readonly id: string; readonly status: ProvisionerJobStatus; + readonly template_name: string; + readonly template_icon: string; + readonly template_display_name: string; } // From codersdk/client.go diff --git a/site/vite.config.mts b/site/vite.config.mts index 4deaac0dd5..436565c491 100644 --- a/site/vite.config.mts +++ b/site/vite.config.mts @@ -52,6 +52,12 @@ export default defineConfig({ "csrf_token=JXm9hOUdZctWt0ZZGAy9xiS/gxMKYOThdxjjMnMUyn4=; Path=/; HttpOnly; SameSite=Lax", }, proxy: { + "//": { + changeOrigin: true, + target: process.env.CODER_HOST || "http://localhost:3000", + secure: process.env.NODE_ENV === "production", + rewrite: (path) => path.replace(/\/+/g, "/"), + }, "/api": { ws: true, changeOrigin: true, @@ -84,6 +90,7 @@ export default defineConfig({ secure: process.env.NODE_ENV === "production", }, }, + allowedHosts: true, }, resolve: { alias: {