diff --git a/Makefile b/Makefile index b69e164317..29f8461f48 100644 --- a/Makefile +++ b/Makefile @@ -809,7 +809,7 @@ provisioner/terraform/testdata/version: .PHONY: provisioner/terraform/testdata/version test: - $(GIT_FLAGS) gotestsum --format standard-quiet -- -v -short -count=1 ./... + $(GIT_FLAGS) gotestsum --format standard-quiet -- -v -short -count=1 ./... $(if $(RUN),-run $(RUN)) .PHONY: test test-cli: 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 3423b696fc..2e48634c7d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -5643,44 +5643,6 @@ const docTemplate = `{ } } }, - "/templateversions/{templateversion}/presets/parameters": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "Templates" - ], - "summary": "Get template version preset parameters", - "operationId": "get-template-version-preset-parameters", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Template version ID", - "name": "templateversion", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.PresetParameter" - } - } - } - } - } - }, "/templateversions/{templateversion}/resources": { "get": { "security": [ @@ -13051,6 +13013,12 @@ const docTemplate = `{ }, "name": { "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.PresetParameter" + } } } }, @@ -13060,9 +13028,6 @@ const docTemplate = `{ "name": { "type": "string" }, - "presetID": { - "type": "string" - }, "value": { "type": "string" } @@ -13207,6 +13172,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 d5a836dcb3..0e03555da4 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -4985,40 +4985,6 @@ } } }, - "/templateversions/{templateversion}/presets/parameters": { - "get": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "produces": ["application/json"], - "tags": ["Templates"], - "summary": "Get template version preset parameters", - "operationId": "get-template-version-preset-parameters", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Template version ID", - "name": "templateversion", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.PresetParameter" - } - } - } - } - } - }, "/templateversions/{templateversion}/resources": { "get": { "security": [ @@ -11776,6 +11742,12 @@ }, "name": { "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.PresetParameter" + } } } }, @@ -11785,9 +11757,6 @@ "name": { "type": "string" }, - "presetID": { - "type": "string" - }, "value": { "type": "string" } @@ -11928,6 +11897,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 3c5363181d..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. @@ -1057,10 +1058,7 @@ func New(options *Options) *API { r.Get("/rich-parameters", api.templateVersionRichParameters) r.Get("/external-auth", api.templateVersionExternalAuth) r.Get("/variables", api.templateVersionVariables) - r.Route("/presets", func(r chi.Router) { - r.Get("/", api.templateVersionPresets) - r.Get("/parameters", api.templateVersionPresetParameters) - }) + r.Get("/presets", api.templateVersionPresets) r.Get("/resources", api.templateVersionResources) r.Get("/logs", api.templateVersionLogs) r.Route("/dry-run", func(r chi.Router) { @@ -1735,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/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index f7c438f4a5..46aa96bf1f 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -886,7 +886,6 @@ func (s *MethodTestSuite) TestOrganization() { JobID: job.ID, }) insertPresetParams := database.InsertPresetParams{ - ID: uuid.New(), TemplateVersionID: workspaceBuild.TemplateVersionID, Name: "test", } @@ -3817,13 +3816,11 @@ func (s *MethodTestSuite) TestSystemFunctions() { CreatedBy: user.ID, }) preset, err := db.InsertPreset(ctx, database.InsertPresetParams{ - ID: uuid.New(), TemplateVersionID: templateVersion.ID, Name: "test", }) require.NoError(s.T(), err) _, err = db.InsertPresetParameters(ctx, database.InsertPresetParametersParams{ - ID: uuid.New(), TemplateVersionPresetID: preset.ID, Names: []string{"test"}, Values: []string{"test"}, diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 8d68054a49..21c4023371 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -8149,6 +8149,7 @@ func (q *FakeQuerier) InsertPreset(_ context.Context, arg database.InsertPresetP q.mutex.Lock() defer q.mutex.Unlock() + //nolint:gosimple // arg needs to keep its type for interface reasons and that type is not appropriate for preset below. preset := database.TemplateVersionPreset{ ID: uuid.New(), TemplateVersionID: arg.TemplateVersionID, diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 7bd2052a22..20e7d14b57 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -1266,14 +1266,14 @@ COMMENT ON COLUMN template_version_parameters.display_order IS 'Specifies the or COMMENT ON COLUMN template_version_parameters.ephemeral IS 'The value of an ephemeral parameter will not be preserved between consecutive workspace builds.'; CREATE TABLE template_version_preset_parameters ( - id uuid NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL, template_version_preset_id uuid NOT NULL, name text NOT NULL, value text NOT NULL ); CREATE TABLE template_version_presets ( - id uuid NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL, template_version_id uuid NOT NULL, name text NOT NULL, created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL diff --git a/coderd/database/migrations/000292_generate_default_preset_parameter_ids.down.sql b/coderd/database/migrations/000292_generate_default_preset_parameter_ids.down.sql new file mode 100644 index 0000000000..0cb92a2619 --- /dev/null +++ b/coderd/database/migrations/000292_generate_default_preset_parameter_ids.down.sql @@ -0,0 +1,5 @@ +ALTER TABLE template_version_presets +ALTER COLUMN id DROP DEFAULT; + +ALTER TABLE template_version_preset_parameters +ALTER COLUMN id DROP DEFAULT; diff --git a/coderd/database/migrations/000292_generate_default_preset_parameter_ids.up.sql b/coderd/database/migrations/000292_generate_default_preset_parameter_ids.up.sql new file mode 100644 index 0000000000..9801d1f37c --- /dev/null +++ b/coderd/database/migrations/000292_generate_default_preset_parameter_ids.up.sql @@ -0,0 +1,5 @@ +ALTER TABLE template_version_presets +ALTER COLUMN id SET DEFAULT gen_random_uuid(); + +ALTER TABLE template_version_preset_parameters +ALTER COLUMN id SET DEFAULT gen_random_uuid(); diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 60f05064b7..2d7fe83296 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -5494,25 +5494,19 @@ func (q *sqlQuerier) GetPresetsByTemplateVersionID(ctx context.Context, template const insertPreset = `-- name: InsertPreset :one INSERT INTO - template_version_presets (id, template_version_id, name, created_at) + template_version_presets (template_version_id, name, created_at) VALUES - ($1, $2, $3, $4) RETURNING id, template_version_id, name, created_at + ($1, $2, $3) RETURNING id, template_version_id, name, created_at ` type InsertPresetParams struct { - ID uuid.UUID `db:"id" json:"id"` TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"` Name string `db:"name" json:"name"` CreatedAt time.Time `db:"created_at" json:"created_at"` } func (q *sqlQuerier) InsertPreset(ctx context.Context, arg InsertPresetParams) (TemplateVersionPreset, error) { - row := q.db.QueryRowContext(ctx, insertPreset, - arg.ID, - arg.TemplateVersionID, - arg.Name, - arg.CreatedAt, - ) + row := q.db.QueryRowContext(ctx, insertPreset, arg.TemplateVersionID, arg.Name, arg.CreatedAt) var i TemplateVersionPreset err := row.Scan( &i.ID, @@ -5525,29 +5519,22 @@ func (q *sqlQuerier) InsertPreset(ctx context.Context, arg InsertPresetParams) ( const insertPresetParameters = `-- name: InsertPresetParameters :many INSERT INTO - template_version_preset_parameters (id, template_version_preset_id, name, value) + template_version_preset_parameters (template_version_preset_id, name, value) SELECT $1, - $2, - unnest($3 :: TEXT[]), - unnest($4 :: TEXT[]) + unnest($2 :: TEXT[]), + unnest($3 :: TEXT[]) RETURNING id, template_version_preset_id, name, value ` type InsertPresetParametersParams struct { - ID uuid.UUID `db:"id" json:"id"` TemplateVersionPresetID uuid.UUID `db:"template_version_preset_id" json:"template_version_preset_id"` Names []string `db:"names" json:"names"` Values []string `db:"values" json:"values"` } func (q *sqlQuerier) InsertPresetParameters(ctx context.Context, arg InsertPresetParametersParams) ([]TemplateVersionPresetParameter, error) { - rows, err := q.db.QueryContext(ctx, insertPresetParameters, - arg.ID, - arg.TemplateVersionPresetID, - pq.Array(arg.Names), - pq.Array(arg.Values), - ) + rows, err := q.db.QueryContext(ctx, insertPresetParameters, arg.TemplateVersionPresetID, pq.Array(arg.Names), pq.Array(arg.Values)) if err != nil { return nil, err } @@ -5755,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 @@ -5780,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[])) @@ -5796,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) { @@ -5837,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/presets.sql b/coderd/database/queries/presets.sql index 6bfe31dc09..8e648fce6c 100644 --- a/coderd/database/queries/presets.sql +++ b/coderd/database/queries/presets.sql @@ -1,14 +1,13 @@ -- name: InsertPreset :one INSERT INTO - template_version_presets (id, template_version_id, name, created_at) + template_version_presets (template_version_id, name, created_at) VALUES - (@id, @template_version_id, @name, @created_at) RETURNING *; + (@template_version_id, @name, @created_at) RETURNING *; -- name: InsertPresetParameters :many INSERT INTO - template_version_preset_parameters (id, template_version_preset_id, name, value) + template_version_preset_parameters (template_version_preset_id, name, value) SELECT - @id, @template_version_preset_id, unnest(@names :: TEXT[]), unnest(@values :: TEXT[]) 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/presets.go b/coderd/presets.go index 742d60f2cf..a2787b63e7 100644 --- a/coderd/presets.go +++ b/coderd/presets.go @@ -29,32 +29,6 @@ func (api *API) templateVersionPresets(rw http.ResponseWriter, r *http.Request) return } - var res []codersdk.Preset - for _, preset := range presets { - res = append(res, codersdk.Preset{ - ID: preset.ID, - Name: preset.Name, - }) - } - - httpapi.Write(ctx, rw, http.StatusOK, res) -} - -// @Summary Get template version preset parameters -// @ID get-template-version-preset-parameters -// @Security CoderSessionToken -// @Produce json -// @Tags Templates -// @Param templateversion path string true "Template version ID" format(uuid) -// @Success 200 {array} codersdk.PresetParameter -// @Router /templateversions/{templateversion}/presets/parameters [get] -func (api *API) templateVersionPresetParameters(rw http.ResponseWriter, r *http.Request) { - ctx := r.Context() - templateVersion := httpmw.TemplateVersionParam(r) - - // TODO (sasswart): Test case: what if a user tries to read presets or preset parameters from a different org? - // TODO (sasswart): Do a prelim auth check here. - presetParams, err := api.Database.GetPresetParametersByTemplateVersionID(ctx, templateVersion.ID) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -64,13 +38,19 @@ func (api *API) templateVersionPresetParameters(rw http.ResponseWriter, r *http. return } - var res []codersdk.PresetParameter - for _, presetParam := range presetParams { - res = append(res, codersdk.PresetParameter{ - PresetID: presetParam.TemplateVersionPresetID, - Name: presetParam.Name, - Value: presetParam.Value, - }) + var res []codersdk.Preset + for _, preset := range presets { + sdkPreset := codersdk.Preset{ + ID: preset.ID, + Name: preset.Name, + } + for _, presetParam := range presetParams { + sdkPreset.Parameters = append(sdkPreset.Parameters, codersdk.PresetParameter{ + Name: presetParam.Name, + Value: presetParam.Value, + }) + } + res = append(res, sdkPreset) } httpapi.Write(ctx, rw, http.StatusOK, res) diff --git a/coderd/presets_test.go b/coderd/presets_test.go index a14d8dd50d..ffe51787d5 100644 --- a/coderd/presets_test.go +++ b/coderd/presets_test.go @@ -1,7 +1,6 @@ package coderd_test import ( - "context" "testing" "github.com/stretchr/testify/require" @@ -11,12 +10,29 @@ import ( "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/httpmw" "github.com/coder/coder/v2/coderd/rbac" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/testutil" ) func TestTemplateVersionPresets(t *testing.T) { + // TODO (sasswart): Test case: what if a user tries to read presets or preset parameters from a different org? + t.Parallel() - ctx := context.Background() + givenPreset := codersdk.Preset{ + Name: "My Preset", + Parameters: []codersdk.PresetParameter{ + { + Name: "preset_param1", + Value: "A1B2C3", + }, + { + Name: "preset_param2", + Value: "D4E5F6", + }, + }, + } + ctx := testutil.Context(t, testutil.WaitShort) client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) user := coderdtest.CreateFirstUser(t, client) @@ -25,15 +41,22 @@ func TestTemplateVersionPresets(t *testing.T) { // nolint:gocritic // This is a test provisionerCtx := dbauthz.AsProvisionerd(ctx) - preset, err := db.InsertPreset(provisionerCtx, database.InsertPresetParams{ - Name: "My Preset", + dbPreset, err := db.InsertPreset(provisionerCtx, database.InsertPresetParams{ + Name: givenPreset.Name, TemplateVersionID: version.ID, }) require.NoError(t, err) + + var presetParameterNames []string + var presetParameterValues []string + for _, presetParameter := range givenPreset.Parameters { + presetParameterNames = append(presetParameterNames, presetParameter.Name) + presetParameterValues = append(presetParameterValues, presetParameter.Value) + } _, err = db.InsertPresetParameters(provisionerCtx, database.InsertPresetParametersParams{ - TemplateVersionPresetID: preset.ID, - Names: []string{"preset_param1", "preset_param2"}, - Values: []string{"A1B2C3", "D4E5F6"}, + TemplateVersionPresetID: dbPreset.ID, + Names: presetParameterNames, + Values: presetParameterValues, }) require.NoError(t, err) @@ -41,16 +64,13 @@ func TestTemplateVersionPresets(t *testing.T) { require.NoError(t, err) userCtx := dbauthz.As(ctx, userSubject) - presets, err := client.TemplateVersionPresets(userCtx, version.ID) + gotPresets, err := client.TemplateVersionPresets(userCtx, version.ID) require.NoError(t, err) - require.Equal(t, 1, len(presets)) - require.Equal(t, "My Preset", presets[0].Name) - presetParams, err := client.TemplateVersionPresetParameters(userCtx, version.ID) - require.NoError(t, err) - require.Equal(t, 2, len(presetParams)) - require.Equal(t, "preset_param1", presetParams[0].Name) - require.Equal(t, "A1B2C3", presetParams[0].Value) - require.Equal(t, "preset_param2", presetParams[1].Name) - require.Equal(t, "D4E5F6", presetParams[1].Value) + require.Equal(t, 1, len(gotPresets)) + require.Equal(t, givenPreset.Name, gotPresets[0].Name) + + for _, presetParameter := range givenPreset.Parameters { + require.Contains(t, gotPresets[0].Parameters, presetParameter) + } } 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/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 4c9932abe6..2a58aa421f 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1830,7 +1830,6 @@ func InsertWorkspacePresetsAndParameters(ctx context.Context, logger slog.Logger func InsertWorkspacePresetAndParameters(ctx context.Context, db database.Store, templateVersionID uuid.UUID, protoPreset *sdkproto.Preset, t time.Time) error { err := db.InTx(func(tx database.Store) error { dbPreset, err := tx.InsertPreset(ctx, database.InsertPresetParams{ - ID: uuid.New(), TemplateVersionID: templateVersionID, Name: protoPreset.Name, CreatedAt: t, @@ -1846,7 +1845,6 @@ func InsertWorkspacePresetAndParameters(ctx context.Context, db database.Store, presetParameterValues = append(presetParameterValues, parameter.Value) } _, err = tx.InsertPresetParameters(ctx, database.InsertPresetParametersParams{ - ID: uuid.New(), TemplateVersionPresetID: dbPreset.ID, Names: presetParameterNames, Values: presetParameterValues, diff --git a/codersdk/presets.go b/codersdk/presets.go index cd724a979a..110f6c605f 100644 --- a/codersdk/presets.go +++ b/codersdk/presets.go @@ -11,14 +11,14 @@ import ( ) type Preset struct { - ID uuid.UUID - Name string + ID uuid.UUID + Name string + Parameters []PresetParameter } type PresetParameter struct { - PresetID uuid.UUID - Name string - Value string + Name string + Value string } // TemplateVersionPresets returns the presets associated with a template version. @@ -34,17 +34,3 @@ func (c *Client) TemplateVersionPresets(ctx context.Context, templateVersionID u var presets []Preset return presets, json.NewDecoder(res.Body).Decode(&presets) } - -// TemplateVersionPresetParameters returns the parameters associated with the given presets. -func (c *Client) TemplateVersionPresetParameters(ctx context.Context, templateVersionID uuid.UUID) ([]PresetParameter, error) { - res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/templateversions/%s/presets/parameters", templateVersionID), nil) - if err != nil { - return nil, xerrors.Errorf("do request: %w", err) - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return nil, ReadBodyAsError(res) - } - var parameters []PresetParameter - return parameters, json.NewDecoder(res.Body).Decode(¶meters) -} 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 d5d471ad46..7b2759e281 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -4432,34 +4432,39 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith ```json { "id": "string", - "name": "string" + "name": "string", + "parameters": [ + { + "name": "string", + "value": "string" + } + ] } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -|--------|--------|----------|--------------|-------------| -| `id` | string | false | | | -| `name` | string | false | | | +| Name | Type | Required | Restrictions | Description | +|--------------|---------------------------------------------------------------|----------|--------------|-------------| +| `id` | string | false | | | +| `name` | string | false | | | +| `parameters` | array of [codersdk.PresetParameter](#codersdkpresetparameter) | false | | | ## codersdk.PresetParameter ```json { "name": "string", - "presetID": "string", "value": "string" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -|------------|--------|----------|--------------|-------------| -| `name` | string | false | | | -| `presetID` | string | false | | | -| `value` | string | false | | | +| Name | Type | Required | Restrictions | Description | +|---------|--------|----------|--------------|-------------| +| `name` | string | false | | | +| `value` | string | false | | | ## codersdk.PrometheusConfig @@ -4522,7 +4527,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", @@ -4532,7 +4540,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" @@ -4579,16 +4590,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 @@ -4844,7 +4861,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", @@ -4854,7 +4874,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" @@ -9862,7 +9885,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", @@ -9872,7 +9898,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" @@ -9998,7 +10027,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", @@ -10008,7 +10040,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" @@ -10065,7 +10100,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", @@ -10075,7 +10113,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/api/templates.md b/docs/reference/api/templates.md index 57d35a6aab..ab8b4f1b7c 100644 --- a/docs/reference/api/templates.md +++ b/docs/reference/api/templates.md @@ -2699,7 +2699,13 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/p [ { "id": "string", - "name": "string" + "name": "string", + "parameters": [ + { + "name": "string", + "value": "string" + } + ] } ] ``` @@ -2719,58 +2725,9 @@ Status Code **200** | `[array item]` | array | false | | | | `» id` | string | false | | | | `» name` | string | false | | | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Get template version preset parameters - -### Code samples - -```shell -# Example request using curl -curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/presets/parameters \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`GET /templateversions/{templateversion}/presets/parameters` - -### Parameters - -| Name | In | Type | Required | Description | -|-------------------|------|--------------|----------|---------------------| -| `templateversion` | path | string(uuid) | true | Template version ID | - -### Example responses - -> 200 Response - -```json -[ - { - "name": "string", - "presetID": "string", - "value": "string" - } -] -``` - -### Responses - -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|-------------------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.PresetParameter](schemas.md#codersdkpresetparameter) | - -

Response Schema

- -Status Code **200** - -| Name | Type | Required | Restrictions | Description | -|----------------|--------|----------|--------------|-------------| -| `[array item]` | array | false | | | -| `» name` | string | false | | | -| `» presetID` | string | false | | | -| `» value` | string | false | | | +| `» parameters` | array | false | | | +| `»» name` | string | false | | | +| `»» value` | string | false | | | To perform this operation, you must be authenticated. [Learn more](authentication.md). 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 3dbe734483..a2f55ad2c8 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -764,7 +764,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 9595f22d89..09541c9767 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1554,11 +1554,11 @@ export interface PprofConfig { export interface Preset { readonly ID: string; readonly Name: string; + readonly Parameters: readonly PresetParameter[]; } // From codersdk/presets.go export interface PresetParameter { - readonly PresetID: string; readonly Name: string; readonly Value: string; } @@ -1604,6 +1604,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: {