From 229d77068b061235f7db2a90e9b23c67d4327e84 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 17 Jul 2023 12:14:36 -0400 Subject: [PATCH 01/16] WIP --- cli/server.go | 1 + .../migrations/000138_join_users.down.sql | 5 ++++ .../migrations/000138_join_users.up.sql | 29 +++++++++++++++++++ coderd/templates_test.go | 6 +++- 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 coderd/database/migrations/000138_join_users.down.sql create mode 100644 coderd/database/migrations/000138_join_users.up.sql diff --git a/cli/server.go b/cli/server.go index ec9c049ecefaa..8a61ad221f250 100644 --- a/cli/server.go +++ b/cli/server.go @@ -607,6 +607,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. options.Pubsub = pubsub.NewInMemory() } else { sqlDB, err := connectToPostgres(ctx, logger, sqlDriver, cfg.PostgresURL.String()) + if err != nil { return xerrors.Errorf("connect to postgres: %w", err) } diff --git a/coderd/database/migrations/000138_join_users.down.sql b/coderd/database/migrations/000138_join_users.down.sql new file mode 100644 index 0000000000000..2b83c1aa76b38 --- /dev/null +++ b/coderd/database/migrations/000138_join_users.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP VIEW visible_users; + +COMMIT; diff --git a/coderd/database/migrations/000138_join_users.up.sql b/coderd/database/migrations/000138_join_users.up.sql new file mode 100644 index 0000000000000..17144cee3f166 --- /dev/null +++ b/coderd/database/migrations/000138_join_users.up.sql @@ -0,0 +1,29 @@ +BEGIN; + +CREATE VIEW + visible_users +AS +SELECT + id, username, avatar_url +FROM + users; + +COMMENT ON VIEW visible_users IS 'Visible fields of users are allowed to be joined with other tables for including context of other resources.'; + +CREATE VIEW + template_with_users +AS + SELECT + templates.*, + visible_users.username AS created_by_username, + visible_users.avatar_url AS created_by_avatar_url + FROM + templates + LEFT JOIN + visible_users + ON + templates.created_by = visible_users.id; + +COMMENT ON VIEW template_with_users IS 'Joins in the username + avatar url of the created by user.'; + +COMMIT; diff --git a/coderd/templates_test.go b/coderd/templates_test.go index 4bd65387dbfd1..d71096f8a89be 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -2,6 +2,7 @@ package coderd_test import ( "context" + "database/sql" "net/http" "sync/atomic" "testing" @@ -29,7 +30,10 @@ func TestTemplate(t *testing.T) { t.Run("Get", func(t *testing.T) { t.Parallel() - client := coderdtest.New(t, nil) + db, _ := sql.Open("postgres", "postgresql://postgres:postgres@localhost:5432?sslmode=disable") + client := coderdtest.New(t, &coderdtest.Options{ + Database: database.New(db), + }) user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) From 3f2a8dd5ead2fbc684a147a67dc7aa8d9661b69f Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 17 Jul 2023 13:29:24 -0400 Subject: [PATCH 02/16] chore: Refactor template sql queries to use new view --- coderd/audit/diff.go | 2 +- coderd/database/dbauthz/dbauthz.go | 16 +- coderd/database/dbfake/dbfake.go | 16 +- coderd/database/dbmetrics/dbmetrics.go | 16 +- coderd/database/dump.sql | 56 +++++- .../migrations/000138_join_users.up.sql | 4 +- coderd/database/modelmethods.go | 7 + coderd/database/models.go | 35 ++++ coderd/database/querier.go | 16 +- coderd/database/queries.sql.go | 176 +++++------------- coderd/database/queries/templates.sql | 18 +- coderd/database/sqlc.yaml | 10 + coderd/httpmw/templateparam.go | 4 +- coderd/templates_test.go | 6 +- enterprise/audit/table.go | 4 +- enterprise/coderd/templates.go | 8 +- 16 files changed, 197 insertions(+), 197 deletions(-) diff --git a/coderd/audit/diff.go b/coderd/audit/diff.go index 858334493cf8d..65947c6ae1111 100644 --- a/coderd/audit/diff.go +++ b/coderd/audit/diff.go @@ -9,7 +9,7 @@ import ( // AuditableResources, then add it to this interface. type Auditable interface { database.APIKey | - database.Template | + database.TemplateWithUser | database.TemplateVersion | database.User | database.Workspace | diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index ef2b7b9d18b91..4d0e56beeab58 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1157,11 +1157,11 @@ func (q *querier) GetTemplateAverageBuildTime(ctx context.Context, arg database. return q.db.GetTemplateAverageBuildTime(ctx, arg) } -func (q *querier) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.Template, error) { +func (q *querier) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.TemplateWithUser, error) { return fetch(q.log, q.auth, q.db.GetTemplateByID)(ctx, id) } -func (q *querier) GetTemplateByOrganizationAndName(ctx context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.Template, error) { +func (q *querier) GetTemplateByOrganizationAndName(ctx context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.TemplateWithUser, error) { return fetch(q.log, q.auth, q.db.GetTemplateByOrganizationAndName)(ctx, arg) } @@ -1302,14 +1302,14 @@ func (q *querier) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt return q.db.GetTemplateVersionsCreatedAfter(ctx, createdAt) } -func (q *querier) GetTemplates(ctx context.Context) ([]database.Template, error) { +func (q *querier) GetTemplates(ctx context.Context) ([]database.TemplateWithUser, error) { if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil { return nil, err } return q.db.GetTemplates(ctx) } -func (q *querier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.Template, error) { +func (q *querier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.TemplateWithUser, error) { prep, err := prepareSQLFilter(ctx, q.auth, rbac.ActionRead, rbac.ResourceTemplate.Type) if err != nil { return nil, xerrors.Errorf("(dev error) prepare sql filter: %w", err) @@ -1808,7 +1808,7 @@ func (q *querier) InsertReplica(ctx context.Context, arg database.InsertReplicaP return q.db.InsertReplica(ctx, arg) } -func (q *querier) InsertTemplate(ctx context.Context, arg database.InsertTemplateParams) (database.Template, error) { +func (q *querier) InsertTemplate(ctx context.Context, arg database.InsertTemplateParams) (error, error) { obj := rbac.ResourceTemplate.InOrg(arg.OrganizationID) return insert(q.log, q.auth, obj, q.db.InsertTemplate)(ctx, arg) } @@ -2134,7 +2134,7 @@ func (q *querier) UpdateReplica(ctx context.Context, arg database.UpdateReplicaP return q.db.UpdateReplica(ctx, arg) } -func (q *querier) UpdateTemplateACLByID(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (database.Template, error) { +func (q *querier) UpdateTemplateACLByID(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (error, error) { // UpdateTemplateACL uses the ActionCreate action. Only users that can create the template // may update the ACL. fetch := func(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (database.Template, error) { @@ -2155,14 +2155,14 @@ func (q *querier) UpdateTemplateDeletedByID(ctx context.Context, arg database.Up return q.SoftDeleteTemplateByID(ctx, arg.ID) } -func (q *querier) UpdateTemplateMetaByID(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (database.Template, error) { +func (q *querier) UpdateTemplateMetaByID(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (error, error) { fetch := func(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (database.Template, error) { return q.db.GetTemplateByID(ctx, arg.ID) } return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateTemplateMetaByID)(ctx, arg) } -func (q *querier) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.Template, error) { +func (q *querier) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (error, error) { fetch := func(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.Template, error) { return q.db.GetTemplateByID(ctx, arg.ID) } diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index d68d0d08af199..b39af86e13b13 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -1844,14 +1844,14 @@ func (q *FakeQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg datab return row, nil } -func (q *FakeQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.Template, error) { +func (q *FakeQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.TemplateWithUser, error) { q.mutex.RLock() defer q.mutex.RUnlock() return q.getTemplateByIDNoLock(ctx, id) } -func (q *FakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.Template, error) { +func (q *FakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.TemplateWithUser, error) { if err := validateDatabaseType(arg); err != nil { return database.Template{}, err } @@ -2087,7 +2087,7 @@ func (q *FakeQuerier) GetTemplateVersionsCreatedAfter(_ context.Context, after t return versions, nil } -func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.Template, error) { +func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.TemplateWithUser, error) { q.mutex.RLock() defer q.mutex.RUnlock() @@ -2105,7 +2105,7 @@ func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.Template, erro return templates, nil } -func (q *FakeQuerier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.Template, error) { +func (q *FakeQuerier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.TemplateWithUser, error) { if err := validateDatabaseType(arg); err != nil { return nil, err } @@ -3436,7 +3436,7 @@ func (q *FakeQuerier) InsertReplica(_ context.Context, arg database.InsertReplic return replica, nil } -func (q *FakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTemplateParams) (database.Template, error) { +func (q *FakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTemplateParams) (error, error) { if err := validateDatabaseType(arg); err != nil { return database.Template{}, err } @@ -4172,7 +4172,7 @@ func (q *FakeQuerier) UpdateReplica(_ context.Context, arg database.UpdateReplic return database.Replica{}, sql.ErrNoRows } -func (q *FakeQuerier) UpdateTemplateACLByID(_ context.Context, arg database.UpdateTemplateACLByIDParams) (database.Template, error) { +func (q *FakeQuerier) UpdateTemplateACLByID(_ context.Context, arg database.UpdateTemplateACLByIDParams) (error, error) { if err := validateDatabaseType(arg); err != nil { return database.Template{}, err } @@ -4233,7 +4233,7 @@ func (q *FakeQuerier) UpdateTemplateDeletedByID(_ context.Context, arg database. return sql.ErrNoRows } -func (q *FakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.UpdateTemplateMetaByIDParams) (database.Template, error) { +func (q *FakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.UpdateTemplateMetaByIDParams) (error, error) { if err := validateDatabaseType(arg); err != nil { return database.Template{}, err } @@ -4257,7 +4257,7 @@ func (q *FakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd return database.Template{}, sql.ErrNoRows } -func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.Template, error) { +func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database.UpdateTemplateScheduleByIDParams) (error, error) { if err := validateDatabaseType(arg); err != nil { return database.Template{}, err } diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index 12f03ea8c75fd..ed1114f1fb234 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -578,14 +578,14 @@ func (m metricsStore) GetTemplateAverageBuildTime(ctx context.Context, arg datab return buildTime, err } -func (m metricsStore) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.Template, error) { +func (m metricsStore) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.TemplateWithUser, error) { start := time.Now() template, err := m.s.GetTemplateByID(ctx, id) m.queryLatencies.WithLabelValues("GetTemplateByID").Observe(time.Since(start).Seconds()) return template, err } -func (m metricsStore) GetTemplateByOrganizationAndName(ctx context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.Template, error) { +func (m metricsStore) GetTemplateByOrganizationAndName(ctx context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.TemplateWithUser, error) { start := time.Now() template, err := m.s.GetTemplateByOrganizationAndName(ctx, arg) m.queryLatencies.WithLabelValues("GetTemplateByOrganizationAndName").Observe(time.Since(start).Seconds()) @@ -655,14 +655,14 @@ func (m metricsStore) GetTemplateVersionsCreatedAfter(ctx context.Context, creat return versions, err } -func (m metricsStore) GetTemplates(ctx context.Context) ([]database.Template, error) { +func (m metricsStore) GetTemplates(ctx context.Context) ([]database.TemplateWithUser, error) { start := time.Now() templates, err := m.s.GetTemplates(ctx) m.queryLatencies.WithLabelValues("GetTemplates").Observe(time.Since(start).Seconds()) return templates, err } -func (m metricsStore) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.Template, error) { +func (m metricsStore) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.TemplateWithUser, error) { start := time.Now() templates, err := m.s.GetTemplatesWithFilter(ctx, arg) m.queryLatencies.WithLabelValues("GetTemplatesWithFilter").Observe(time.Since(start).Seconds()) @@ -1103,7 +1103,7 @@ func (m metricsStore) InsertReplica(ctx context.Context, arg database.InsertRepl return replica, err } -func (m metricsStore) InsertTemplate(ctx context.Context, arg database.InsertTemplateParams) (database.Template, error) { +func (m metricsStore) InsertTemplate(ctx context.Context, arg database.InsertTemplateParams) (error, error) { start := time.Now() template, err := m.s.InsertTemplate(ctx, arg) m.queryLatencies.WithLabelValues("InsertTemplate").Observe(time.Since(start).Seconds()) @@ -1306,7 +1306,7 @@ func (m metricsStore) UpdateReplica(ctx context.Context, arg database.UpdateRepl return replica, err } -func (m metricsStore) UpdateTemplateACLByID(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (database.Template, error) { +func (m metricsStore) UpdateTemplateACLByID(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (error, error) { start := time.Now() template, err := m.s.UpdateTemplateACLByID(ctx, arg) m.queryLatencies.WithLabelValues("UpdateTemplateACLByID").Observe(time.Since(start).Seconds()) @@ -1327,14 +1327,14 @@ func (m metricsStore) UpdateTemplateDeletedByID(ctx context.Context, arg databas return err } -func (m metricsStore) UpdateTemplateMetaByID(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (database.Template, error) { +func (m metricsStore) UpdateTemplateMetaByID(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (error, error) { start := time.Now() template, err := m.s.UpdateTemplateMetaByID(ctx, arg) m.queryLatencies.WithLabelValues("UpdateTemplateMetaByID").Observe(time.Since(start).Seconds()) return template, err } -func (m metricsStore) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.Template, error) { +func (m metricsStore) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (error, error) { start := time.Now() template, err := m.s.UpdateTemplateScheduleByID(ctx, arg) m.queryLatencies.WithLabelValues("UpdateTemplateScheduleByID").Observe(time.Since(start).Seconds()) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 322de586cfa31..11d4bcb0caa8d 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -579,15 +579,6 @@ COMMENT ON COLUMN templates.allow_user_autostart IS 'Allow users to specify an a COMMENT ON COLUMN templates.allow_user_autostop IS 'Allow users to specify custom autostop values for workspaces (enterprise).'; -CREATE TABLE user_links ( - user_id uuid NOT NULL, - login_type login_type NOT NULL, - linked_id text DEFAULT ''::text NOT NULL, - oauth_access_token text DEFAULT ''::text NOT NULL, - oauth_refresh_token text DEFAULT ''::text NOT NULL, - oauth_expiry timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL -); - CREATE TABLE users ( id uuid NOT NULL, email text NOT NULL, @@ -603,6 +594,53 @@ CREATE TABLE users ( last_seen_at timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL ); +CREATE VIEW visible_users AS + SELECT users.id, + users.username, + users.avatar_url + FROM users; + +COMMENT ON VIEW visible_users IS 'Visible fields of users are allowed to be joined with other tables for including context of other resources.'; + +CREATE VIEW template_with_users AS + SELECT templates.id, + templates.created_at, + templates.updated_at, + templates.organization_id, + templates.deleted, + templates.name, + templates.provisioner, + templates.active_version_id, + templates.description, + templates.default_ttl, + templates.created_by, + templates.icon, + templates.user_acl, + templates.group_acl, + templates.display_name, + templates.allow_user_cancel_workspace_jobs, + templates.max_ttl, + templates.allow_user_autostart, + templates.allow_user_autostop, + templates.failure_ttl, + templates.inactivity_ttl, + templates.locked_ttl, + COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url, + COALESCE(visible_users.username, ''::text) AS created_by_username + FROM (public.templates + LEFT JOIN visible_users ON ((templates.created_by = visible_users.id))); + +COMMENT ON VIEW template_with_users IS 'Joins in the username + avatar url of the created by user.'; + +CREATE TABLE user_links ( + user_id uuid NOT NULL, + login_type login_type NOT NULL, + linked_id text DEFAULT ''::text NOT NULL, + oauth_access_token text DEFAULT ''::text NOT NULL, + oauth_refresh_token text DEFAULT ''::text NOT NULL, + oauth_expiry timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL +); + CREATE UNLOGGED TABLE workspace_agent_metadata ( workspace_agent_id uuid NOT NULL, display_name character varying(127) NOT NULL, diff --git a/coderd/database/migrations/000138_join_users.up.sql b/coderd/database/migrations/000138_join_users.up.sql index 17144cee3f166..7daabfb23f699 100644 --- a/coderd/database/migrations/000138_join_users.up.sql +++ b/coderd/database/migrations/000138_join_users.up.sql @@ -15,8 +15,8 @@ CREATE VIEW AS SELECT templates.*, - visible_users.username AS created_by_username, - visible_users.avatar_url AS created_by_avatar_url + coalesce(visible_users.avatar_url, '') AS created_by_avatar_url, + coalesce(visible_users.username, '') AS created_by_username FROM templates LEFT JOIN diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index bb7dfdd1bb818..0739e32faee5c 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -109,6 +109,13 @@ func (t Template) RBACObject() rbac.Object { WithGroupACL(t.GroupACL) } +func (t TemplateWithUser) RBACObject() rbac.Object { + return rbac.ResourceTemplate.WithID(t.ID). + InOrg(t.OrganizationID). + WithACLUserList(t.UserACL). + WithGroupACL(t.GroupACL) +} + func (t GetFileTemplatesRow) RBACObject() rbac.Object { return rbac.ResourceTemplate.WithID(t.TemplateID). InOrg(t.TemplateOrganizationID). diff --git a/coderd/database/models.go b/coderd/database/models.go index 72041fb6c8a52..da10c4f0f2684 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1667,6 +1667,34 @@ type TemplateVersionVariable struct { Sensitive bool `db:"sensitive" json:"sensitive"` } +// Joins in the username + avatar url of the created by user. +type TemplateWithUser struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` + Deleted bool `db:"deleted" json:"deleted"` + Name string `db:"name" json:"name"` + Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` + Description string `db:"description" json:"description"` + DefaultTTL int64 `db:"default_ttl" json:"default_ttl"` + CreatedBy uuid.UUID `db:"created_by" json:"created_by"` + Icon string `db:"icon" json:"icon"` + UserACL TemplateACL `db:"user_acl" json:"user_acl"` + GroupACL TemplateACL `db:"group_acl" json:"group_acl"` + DisplayName string `db:"display_name" json:"display_name"` + AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` + MaxTTL int64 `db:"max_ttl" json:"max_ttl"` + AllowUserAutostart bool `db:"allow_user_autostart" json:"allow_user_autostart"` + AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` + FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` + InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` + LockedTTL int64 `db:"locked_ttl" json:"locked_ttl"` + CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"` + CreatedByUsername string `db:"created_by_username" json:"created_by_username"` +} + type User struct { ID uuid.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` @@ -1691,6 +1719,13 @@ type UserLink struct { OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"` } +// Visible fields of users are allowed to be joined with other tables for including context of other resources. +type VisibleUser struct { + ID uuid.UUID `db:"id" json:"id"` + Username string `db:"username" json:"username"` + AvatarURL sql.NullString `db:"avatar_url" json:"avatar_url"` +} + type Workspace struct { ID uuid.UUID `db:"id" json:"id"` CreatedAt time.Time `db:"created_at" json:"created_at"` diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 9fe7a61b7e6c6..aafdab99f9294 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -102,8 +102,8 @@ type sqlcQuerier interface { GetTailnetAgents(ctx context.Context, id uuid.UUID) ([]TailnetAgent, error) GetTailnetClientsForAgent(ctx context.Context, agentID uuid.UUID) ([]TailnetClient, error) GetTemplateAverageBuildTime(ctx context.Context, arg GetTemplateAverageBuildTimeParams) (GetTemplateAverageBuildTimeRow, error) - GetTemplateByID(ctx context.Context, id uuid.UUID) (Template, error) - GetTemplateByOrganizationAndName(ctx context.Context, arg GetTemplateByOrganizationAndNameParams) (Template, error) + GetTemplateByID(ctx context.Context, id uuid.UUID) (TemplateWithUser, error) + GetTemplateByOrganizationAndName(ctx context.Context, arg GetTemplateByOrganizationAndNameParams) (TemplateWithUser, error) GetTemplateDAUs(ctx context.Context, arg GetTemplateDAUsParams) ([]GetTemplateDAUsRow, error) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (TemplateVersion, error) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.UUID) (TemplateVersion, error) @@ -113,8 +113,8 @@ type sqlcQuerier interface { GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UUID) ([]TemplateVersion, error) GetTemplateVersionsByTemplateID(ctx context.Context, arg GetTemplateVersionsByTemplateIDParams) ([]TemplateVersion, error) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) - GetTemplates(ctx context.Context) ([]Template, error) - GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]Template, error) + GetTemplates(ctx context.Context) ([]TemplateWithUser, error) + GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]TemplateWithUser, error) GetUnexpiredLicenses(ctx context.Context) ([]License, error) GetUserByEmailOrUsername(ctx context.Context, arg GetUserByEmailOrUsernameParams) (User, error) GetUserByID(ctx context.Context, id uuid.UUID) (User, error) @@ -191,7 +191,7 @@ type sqlcQuerier interface { InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error) InsertProvisionerJobLogs(ctx context.Context, arg InsertProvisionerJobLogsParams) ([]ProvisionerJobLog, error) InsertReplica(ctx context.Context, arg InsertReplicaParams) (Replica, error) - InsertTemplate(ctx context.Context, arg InsertTemplateParams) (Template, error) + InsertTemplate(ctx context.Context, arg InsertTemplateParams) error InsertTemplateVersion(ctx context.Context, arg InsertTemplateVersionParams) (TemplateVersion, error) InsertTemplateVersionParameter(ctx context.Context, arg InsertTemplateVersionParameterParams) (TemplateVersionParameter, error) InsertTemplateVersionVariable(ctx context.Context, arg InsertTemplateVersionVariableParams) (TemplateVersionVariable, error) @@ -225,11 +225,11 @@ type sqlcQuerier interface { UpdateProvisionerJobWithCancelByID(ctx context.Context, arg UpdateProvisionerJobWithCancelByIDParams) error UpdateProvisionerJobWithCompleteByID(ctx context.Context, arg UpdateProvisionerJobWithCompleteByIDParams) error UpdateReplica(ctx context.Context, arg UpdateReplicaParams) (Replica, error) - UpdateTemplateACLByID(ctx context.Context, arg UpdateTemplateACLByIDParams) (Template, error) + UpdateTemplateACLByID(ctx context.Context, arg UpdateTemplateACLByIDParams) error UpdateTemplateActiveVersionByID(ctx context.Context, arg UpdateTemplateActiveVersionByIDParams) error UpdateTemplateDeletedByID(ctx context.Context, arg UpdateTemplateDeletedByIDParams) error - UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) (Template, error) - UpdateTemplateScheduleByID(ctx context.Context, arg UpdateTemplateScheduleByIDParams) (Template, error) + UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error + UpdateTemplateScheduleByID(ctx context.Context, arg UpdateTemplateScheduleByIDParams) error UpdateTemplateVersionByID(ctx context.Context, arg UpdateTemplateVersionByIDParams) (TemplateVersion, error) UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error UpdateTemplateVersionGitAuthProvidersByJobID(ctx context.Context, arg UpdateTemplateVersionGitAuthProvidersByJobIDParams) error diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 484a6ff4d6491..5b2800c0ce34c 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3637,18 +3637,18 @@ func (q *sqlQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg GetTem const getTemplateByID = `-- name: GetTemplateByID :one SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl, created_by_avatar_url, created_by_username FROM - templates + template_with_users AS templates WHERE id = $1 LIMIT 1 ` -func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Template, error) { +func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (TemplateWithUser, error) { row := q.db.QueryRowContext(ctx, getTemplateByID, id) - var i Template + var i TemplateWithUser err := row.Scan( &i.ID, &i.CreatedAt, @@ -3672,15 +3672,17 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.FailureTTL, &i.InactivityTTL, &i.LockedTTL, + &i.CreatedByAvatarURL, + &i.CreatedByUsername, ) return i, err } const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl, created_by_avatar_url, created_by_username FROM - templates + template_with_users AS templates WHERE organization_id = $1 AND deleted = $2 @@ -3695,9 +3697,9 @@ type GetTemplateByOrganizationAndNameParams struct { Name string `db:"name" json:"name"` } -func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg GetTemplateByOrganizationAndNameParams) (Template, error) { +func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg GetTemplateByOrganizationAndNameParams) (TemplateWithUser, error) { row := q.db.QueryRowContext(ctx, getTemplateByOrganizationAndName, arg.OrganizationID, arg.Deleted, arg.Name) - var i Template + var i TemplateWithUser err := row.Scan( &i.ID, &i.CreatedAt, @@ -3721,24 +3723,26 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.FailureTTL, &i.InactivityTTL, &i.LockedTTL, + &i.CreatedByAvatarURL, + &i.CreatedByUsername, ) return i, err } const getTemplates = `-- name: GetTemplates :many -SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl FROM templates +SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl, created_by_avatar_url, created_by_username FROM template_with_users AS templates ORDER BY (name, id) ASC ` -func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { +func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]TemplateWithUser, error) { rows, err := q.db.QueryContext(ctx, getTemplates) if err != nil { return nil, err } defer rows.Close() - var items []Template + var items []TemplateWithUser for rows.Next() { - var i Template + var i TemplateWithUser if err := rows.Scan( &i.ID, &i.CreatedAt, @@ -3762,6 +3766,8 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.FailureTTL, &i.InactivityTTL, &i.LockedTTL, + &i.CreatedByAvatarURL, + &i.CreatedByUsername, ); err != nil { return nil, err } @@ -3778,9 +3784,9 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl, created_by_avatar_url, created_by_username FROM - templates + template_with_users AS templates WHERE -- Optionally include deleted templates templates.deleted = $1 @@ -3814,7 +3820,7 @@ type GetTemplatesWithFilterParams struct { IDs []uuid.UUID `db:"ids" json:"ids"` } -func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]Template, error) { +func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]TemplateWithUser, error) { rows, err := q.db.QueryContext(ctx, getTemplatesWithFilter, arg.Deleted, arg.OrganizationID, @@ -3825,9 +3831,9 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate return nil, err } defer rows.Close() - var items []Template + var items []TemplateWithUser for rows.Next() { - var i Template + var i TemplateWithUser if err := rows.Scan( &i.ID, &i.CreatedAt, @@ -3851,6 +3857,8 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.FailureTTL, &i.InactivityTTL, &i.LockedTTL, + &i.CreatedByAvatarURL, + &i.CreatedByUsername, ); err != nil { return nil, err } @@ -3865,7 +3873,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate return items, nil } -const insertTemplate = `-- name: InsertTemplate :one +const insertTemplate = `-- name: InsertTemplate :exec INSERT INTO templates ( id, @@ -3884,7 +3892,7 @@ INSERT INTO allow_user_cancel_workspace_jobs ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) ` type InsertTemplateParams struct { @@ -3904,8 +3912,8 @@ type InsertTemplateParams struct { AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` } -func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParams) (Template, error) { - row := q.db.QueryRowContext(ctx, insertTemplate, +func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParams) error { + _, err := q.db.ExecContext(ctx, insertTemplate, arg.ID, arg.CreatedAt, arg.UpdatedAt, @@ -3921,35 +3929,10 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam arg.DisplayName, arg.AllowUserCancelWorkspaceJobs, ) - var i Template - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.OrganizationID, - &i.Deleted, - &i.Name, - &i.Provisioner, - &i.ActiveVersionID, - &i.Description, - &i.DefaultTTL, - &i.CreatedBy, - &i.Icon, - &i.UserACL, - &i.GroupACL, - &i.DisplayName, - &i.AllowUserCancelWorkspaceJobs, - &i.MaxTTL, - &i.AllowUserAutostart, - &i.AllowUserAutostop, - &i.FailureTTL, - &i.InactivityTTL, - &i.LockedTTL, - ) - return i, err + return err } -const updateTemplateACLByID = `-- name: UpdateTemplateACLByID :one +const updateTemplateACLByID = `-- name: UpdateTemplateACLByID :exec UPDATE templates SET @@ -3967,34 +3950,9 @@ type UpdateTemplateACLByIDParams struct { ID uuid.UUID `db:"id" json:"id"` } -func (q *sqlQuerier) UpdateTemplateACLByID(ctx context.Context, arg UpdateTemplateACLByIDParams) (Template, error) { - row := q.db.QueryRowContext(ctx, updateTemplateACLByID, arg.GroupACL, arg.UserACL, arg.ID) - var i Template - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.OrganizationID, - &i.Deleted, - &i.Name, - &i.Provisioner, - &i.ActiveVersionID, - &i.Description, - &i.DefaultTTL, - &i.CreatedBy, - &i.Icon, - &i.UserACL, - &i.GroupACL, - &i.DisplayName, - &i.AllowUserCancelWorkspaceJobs, - &i.MaxTTL, - &i.AllowUserAutostart, - &i.AllowUserAutostop, - &i.FailureTTL, - &i.InactivityTTL, - &i.LockedTTL, - ) - return i, err +func (q *sqlQuerier) UpdateTemplateACLByID(ctx context.Context, arg UpdateTemplateACLByIDParams) error { + _, err := q.db.ExecContext(ctx, updateTemplateACLByID, arg.GroupACL, arg.UserACL, arg.ID) + return err } const updateTemplateActiveVersionByID = `-- name: UpdateTemplateActiveVersionByID :exec @@ -4039,7 +3997,7 @@ func (q *sqlQuerier) UpdateTemplateDeletedByID(ctx context.Context, arg UpdateTe return err } -const updateTemplateMetaByID = `-- name: UpdateTemplateMetaByID :one +const updateTemplateMetaByID = `-- name: UpdateTemplateMetaByID :exec UPDATE templates SET @@ -4065,8 +4023,8 @@ type UpdateTemplateMetaByIDParams struct { AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` } -func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) (Template, error) { - row := q.db.QueryRowContext(ctx, updateTemplateMetaByID, +func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error { + _, err := q.db.ExecContext(ctx, updateTemplateMetaByID, arg.ID, arg.UpdatedAt, arg.Description, @@ -4075,35 +4033,10 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl arg.DisplayName, arg.AllowUserCancelWorkspaceJobs, ) - var i Template - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.OrganizationID, - &i.Deleted, - &i.Name, - &i.Provisioner, - &i.ActiveVersionID, - &i.Description, - &i.DefaultTTL, - &i.CreatedBy, - &i.Icon, - &i.UserACL, - &i.GroupACL, - &i.DisplayName, - &i.AllowUserCancelWorkspaceJobs, - &i.MaxTTL, - &i.AllowUserAutostart, - &i.AllowUserAutostop, - &i.FailureTTL, - &i.InactivityTTL, - &i.LockedTTL, - ) - return i, err + return err } -const updateTemplateScheduleByID = `-- name: UpdateTemplateScheduleByID :one +const updateTemplateScheduleByID = `-- name: UpdateTemplateScheduleByID :exec UPDATE templates SET @@ -4133,8 +4066,8 @@ type UpdateTemplateScheduleByIDParams struct { LockedTTL int64 `db:"locked_ttl" json:"locked_ttl"` } -func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateTemplateScheduleByIDParams) (Template, error) { - row := q.db.QueryRowContext(ctx, updateTemplateScheduleByID, +func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateTemplateScheduleByIDParams) error { + _, err := q.db.ExecContext(ctx, updateTemplateScheduleByID, arg.ID, arg.UpdatedAt, arg.AllowUserAutostart, @@ -4145,32 +4078,7 @@ func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateT arg.InactivityTTL, arg.LockedTTL, ) - var i Template - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.OrganizationID, - &i.Deleted, - &i.Name, - &i.Provisioner, - &i.ActiveVersionID, - &i.Description, - &i.DefaultTTL, - &i.CreatedBy, - &i.Icon, - &i.UserACL, - &i.GroupACL, - &i.DisplayName, - &i.AllowUserCancelWorkspaceJobs, - &i.MaxTTL, - &i.AllowUserAutostart, - &i.AllowUserAutostop, - &i.FailureTTL, - &i.InactivityTTL, - &i.LockedTTL, - ) - return i, err + return err } const getTemplateVersionParameters = `-- name: GetTemplateVersionParameters :many diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 3750b2fa76fd7..7b04480d3967f 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -2,7 +2,7 @@ SELECT * FROM - templates + template_with_users AS templates WHERE id = $1 LIMIT @@ -12,7 +12,7 @@ LIMIT SELECT * FROM - templates + template_with_users AS templates WHERE -- Optionally include deleted templates templates.deleted = @deleted @@ -43,7 +43,7 @@ ORDER BY (name, id) ASC SELECT * FROM - templates + template_with_users AS templates WHERE organization_id = @organization_id AND deleted = @deleted @@ -52,11 +52,11 @@ LIMIT 1; -- name: GetTemplates :many -SELECT * FROM templates +SELECT * FROM template_with_users AS templates ORDER BY (name, id) ASC ; --- name: InsertTemplate :one +-- name: InsertTemplate :exec INSERT INTO templates ( id, @@ -75,7 +75,7 @@ INSERT INTO allow_user_cancel_workspace_jobs ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14); -- name: UpdateTemplateActiveVersionByID :exec UPDATE @@ -95,7 +95,7 @@ SET WHERE id = $1; --- name: UpdateTemplateMetaByID :one +-- name: UpdateTemplateMetaByID :exec UPDATE templates SET @@ -110,7 +110,7 @@ WHERE RETURNING *; --- name: UpdateTemplateScheduleByID :one +-- name: UpdateTemplateScheduleByID :exec UPDATE templates SET @@ -127,7 +127,7 @@ WHERE RETURNING *; --- name: UpdateTemplateACLByID :one +-- name: UpdateTemplateACLByID :exec UPDATE templates SET diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index 964706cf0ad06..a3782d18cb864 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -21,12 +21,22 @@ overrides: - column: "templates.group_acl" go_type: type: "TemplateACL" + - column: "template_with_users.user_acl" + go_type: + type: "TemplateACL" + - column: "template_with_users.group_acl" + go_type: + type: "TemplateACL" + - column: "template_with_users.created_by_avatar_url" + go_type: + type: "string" rename: api_key: APIKey api_key_scope: APIKeyScope api_key_scope_all: APIKeyScopeAll api_key_scope_application_connect: APIKeyScopeApplicationConnect avatar_url: AvatarURL + created_by_avatar_url: CreatedByAvatarURL session_count_vscode: SessionCountVSCode session_count_jetbrains: SessionCountJetBrains session_count_reconnecting_pty: SessionCountReconnectingPTY diff --git a/coderd/httpmw/templateparam.go b/coderd/httpmw/templateparam.go index 1ba57167d5483..4b1f60bbb8671 100644 --- a/coderd/httpmw/templateparam.go +++ b/coderd/httpmw/templateparam.go @@ -14,8 +14,8 @@ import ( type templateParamContextKey struct{} // TemplateParam returns the template from the ExtractTemplateParam handler. -func TemplateParam(r *http.Request) database.Template { - template, ok := r.Context().Value(templateParamContextKey{}).(database.Template) +func TemplateParam(r *http.Request) database.TemplateWithUser { + template, ok := r.Context().Value(templateParamContextKey{}).(database.TemplateWithUser) if !ok { panic("developer error: template param middleware not provided") } diff --git a/coderd/templates_test.go b/coderd/templates_test.go index d71096f8a89be..4bd65387dbfd1 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -2,7 +2,6 @@ package coderd_test import ( "context" - "database/sql" "net/http" "sync/atomic" "testing" @@ -30,10 +29,7 @@ func TestTemplate(t *testing.T) { t.Run("Get", func(t *testing.T) { t.Parallel() - db, _ := sql.Open("postgres", "postgresql://postgres:postgres@localhost:5432?sslmode=disable") - client := coderdtest.New(t, &coderdtest.Options{ - Database: database.New(db), - }) + client := coderdtest.New(t, nil) user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 4a2df1e64194b..42ef48b30b9a5 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -57,7 +57,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "private_key": ActionSecret, // We don't want to expose private keys in diffs. "public_key": ActionTrack, // Public keys are ok to expose in a diff. }, - &database.Template{}: { + &database.TemplateWithUser{}: { "id": ActionTrack, "created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff. "updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff. @@ -71,6 +71,8 @@ var auditableResourcesTypes = map[any]map[string]Action{ "icon": ActionTrack, "default_ttl": ActionTrack, "created_by": ActionTrack, + "created_by_username": ActionTrack, + "created_by_avatar_url": ActionIgnore, "group_acl": ActionTrack, "user_acl": ActionTrack, "allow_user_autostart": ActionTrack, diff --git a/enterprise/coderd/templates.go b/enterprise/coderd/templates.go index 0ae433787526b..81935bc075f4c 100644 --- a/enterprise/coderd/templates.go +++ b/enterprise/coderd/templates.go @@ -105,7 +105,7 @@ func (api *API) patchTemplateACL(rw http.ResponseWriter, r *http.Request) { ctx = r.Context() template = httpmw.TemplateParam(r) auditor = api.AGPL.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ Audit: *auditor, Log: api.Logger, Request: r, @@ -163,7 +163,7 @@ func (api *API) patchTemplateACL(rw http.ResponseWriter, r *http.Request) { } } - template, err = tx.UpdateTemplateACLByID(ctx, database.UpdateTemplateACLByIDParams{ + err = tx.UpdateTemplateACLByID(ctx, database.UpdateTemplateACLByIDParams{ ID: template.ID, UserACL: template.UserACL, GroupACL: template.GroupACL, @@ -171,6 +171,10 @@ func (api *API) patchTemplateACL(rw http.ResponseWriter, r *http.Request) { if err != nil { return xerrors.Errorf("update template ACL by ID: %w", err) } + template, err = tx.GetTemplateByID(ctx, template.ID) + if err != nil { + return xerrors.Errorf("get updated template by ID: %w", err) + } return nil }, nil) if err != nil { From b3d5199cfe4b580215e7b707f970ed4f8c35efa8 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Mon, 17 Jul 2023 15:17:38 -0400 Subject: [PATCH 03/16] Template -> TemplateWithUser --- cli/server.go | 1 - coderd/database/dbauthz/dbauthz.go | 35 +++++---- coderd/database/dbfake/dbfake.go | 73 ++++++++++++------ coderd/database/dbmetrics/dbmetrics.go | 26 +++---- coderd/database/dbmock/dbmock.go | 48 ++++++------ coderd/database/modelmethods.go | 2 +- coderd/database/modelqueries.go | 18 +++-- coderd/schedule/mock.go | 4 +- coderd/schedule/template.go | 45 ++++++++---- coderd/telemetry/telemetry.go | 2 +- coderd/templates.go | 98 +++++++------------------ coderd/templateversions.go | 2 +- coderd/workspaces.go | 8 +- coderd/wsbuilder/wsbuilder.go | 4 +- enterprise/coderd/provisionerdaemons.go | 41 +++++++---- 15 files changed, 208 insertions(+), 199 deletions(-) diff --git a/cli/server.go b/cli/server.go index 8a61ad221f250..ec9c049ecefaa 100644 --- a/cli/server.go +++ b/cli/server.go @@ -607,7 +607,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. options.Pubsub = pubsub.NewInMemory() } else { sqlDB, err := connectToPostgres(ctx, logger, sqlDriver, cfg.PostgresURL.String()) - if err != nil { return xerrors.Errorf("connect to postgres: %w", err) } diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 4d0e56beeab58..7d73ea768d900 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1808,9 +1808,12 @@ func (q *querier) InsertReplica(ctx context.Context, arg database.InsertReplicaP return q.db.InsertReplica(ctx, arg) } -func (q *querier) InsertTemplate(ctx context.Context, arg database.InsertTemplateParams) (error, error) { +func (q *querier) InsertTemplate(ctx context.Context, arg database.InsertTemplateParams) error { obj := rbac.ResourceTemplate.InOrg(arg.OrganizationID) - return insert(q.log, q.auth, obj, q.db.InsertTemplate)(ctx, arg) + if err := q.authorizeContext(ctx, rbac.ActionCreate, obj); err != nil { + return err + } + return q.db.InsertTemplate(ctx, arg) } func (q *querier) InsertTemplateVersion(ctx context.Context, arg database.InsertTemplateVersionParams) (database.TemplateVersion, error) { @@ -2134,17 +2137,17 @@ func (q *querier) UpdateReplica(ctx context.Context, arg database.UpdateReplicaP return q.db.UpdateReplica(ctx, arg) } -func (q *querier) UpdateTemplateACLByID(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (error, error) { - // UpdateTemplateACL uses the ActionCreate action. Only users that can create the template - // may update the ACL. - fetch := func(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (database.Template, error) { +func (q *querier) UpdateTemplateACLByID(ctx context.Context, arg database.UpdateTemplateACLByIDParams) error { + fetch := func(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (database.TemplateWithUser, error) { return q.db.GetTemplateByID(ctx, arg.ID) } - return fetchAndQuery(q.log, q.auth, rbac.ActionCreate, fetch, q.db.UpdateTemplateACLByID)(ctx, arg) + // UpdateTemplateACL uses the ActionCreate action. Only users that can create the template + // may update the ACL. + return fetchAndExec(q.log, q.auth, rbac.ActionCreate, fetch, q.db.UpdateTemplateACLByID)(ctx, arg) } func (q *querier) UpdateTemplateActiveVersionByID(ctx context.Context, arg database.UpdateTemplateActiveVersionByIDParams) error { - fetch := func(ctx context.Context, arg database.UpdateTemplateActiveVersionByIDParams) (database.Template, error) { + fetch := func(ctx context.Context, arg database.UpdateTemplateActiveVersionByIDParams) (database.TemplateWithUser, error) { return q.db.GetTemplateByID(ctx, arg.ID) } return update(q.log, q.auth, fetch, q.db.UpdateTemplateActiveVersionByID)(ctx, arg) @@ -2155,18 +2158,18 @@ func (q *querier) UpdateTemplateDeletedByID(ctx context.Context, arg database.Up return q.SoftDeleteTemplateByID(ctx, arg.ID) } -func (q *querier) UpdateTemplateMetaByID(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (error, error) { - fetch := func(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (database.Template, error) { +func (q *querier) UpdateTemplateMetaByID(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) error { + fetch := func(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (database.TemplateWithUser, error) { return q.db.GetTemplateByID(ctx, arg.ID) } - return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateTemplateMetaByID)(ctx, arg) + return update(q.log, q.auth, fetch, q.db.UpdateTemplateMetaByID)(ctx, arg) } -func (q *querier) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (error, error) { - fetch := func(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.Template, error) { +func (q *querier) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) error { + fetch := func(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.TemplateWithUser, error) { return q.db.GetTemplateByID(ctx, arg.ID) } - return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateTemplateScheduleByID)(ctx, arg) + return update(q.log, q.auth, fetch, q.db.UpdateTemplateScheduleByID)(ctx, arg) } func (q *querier) UpdateTemplateVersionByID(ctx context.Context, arg database.UpdateTemplateVersionByIDParams) (database.TemplateVersion, error) { @@ -2507,7 +2510,7 @@ func (q *querier) UpdateWorkspaceTTL(ctx context.Context, arg database.UpdateWor } func (q *querier) UpdateWorkspaceTTLToBeWithinTemplateMax(ctx context.Context, arg database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams) error { - fetch := func(ctx context.Context, arg database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams) (database.Template, error) { + fetch := func(ctx context.Context, arg database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams) (database.TemplateWithUser, error) { return q.db.GetTemplateByID(ctx, arg.TemplateID) } return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspaceTTLToBeWithinTemplateMax)(ctx, arg) @@ -2574,7 +2577,7 @@ func (q *querier) UpsertTailnetCoordinator(ctx context.Context, id uuid.UUID) (d return q.db.UpsertTailnetCoordinator(ctx, id) } -func (q *querier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, _ rbac.PreparedAuthorized) ([]database.Template, error) { +func (q *querier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, _ rbac.PreparedAuthorized) ([]database.TemplateWithUser, error) { // TODO Delete this function, all GetTemplates should be authorized. For now just call getTemplates on the authz querier. return q.GetTemplatesWithFilter(ctx, arg) } diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index b39af86e13b13..b4246bb749f89 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -443,13 +443,38 @@ func (q *FakeQuerier) getLatestWorkspaceBuildByWorkspaceIDNoLock(_ context.Conte return row, nil } -func (q *FakeQuerier) getTemplateByIDNoLock(_ context.Context, id uuid.UUID) (database.Template, error) { +func (q *FakeQuerier) getTemplateByIDNoLock(_ context.Context, id uuid.UUID) (database.TemplateWithUser, error) { for _, template := range q.templates { if template.ID == id { - return template.DeepCopy(), nil + return q.templateWithUser(template), nil } } - return database.Template{}, sql.ErrNoRows + return database.TemplateWithUser{}, sql.ErrNoRows +} + +func (q *FakeQuerier) templatesWithUser(tpl []database.Template) []database.TemplateWithUser { + cpy := make([]database.TemplateWithUser, 0, len(tpl)) + for _, t := range tpl { + cpy = append(cpy, q.templateWithUser(t)) + } + return cpy +} + +func (q *FakeQuerier) templateWithUser(tpl database.Template) database.TemplateWithUser { + var user database.User + for _, _user := range q.users { + if _user.ID == tpl.CreatedBy { + user = _user + break + } + } + var withUser database.TemplateWithUser + // This is a cheeky way to copy the fields over without explictly listing them all. + d, _ := json.Marshal(tpl) + _ = json.Unmarshal(d, &withUser) + withUser.CreatedByUsername = user.Username + withUser.CreatedByAvatarURL = user.AvatarURL.String + return withUser } func (q *FakeQuerier) getTemplateVersionByIDNoLock(_ context.Context, templateVersionID uuid.UUID) (database.TemplateVersion, error) { @@ -1853,7 +1878,7 @@ func (q *FakeQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (databa func (q *FakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.TemplateWithUser, error) { if err := validateDatabaseType(arg); err != nil { - return database.Template{}, err + return database.TemplateWithUser{}, err } q.mutex.RLock() @@ -1869,9 +1894,9 @@ func (q *FakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg da if template.Deleted != arg.Deleted { continue } - return template.DeepCopy(), nil + return q.templateWithUser(template), nil } - return database.Template{}, sql.ErrNoRows + return database.TemplateWithUser{}, sql.ErrNoRows } func (q *FakeQuerier) GetTemplateDAUs(_ context.Context, arg database.GetTemplateDAUsParams) ([]database.GetTemplateDAUsRow, error) { @@ -2102,7 +2127,7 @@ func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.TemplateWithUs return i.ID.String() < j.ID.String() }) - return templates, nil + return q.templatesWithUser(templates), nil } func (q *FakeQuerier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.TemplateWithUser, error) { @@ -3436,9 +3461,9 @@ func (q *FakeQuerier) InsertReplica(_ context.Context, arg database.InsertReplic return replica, nil } -func (q *FakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTemplateParams) (error, error) { +func (q *FakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTemplateParams) error { if err := validateDatabaseType(arg); err != nil { - return database.Template{}, err + return err } q.mutex.Lock() @@ -3464,7 +3489,7 @@ func (q *FakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTempl AllowUserAutostop: true, } q.templates = append(q.templates, template) - return template.DeepCopy(), nil + return nil } func (q *FakeQuerier) InsertTemplateVersion(_ context.Context, arg database.InsertTemplateVersionParams) (database.TemplateVersion, error) { @@ -4172,9 +4197,9 @@ func (q *FakeQuerier) UpdateReplica(_ context.Context, arg database.UpdateReplic return database.Replica{}, sql.ErrNoRows } -func (q *FakeQuerier) UpdateTemplateACLByID(_ context.Context, arg database.UpdateTemplateACLByIDParams) (error, error) { +func (q *FakeQuerier) UpdateTemplateACLByID(_ context.Context, arg database.UpdateTemplateACLByIDParams) error { if err := validateDatabaseType(arg); err != nil { - return database.Template{}, err + return err } q.mutex.Lock() @@ -4186,11 +4211,11 @@ func (q *FakeQuerier) UpdateTemplateACLByID(_ context.Context, arg database.Upda template.UserACL = arg.UserACL q.templates[i] = template - return template.DeepCopy(), nil + return nil } } - return database.Template{}, sql.ErrNoRows + return sql.ErrNoRows } func (q *FakeQuerier) UpdateTemplateActiveVersionByID(_ context.Context, arg database.UpdateTemplateActiveVersionByIDParams) error { @@ -4233,9 +4258,9 @@ func (q *FakeQuerier) UpdateTemplateDeletedByID(_ context.Context, arg database. return sql.ErrNoRows } -func (q *FakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.UpdateTemplateMetaByIDParams) (error, error) { +func (q *FakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.UpdateTemplateMetaByIDParams) error { if err := validateDatabaseType(arg); err != nil { - return database.Template{}, err + return err } q.mutex.Lock() @@ -4251,15 +4276,15 @@ func (q *FakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd tpl.Description = arg.Description tpl.Icon = arg.Icon q.templates[idx] = tpl - return tpl.DeepCopy(), nil + return nil } - return database.Template{}, sql.ErrNoRows + return sql.ErrNoRows } -func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database.UpdateTemplateScheduleByIDParams) (error, error) { +func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database.UpdateTemplateScheduleByIDParams) error { if err := validateDatabaseType(arg); err != nil { - return database.Template{}, err + return err } q.mutex.Lock() @@ -4278,10 +4303,10 @@ func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database tpl.InactivityTTL = arg.InactivityTTL tpl.LockedTTL = arg.LockedTTL q.templates[idx] = tpl - return tpl.DeepCopy(), nil + return nil } - return database.Template{}, sql.ErrNoRows + return sql.ErrNoRows } func (q *FakeQuerier) UpdateTemplateVersionByID(_ context.Context, arg database.UpdateTemplateVersionByIDParams) (database.TemplateVersion, error) { @@ -4967,7 +4992,7 @@ func (*FakeQuerier) UpsertTailnetCoordinator(context.Context, uuid.UUID) (databa return database.TailnetCoordinator{}, ErrUnimplemented } -func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.Template, error) { +func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.TemplateWithUser, error) { if err := validateDatabaseType(arg); err != nil { return nil, err } @@ -5021,7 +5046,7 @@ func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.G } return i.ID.String() < j.ID.String() }) - return templates, nil + return q.templatesWithUser(templates), nil } return nil, sql.ErrNoRows diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index ed1114f1fb234..bc2d0166ccd50 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -1103,11 +1103,11 @@ func (m metricsStore) InsertReplica(ctx context.Context, arg database.InsertRepl return replica, err } -func (m metricsStore) InsertTemplate(ctx context.Context, arg database.InsertTemplateParams) (error, error) { +func (m metricsStore) InsertTemplate(ctx context.Context, arg database.InsertTemplateParams) error { start := time.Now() - template, err := m.s.InsertTemplate(ctx, arg) + err := m.s.InsertTemplate(ctx, arg) m.queryLatencies.WithLabelValues("InsertTemplate").Observe(time.Since(start).Seconds()) - return template, err + return err } func (m metricsStore) InsertTemplateVersion(ctx context.Context, arg database.InsertTemplateVersionParams) (database.TemplateVersion, error) { @@ -1306,11 +1306,11 @@ func (m metricsStore) UpdateReplica(ctx context.Context, arg database.UpdateRepl return replica, err } -func (m metricsStore) UpdateTemplateACLByID(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (error, error) { +func (m metricsStore) UpdateTemplateACLByID(ctx context.Context, arg database.UpdateTemplateACLByIDParams) error { start := time.Now() - template, err := m.s.UpdateTemplateACLByID(ctx, arg) + err := m.s.UpdateTemplateACLByID(ctx, arg) m.queryLatencies.WithLabelValues("UpdateTemplateACLByID").Observe(time.Since(start).Seconds()) - return template, err + return err } func (m metricsStore) UpdateTemplateActiveVersionByID(ctx context.Context, arg database.UpdateTemplateActiveVersionByIDParams) error { @@ -1327,18 +1327,18 @@ func (m metricsStore) UpdateTemplateDeletedByID(ctx context.Context, arg databas return err } -func (m metricsStore) UpdateTemplateMetaByID(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (error, error) { +func (m metricsStore) UpdateTemplateMetaByID(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) error { start := time.Now() - template, err := m.s.UpdateTemplateMetaByID(ctx, arg) + err := m.s.UpdateTemplateMetaByID(ctx, arg) m.queryLatencies.WithLabelValues("UpdateTemplateMetaByID").Observe(time.Since(start).Seconds()) - return template, err + return err } -func (m metricsStore) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (error, error) { +func (m metricsStore) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) error { start := time.Now() - template, err := m.s.UpdateTemplateScheduleByID(ctx, arg) + err := m.s.UpdateTemplateScheduleByID(ctx, arg) m.queryLatencies.WithLabelValues("UpdateTemplateScheduleByID").Observe(time.Since(start).Seconds()) - return template, err + return err } func (m metricsStore) UpdateTemplateVersionByID(ctx context.Context, arg database.UpdateTemplateVersionByIDParams) (database.TemplateVersion, error) { @@ -1604,7 +1604,7 @@ func (m metricsStore) UpsertTailnetCoordinator(ctx context.Context, id uuid.UUID return m.s.UpsertTailnetCoordinator(ctx, id) } -func (m metricsStore) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.Template, error) { +func (m metricsStore) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.TemplateWithUser, error) { start := time.Now() templates, err := m.s.GetAuthorizedTemplates(ctx, arg, prepared) m.queryLatencies.WithLabelValues("GetAuthorizedTemplates").Observe(time.Since(start).Seconds()) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index f672a5e5dfc61..7d911b5961d4c 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -417,10 +417,10 @@ func (mr *MockStoreMockRecorder) GetAuthorizationUserRoles(arg0, arg1 interface{ } // GetAuthorizedTemplates mocks base method. -func (m *MockStore) GetAuthorizedTemplates(arg0 context.Context, arg1 database.GetTemplatesWithFilterParams, arg2 rbac.PreparedAuthorized) ([]database.Template, error) { +func (m *MockStore) GetAuthorizedTemplates(arg0 context.Context, arg1 database.GetTemplatesWithFilterParams, arg2 rbac.PreparedAuthorized) ([]database.TemplateWithUser, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAuthorizedTemplates", arg0, arg1, arg2) - ret0, _ := ret[0].([]database.Template) + ret0, _ := ret[0].([]database.TemplateWithUser) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1152,10 +1152,10 @@ func (mr *MockStoreMockRecorder) GetTemplateAverageBuildTime(arg0, arg1 interfac } // GetTemplateByID mocks base method. -func (m *MockStore) GetTemplateByID(arg0 context.Context, arg1 uuid.UUID) (database.Template, error) { +func (m *MockStore) GetTemplateByID(arg0 context.Context, arg1 uuid.UUID) (database.TemplateWithUser, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTemplateByID", arg0, arg1) - ret0, _ := ret[0].(database.Template) + ret0, _ := ret[0].(database.TemplateWithUser) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1167,10 +1167,10 @@ func (mr *MockStoreMockRecorder) GetTemplateByID(arg0, arg1 interface{}) *gomock } // GetTemplateByOrganizationAndName mocks base method. -func (m *MockStore) GetTemplateByOrganizationAndName(arg0 context.Context, arg1 database.GetTemplateByOrganizationAndNameParams) (database.Template, error) { +func (m *MockStore) GetTemplateByOrganizationAndName(arg0 context.Context, arg1 database.GetTemplateByOrganizationAndNameParams) (database.TemplateWithUser, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTemplateByOrganizationAndName", arg0, arg1) - ret0, _ := ret[0].(database.Template) + ret0, _ := ret[0].(database.TemplateWithUser) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1347,10 +1347,10 @@ func (mr *MockStoreMockRecorder) GetTemplateVersionsCreatedAfter(arg0, arg1 inte } // GetTemplates mocks base method. -func (m *MockStore) GetTemplates(arg0 context.Context) ([]database.Template, error) { +func (m *MockStore) GetTemplates(arg0 context.Context) ([]database.TemplateWithUser, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTemplates", arg0) - ret0, _ := ret[0].([]database.Template) + ret0, _ := ret[0].([]database.TemplateWithUser) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1362,10 +1362,10 @@ func (mr *MockStoreMockRecorder) GetTemplates(arg0 interface{}) *gomock.Call { } // GetTemplatesWithFilter mocks base method. -func (m *MockStore) GetTemplatesWithFilter(arg0 context.Context, arg1 database.GetTemplatesWithFilterParams) ([]database.Template, error) { +func (m *MockStore) GetTemplatesWithFilter(arg0 context.Context, arg1 database.GetTemplatesWithFilterParams) ([]database.TemplateWithUser, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTemplatesWithFilter", arg0, arg1) - ret0, _ := ret[0].([]database.Template) + ret0, _ := ret[0].([]database.TemplateWithUser) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -2318,12 +2318,11 @@ func (mr *MockStoreMockRecorder) InsertReplica(arg0, arg1 interface{}) *gomock.C } // InsertTemplate mocks base method. -func (m *MockStore) InsertTemplate(arg0 context.Context, arg1 database.InsertTemplateParams) (database.Template, error) { +func (m *MockStore) InsertTemplate(arg0 context.Context, arg1 database.InsertTemplateParams) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InsertTemplate", arg0, arg1) - ret0, _ := ret[0].(database.Template) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret0, _ := ret[0].(error) + return ret0 } // InsertTemplate indicates an expected call of InsertTemplate. @@ -2761,12 +2760,11 @@ func (mr *MockStoreMockRecorder) UpdateReplica(arg0, arg1 interface{}) *gomock.C } // UpdateTemplateACLByID mocks base method. -func (m *MockStore) UpdateTemplateACLByID(arg0 context.Context, arg1 database.UpdateTemplateACLByIDParams) (database.Template, error) { +func (m *MockStore) UpdateTemplateACLByID(arg0 context.Context, arg1 database.UpdateTemplateACLByIDParams) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTemplateACLByID", arg0, arg1) - ret0, _ := ret[0].(database.Template) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret0, _ := ret[0].(error) + return ret0 } // UpdateTemplateACLByID indicates an expected call of UpdateTemplateACLByID. @@ -2804,12 +2802,11 @@ func (mr *MockStoreMockRecorder) UpdateTemplateDeletedByID(arg0, arg1 interface{ } // UpdateTemplateMetaByID mocks base method. -func (m *MockStore) UpdateTemplateMetaByID(arg0 context.Context, arg1 database.UpdateTemplateMetaByIDParams) (database.Template, error) { +func (m *MockStore) UpdateTemplateMetaByID(arg0 context.Context, arg1 database.UpdateTemplateMetaByIDParams) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTemplateMetaByID", arg0, arg1) - ret0, _ := ret[0].(database.Template) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret0, _ := ret[0].(error) + return ret0 } // UpdateTemplateMetaByID indicates an expected call of UpdateTemplateMetaByID. @@ -2819,12 +2816,11 @@ func (mr *MockStoreMockRecorder) UpdateTemplateMetaByID(arg0, arg1 interface{}) } // UpdateTemplateScheduleByID mocks base method. -func (m *MockStore) UpdateTemplateScheduleByID(arg0 context.Context, arg1 database.UpdateTemplateScheduleByIDParams) (database.Template, error) { +func (m *MockStore) UpdateTemplateScheduleByID(arg0 context.Context, arg1 database.UpdateTemplateScheduleByIDParams) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTemplateScheduleByID", arg0, arg1) - ret0, _ := ret[0].(database.Template) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret0, _ := ret[0].(error) + return ret0 } // UpdateTemplateScheduleByID indicates an expected call of UpdateTemplateScheduleByID. diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 0739e32faee5c..ca70fbb3e9fad 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -130,7 +130,7 @@ func (t Template) DeepCopy() Template { return cpy } -func (TemplateVersion) RBACObject(template Template) rbac.Object { +func (TemplateVersion) RBACObject(template TemplateWithUser) rbac.Object { // Just use the parent template resource for controlling versions return template.RBACObject() } diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index a7f186b668b0a..49013e7e82dfc 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -27,12 +27,12 @@ type customQuerier interface { } type templateQuerier interface { - GetAuthorizedTemplates(ctx context.Context, arg GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]Template, error) + GetAuthorizedTemplates(ctx context.Context, arg GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]TemplateWithUser, error) GetTemplateGroupRoles(ctx context.Context, id uuid.UUID) ([]TemplateGroup, error) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]TemplateUser, error) } -func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]Template, error) { +func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]TemplateWithUser, error) { authorizedFilter, err := prepared.CompileToSQL(ctx, regosql.ConvertConfig{ VariableConverter: regosql.TemplateConverter(), }) @@ -54,12 +54,12 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate pq.Array(arg.IDs), ) if err != nil { - return nil, xerrors.Errorf("query context: %w", err) + return nil, err } defer rows.Close() - var items []Template + var items []TemplateWithUser for rows.Next() { - var i Template + var i TemplateWithUser if err := rows.Scan( &i.ID, &i.CreatedAt, @@ -83,16 +83,18 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate &i.FailureTTL, &i.InactivityTTL, &i.LockedTTL, + &i.CreatedByAvatarURL, + &i.CreatedByUsername, ); err != nil { - return nil, xerrors.Errorf("scan: %w", err) + return nil, err } items = append(items, i) } if err := rows.Close(); err != nil { - return nil, xerrors.Errorf("close: %w", err) + return nil, err } if err := rows.Err(); err != nil { - return nil, xerrors.Errorf("rows err: %w", err) + return nil, err } return items, nil } diff --git a/coderd/schedule/mock.go b/coderd/schedule/mock.go index 5c3c1e77ed803..68cf778b4a49e 100644 --- a/coderd/schedule/mock.go +++ b/coderd/schedule/mock.go @@ -10,7 +10,7 @@ import ( type MockTemplateScheduleStore struct { GetFn func(ctx context.Context, db database.Store, templateID uuid.UUID) (TemplateScheduleOptions, error) - SetFn func(ctx context.Context, db database.Store, template database.Template, options TemplateScheduleOptions) (database.Template, error) + SetFn func(ctx context.Context, db database.Store, template database.TemplateWithUser, options TemplateScheduleOptions) (database.TemplateWithUser, error) } var _ TemplateScheduleStore = MockTemplateScheduleStore{} @@ -23,7 +23,7 @@ func (m MockTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.Contex return NewAGPLTemplateScheduleStore().GetTemplateScheduleOptions(ctx, db, templateID) } -func (m MockTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, template database.Template, options TemplateScheduleOptions) (database.Template, error) { +func (m MockTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, template database.TemplateWithUser, options TemplateScheduleOptions) (database.TemplateWithUser, error) { if m.SetFn != nil { return m.SetFn(ctx, db, template, options) } diff --git a/coderd/schedule/template.go b/coderd/schedule/template.go index 12e87aac16527..43c2a6b183124 100644 --- a/coderd/schedule/template.go +++ b/coderd/schedule/template.go @@ -30,7 +30,7 @@ type TemplateScheduleOptions struct { // scheduling options set by the template/site admin. type TemplateScheduleStore interface { GetTemplateScheduleOptions(ctx context.Context, db database.Store, templateID uuid.UUID) (TemplateScheduleOptions, error) - SetTemplateScheduleOptions(ctx context.Context, db database.Store, template database.Template, opts TemplateScheduleOptions) (database.Template, error) + SetTemplateScheduleOptions(ctx context.Context, db database.Store, template database.TemplateWithUser, opts TemplateScheduleOptions) (database.TemplateWithUser, error) } type agplTemplateScheduleStore struct{} @@ -62,23 +62,38 @@ func (*agplTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.Context }, nil } -func (*agplTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.Template, opts TemplateScheduleOptions) (database.Template, error) { +func (*agplTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.TemplateWithUser, opts TemplateScheduleOptions) (database.TemplateWithUser, error) { if int64(opts.DefaultTTL) == tpl.DefaultTTL { // Avoid updating the UpdatedAt timestamp if nothing will be changed. return tpl, nil } - return db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ - ID: tpl.ID, - UpdatedAt: database.Now(), - DefaultTTL: int64(opts.DefaultTTL), - // Don't allow changing it, but keep the value in the DB (to avoid - // clearing settings if the license has an issue). - AllowUserAutostart: tpl.AllowUserAutostart, - AllowUserAutostop: tpl.AllowUserAutostop, - MaxTTL: tpl.MaxTTL, - FailureTTL: tpl.FailureTTL, - InactivityTTL: tpl.InactivityTTL, - LockedTTL: tpl.LockedTTL, - }) + var template database.TemplateWithUser + err := db.InTx(func(db database.Store) error { + err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ + ID: tpl.ID, + UpdatedAt: database.Now(), + DefaultTTL: int64(opts.DefaultTTL), + // Don't allow changing it, but keep the value in the DB (to avoid + // clearing settings if the license has an issue). + AllowUserAutostart: tpl.AllowUserAutostart, + AllowUserAutostop: tpl.AllowUserAutostop, + MaxTTL: tpl.MaxTTL, + FailureTTL: tpl.FailureTTL, + InactivityTTL: tpl.InactivityTTL, + LockedTTL: tpl.LockedTTL, + }) + if err != nil { + return err + } + + template, err = db.GetTemplateByID(ctx, tpl.ID) + if err != nil { + return err + } + + return nil + }, nil) + + return template, err } diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index ced52a58fd273..62475604abcb4 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -629,7 +629,7 @@ func ConvertUser(dbUser database.User) User { } // ConvertTemplate anonymizes a template. -func ConvertTemplate(dbTemplate database.Template) Template { +func ConvertTemplate(dbTemplate database.TemplateWithUser) Template { return Template{ ID: dbTemplate.ID, CreatedBy: dbTemplate.CreatedBy, diff --git a/coderd/templates.go b/coderd/templates.go index 3404a3ee16677..f3d32b40f4188 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -1,7 +1,6 @@ package coderd import ( - "context" "database/sql" "errors" "fmt" @@ -40,16 +39,7 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() template := httpmw.TemplateParam(r) - createdByNameMap, err := getCreatedByNamesByTemplateIDs(ctx, api.Database, []database.Template{template}) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching creator name.", - Detail: err.Error(), - }) - return - } - - httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplate(template, createdByNameMap[template.ID.String()])) + httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplate(template)) } // @Summary Delete template by ID @@ -65,7 +55,7 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) { ctx = r.Context() template = httpmw.TemplateParam(r) auditor = *api.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ Audit: auditor, Log: api.Logger, Request: r, @@ -131,7 +121,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque organization = httpmw.OrganizationParam(r) apiKey = httpmw.APIKey(r) auditor = *api.Auditor.Load() - templateAudit, commitTemplateAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ + templateAudit, commitTemplateAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ Audit: auditor, Log: api.Logger, Request: r, @@ -154,7 +144,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque // Make a temporary struct to represent the template. This is used for // auditing if any of the following checks fail. It will be overwritten when // the template is inserted into the db. - templateAudit.New = database.Template{ + templateAudit.New = database.TemplateWithUser{ OrganizationID: organization.ID, Name: createTemplate.Name, Description: createTemplate.Description, @@ -274,7 +264,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque } var ( - dbTemplate database.Template + dbTemplate database.TemplateWithUser template codersdk.Template allowUserCancelWorkspaceJobs = ptr.NilToDefault(createTemplate.AllowUserCancelWorkspaceJobs, false) @@ -290,7 +280,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque } err = api.Database.InTx(func(tx database.Store) error { now := database.Now() - dbTemplate, err = tx.InsertTemplate(ctx, database.InsertTemplateParams{ + err = tx.InsertTemplate(ctx, database.InsertTemplateParams{ ID: uuid.New(), CreatedAt: now, UpdatedAt: now, @@ -310,6 +300,11 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque return xerrors.Errorf("insert template: %s", err) } + dbTemplate, err = tx.GetTemplateByID(ctx, dbTemplate.ID) + if err != nil { + return xerrors.Errorf("get template by id: %s", err) + } + dbTemplate, err = (*api.TemplateScheduleStore.Load()).SetTemplateScheduleOptions(ctx, tx, dbTemplate, schedule.TemplateScheduleOptions{ UserAutostartEnabled: allowUserAutostart, UserAutostopEnabled: allowUserAutostop, @@ -348,12 +343,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque } templateVersionAudit.New = newTemplateVersion - createdByNameMap, err := getCreatedByNamesByTemplateIDs(ctx, tx, []database.Template{dbTemplate}) - if err != nil { - return xerrors.Errorf("get creator name: %w", err) - } - - template = api.convertTemplate(dbTemplate, createdByNameMap[dbTemplate.ID.String()]) + template = api.convertTemplate(dbTemplate) return nil }, nil) if err != nil { @@ -409,16 +399,7 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request) return } - createdByNameMap, err := getCreatedByNamesByTemplateIDs(ctx, api.Database, templates) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching creator names.", - Detail: err.Error(), - }) - return - } - - httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplates(templates, createdByNameMap)) + httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplates(templates)) } // @Summary Get templates by organization and template name @@ -451,16 +432,7 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re return } - createdByNameMap, err := getCreatedByNamesByTemplateIDs(ctx, api.Database, []database.Template{template}) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching creator name.", - Detail: err.Error(), - }) - return - } - - httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplate(template, createdByNameMap[template.ID.String()])) + httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplate(template)) } // @Summary Update template metadata by ID @@ -476,7 +448,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { ctx = r.Context() template = httpmw.TemplateParam(r) auditor = *api.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ Audit: auditor, Log: api.Logger, Request: r, @@ -522,7 +494,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { return } - var updated database.Template + var updated database.TemplateWithUser err := api.Database.InTx(func(tx database.Store) error { if req.Name == template.Name && req.Description == template.Description && @@ -546,7 +518,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { } var err error - updated, err = tx.UpdateTemplateMetaByID(ctx, database.UpdateTemplateMetaByIDParams{ + err = tx.UpdateTemplateMetaByID(ctx, database.UpdateTemplateMetaByIDParams{ ID: template.ID, UpdatedAt: database.Now(), Name: name, @@ -559,6 +531,11 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { return xerrors.Errorf("update template metadata: %w", err) } + updated, err = tx.GetTemplateByID(ctx, template.ID) + if err != nil { + return xerrors.Errorf("fetch updated template metadata: %w", err) + } + defaultTTL := time.Duration(req.DefaultTTLMillis) * time.Millisecond maxTTL := time.Duration(req.MaxTTLMillis) * time.Millisecond failureTTL := time.Duration(req.FailureTTLMillis) * time.Millisecond @@ -603,16 +580,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { } aReq.New = updated - createdByNameMap, err := getCreatedByNamesByTemplateIDs(ctx, api.Database, []database.Template{updated}) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching creator name.", - Detail: err.Error(), - }) - return - } - - httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplate(updated, createdByNameMap[updated.ID.String()])) + httpapi.Write(ctx, rw, http.StatusOK, api.convertTemplate(updated)) } // @Summary Get template DAUs by ID @@ -680,23 +648,11 @@ func (api *API) templateExamples(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, ex) } -func getCreatedByNamesByTemplateIDs(ctx context.Context, db database.Store, templates []database.Template) (map[string]string, error) { - creators := make(map[string]string, len(templates)) - for _, template := range templates { - creator, err := db.GetUserByID(ctx, template.CreatedBy) - if err != nil { - return map[string]string{}, err - } - creators[template.ID.String()] = creator.Username - } - return creators, nil -} - -func (api *API) convertTemplates(templates []database.Template, createdByNameMap map[string]string) []codersdk.Template { +func (api *API) convertTemplates(templates []database.TemplateWithUser) []codersdk.Template { apiTemplates := make([]codersdk.Template, 0, len(templates)) for _, template := range templates { - apiTemplates = append(apiTemplates, api.convertTemplate(template, createdByNameMap[template.ID.String()])) + apiTemplates = append(apiTemplates, api.convertTemplate(template)) } // Sort templates by ActiveUserCount DESC @@ -708,7 +664,7 @@ func (api *API) convertTemplates(templates []database.Template, createdByNameMap } func (api *API) convertTemplate( - template database.Template, createdByName string, + template database.TemplateWithUser, ) codersdk.Template { activeCount, _ := api.metricsCache.TemplateUniqueUsers(template.ID) @@ -730,7 +686,7 @@ func (api *API) convertTemplate( DefaultTTLMillis: time.Duration(template.DefaultTTL).Milliseconds(), MaxTTLMillis: time.Duration(template.MaxTTL).Milliseconds(), CreatedByID: template.CreatedBy, - CreatedByName: createdByName, + CreatedByName: template.CreatedByUsername, AllowUserAutostart: template.AllowUserAutostart, AllowUserAutostop: template.AllowUserAutostop, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 13fc4420fc7e9..fa543557ce7a6 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -1043,7 +1043,7 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque ctx = r.Context() template = httpmw.TemplateParam(r) auditor = *api.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ Audit: auditor, Log: api.Logger, Request: r, diff --git a/coderd/workspaces.go b/coderd/workspaces.go index fe26804c3c78e..dc5d021e07745 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -1000,7 +1000,7 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { } type workspaceData struct { - templates []database.Template + templates []database.TemplateWithUser builds []codersdk.WorkspaceBuild users []database.User } @@ -1057,7 +1057,7 @@ func convertWorkspaces(workspaces []database.Workspace, data workspaceData) ([]c for _, workspaceBuild := range data.builds { buildByWorkspaceID[workspaceBuild.WorkspaceID] = workspaceBuild } - templateByID := map[uuid.UUID]database.Template{} + templateByID := map[uuid.UUID]database.TemplateWithUser{} for _, template := range data.templates { templateByID[template.ID] = template } @@ -1094,7 +1094,7 @@ func convertWorkspaces(workspaces []database.Workspace, data workspaceData) ([]c func convertWorkspace( workspace database.Workspace, workspaceBuild codersdk.WorkspaceBuild, - template database.Template, + template database.TemplateWithUser, owner *database.User, ) codersdk.Workspace { var autostartSchedule *string @@ -1159,7 +1159,7 @@ func convertWorkspaceTTLMillis(i sql.NullInt64) *int64 { // Calculate the time of the upcoming workspace deletion, if applicable; otherwise, return nil. // Workspaces may have impending deletions if InactivityTTL feature is turned on and the workspace is inactive. -func calculateDeletingAt(workspace database.Workspace, template database.Template, build codersdk.WorkspaceBuild) *time.Time { +func calculateDeletingAt(workspace database.Workspace, template database.TemplateWithUser, build codersdk.WorkspaceBuild) *time.Time { inactiveStatuses := []codersdk.WorkspaceStatus{codersdk.WorkspaceStatusStopped, codersdk.WorkspaceStatusCanceled, codersdk.WorkspaceStatusFailed, codersdk.WorkspaceStatusDeleted} isInactive := slices.Contains(inactiveStatuses, build.Status) // If InactivityTTL is turned off (set to 0) or if the workspace is active, there is no impending deletion diff --git a/coderd/wsbuilder/wsbuilder.go b/coderd/wsbuilder/wsbuilder.go index 7f6d840f9db6d..3ebbc3c31620d 100644 --- a/coderd/wsbuilder/wsbuilder.go +++ b/coderd/wsbuilder/wsbuilder.go @@ -50,7 +50,7 @@ type Builder struct { store database.Store // cache of objects, so we only fetch once - template *database.Template + template *database.TemplateWithUser templateVersion *database.TemplateVersion templateVersionJob *database.ProvisionerJob templateVersionParameters *[]database.TemplateVersionParameter @@ -368,7 +368,7 @@ func (b *Builder) buildTx(authFunc func(action rbac.Action, object rbac.Objecter return &workspaceBuild, &provisionerJob, nil } -func (b *Builder) getTemplate() (*database.Template, error) { +func (b *Builder) getTemplate() (*database.TemplateWithUser, error) { if b.template != nil { return b.template, nil } diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index 5bb35bdbd3b22..5099371b98ced 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -331,7 +331,7 @@ func (*EnterpriseTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.C }, nil } -func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.Template, opts schedule.TemplateScheduleOptions) (database.Template, error) { +func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.TemplateWithUser, opts schedule.TemplateScheduleOptions) (database.TemplateWithUser, error) { if int64(opts.DefaultTTL) == tpl.DefaultTTL && int64(opts.MaxTTL) == tpl.MaxTTL && int64(opts.FailureTTL) == tpl.FailureTTL && @@ -343,19 +343,32 @@ func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.C return tpl, nil } - template, err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ - ID: tpl.ID, - UpdatedAt: database.Now(), - AllowUserAutostart: opts.UserAutostartEnabled, - AllowUserAutostop: opts.UserAutostopEnabled, - DefaultTTL: int64(opts.DefaultTTL), - MaxTTL: int64(opts.MaxTTL), - FailureTTL: int64(opts.FailureTTL), - InactivityTTL: int64(opts.InactivityTTL), - LockedTTL: int64(opts.LockedTTL), - }) + var template database.TemplateWithUser + err := db.InTx(func(db database.Store) error { + err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ + ID: tpl.ID, + UpdatedAt: database.Now(), + AllowUserAutostart: opts.UserAutostartEnabled, + AllowUserAutostop: opts.UserAutostopEnabled, + DefaultTTL: int64(opts.DefaultTTL), + MaxTTL: int64(opts.MaxTTL), + FailureTTL: int64(opts.FailureTTL), + InactivityTTL: int64(opts.InactivityTTL), + LockedTTL: int64(opts.LockedTTL), + }) + if err != nil { + return xerrors.Errorf("update template schedule: %w", err) + } + + template, err = db.GetTemplateByID(ctx, tpl.ID) + if err != nil { + return xerrors.Errorf("get updated template schedule: %w", err) + } + + return nil + }, nil) if err != nil { - return database.Template{}, xerrors.Errorf("update template schedule: %w", err) + return database.TemplateWithUser{}, err } // Update all workspaces using the template to set the user defined schedule @@ -374,7 +387,7 @@ func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.C TemplateMaxTTL: int64(opts.MaxTTL), }) if err != nil { - return database.Template{}, xerrors.Errorf("update TTL of all workspaces on template to be within new template max TTL: %w", err) + return database.TemplateWithUser{}, xerrors.Errorf("update TTL of all workspaces on template to be within new template max TTL: %w", err) } } From 1eaa49f243858d6044ba745c5e1178532bfa5512 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 18 Jul 2023 12:31:03 -0400 Subject: [PATCH 04/16] TemplateWithUser -> Template --- coderd/audit/diff.go | 2 +- coderd/database/dbauthz/dbauthz.go | 20 ++++----- coderd/database/dbfake/dbfake.go | 26 ++++++------ coderd/database/dbmetrics/dbmetrics.go | 10 ++--- coderd/database/dbmock/dbmock.go | 20 ++++----- coderd/database/modelmethods.go | 4 +- coderd/database/modelqueries.go | 8 ++-- coderd/database/models.go | 56 ++++++++++++------------- coderd/database/querier.go | 8 ++-- coderd/database/queries.sql.go | 22 +++++----- coderd/database/queries/templates.sql | 2 +- coderd/database/sqlc.yaml | 5 +++ coderd/httpmw/templateparam.go | 4 +- coderd/schedule/mock.go | 4 +- coderd/schedule/template.go | 6 +-- coderd/telemetry/telemetry.go | 2 +- coderd/templates.go | 16 +++---- coderd/templateversions.go | 2 +- coderd/workspaces.go | 8 ++-- coderd/wsbuilder/wsbuilder.go | 4 +- enterprise/audit/table.go | 2 +- enterprise/coderd/provisionerdaemons.go | 8 ++-- enterprise/coderd/templates.go | 2 +- 23 files changed, 123 insertions(+), 118 deletions(-) diff --git a/coderd/audit/diff.go b/coderd/audit/diff.go index 65947c6ae1111..858334493cf8d 100644 --- a/coderd/audit/diff.go +++ b/coderd/audit/diff.go @@ -9,7 +9,7 @@ import ( // AuditableResources, then add it to this interface. type Auditable interface { database.APIKey | - database.TemplateWithUser | + database.Template | database.TemplateVersion | database.User | database.Workspace | diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 7d73ea768d900..3cc557331d4a5 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1157,11 +1157,11 @@ func (q *querier) GetTemplateAverageBuildTime(ctx context.Context, arg database. return q.db.GetTemplateAverageBuildTime(ctx, arg) } -func (q *querier) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.TemplateWithUser, error) { +func (q *querier) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.Template, error) { return fetch(q.log, q.auth, q.db.GetTemplateByID)(ctx, id) } -func (q *querier) GetTemplateByOrganizationAndName(ctx context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.TemplateWithUser, error) { +func (q *querier) GetTemplateByOrganizationAndName(ctx context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.Template, error) { return fetch(q.log, q.auth, q.db.GetTemplateByOrganizationAndName)(ctx, arg) } @@ -1302,14 +1302,14 @@ func (q *querier) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt return q.db.GetTemplateVersionsCreatedAfter(ctx, createdAt) } -func (q *querier) GetTemplates(ctx context.Context) ([]database.TemplateWithUser, error) { +func (q *querier) GetTemplates(ctx context.Context) ([]database.Template, error) { if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil { return nil, err } return q.db.GetTemplates(ctx) } -func (q *querier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.TemplateWithUser, error) { +func (q *querier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.Template, error) { prep, err := prepareSQLFilter(ctx, q.auth, rbac.ActionRead, rbac.ResourceTemplate.Type) if err != nil { return nil, xerrors.Errorf("(dev error) prepare sql filter: %w", err) @@ -2138,7 +2138,7 @@ func (q *querier) UpdateReplica(ctx context.Context, arg database.UpdateReplicaP } func (q *querier) UpdateTemplateACLByID(ctx context.Context, arg database.UpdateTemplateACLByIDParams) error { - fetch := func(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (database.TemplateWithUser, error) { + fetch := func(ctx context.Context, arg database.UpdateTemplateACLByIDParams) (database.Template, error) { return q.db.GetTemplateByID(ctx, arg.ID) } // UpdateTemplateACL uses the ActionCreate action. Only users that can create the template @@ -2147,7 +2147,7 @@ func (q *querier) UpdateTemplateACLByID(ctx context.Context, arg database.Update } func (q *querier) UpdateTemplateActiveVersionByID(ctx context.Context, arg database.UpdateTemplateActiveVersionByIDParams) error { - fetch := func(ctx context.Context, arg database.UpdateTemplateActiveVersionByIDParams) (database.TemplateWithUser, error) { + fetch := func(ctx context.Context, arg database.UpdateTemplateActiveVersionByIDParams) (database.Template, error) { return q.db.GetTemplateByID(ctx, arg.ID) } return update(q.log, q.auth, fetch, q.db.UpdateTemplateActiveVersionByID)(ctx, arg) @@ -2159,14 +2159,14 @@ func (q *querier) UpdateTemplateDeletedByID(ctx context.Context, arg database.Up } func (q *querier) UpdateTemplateMetaByID(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) error { - fetch := func(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (database.TemplateWithUser, error) { + fetch := func(ctx context.Context, arg database.UpdateTemplateMetaByIDParams) (database.Template, error) { return q.db.GetTemplateByID(ctx, arg.ID) } return update(q.log, q.auth, fetch, q.db.UpdateTemplateMetaByID)(ctx, arg) } func (q *querier) UpdateTemplateScheduleByID(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) error { - fetch := func(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.TemplateWithUser, error) { + fetch := func(ctx context.Context, arg database.UpdateTemplateScheduleByIDParams) (database.Template, error) { return q.db.GetTemplateByID(ctx, arg.ID) } return update(q.log, q.auth, fetch, q.db.UpdateTemplateScheduleByID)(ctx, arg) @@ -2510,7 +2510,7 @@ func (q *querier) UpdateWorkspaceTTL(ctx context.Context, arg database.UpdateWor } func (q *querier) UpdateWorkspaceTTLToBeWithinTemplateMax(ctx context.Context, arg database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams) error { - fetch := func(ctx context.Context, arg database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams) (database.TemplateWithUser, error) { + fetch := func(ctx context.Context, arg database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams) (database.Template, error) { return q.db.GetTemplateByID(ctx, arg.TemplateID) } return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspaceTTLToBeWithinTemplateMax)(ctx, arg) @@ -2577,7 +2577,7 @@ func (q *querier) UpsertTailnetCoordinator(ctx context.Context, id uuid.UUID) (d return q.db.UpsertTailnetCoordinator(ctx, id) } -func (q *querier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, _ rbac.PreparedAuthorized) ([]database.TemplateWithUser, error) { +func (q *querier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, _ rbac.PreparedAuthorized) ([]database.Template, error) { // TODO Delete this function, all GetTemplates should be authorized. For now just call getTemplates on the authz querier. return q.GetTemplatesWithFilter(ctx, arg) } diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index b4246bb749f89..a214cbc4ffb9b 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -443,24 +443,24 @@ func (q *FakeQuerier) getLatestWorkspaceBuildByWorkspaceIDNoLock(_ context.Conte return row, nil } -func (q *FakeQuerier) getTemplateByIDNoLock(_ context.Context, id uuid.UUID) (database.TemplateWithUser, error) { +func (q *FakeQuerier) getTemplateByIDNoLock(_ context.Context, id uuid.UUID) (database.Template, error) { for _, template := range q.templates { if template.ID == id { return q.templateWithUser(template), nil } } - return database.TemplateWithUser{}, sql.ErrNoRows + return database.Template{}, sql.ErrNoRows } -func (q *FakeQuerier) templatesWithUser(tpl []database.Template) []database.TemplateWithUser { - cpy := make([]database.TemplateWithUser, 0, len(tpl)) +func (q *FakeQuerier) templatesWithUser(tpl []database.Template) []database.Template { + cpy := make([]database.Template, 0, len(tpl)) for _, t := range tpl { cpy = append(cpy, q.templateWithUser(t)) } return cpy } -func (q *FakeQuerier) templateWithUser(tpl database.Template) database.TemplateWithUser { +func (q *FakeQuerier) templateWithUser(tpl database.Template) database.Template { var user database.User for _, _user := range q.users { if _user.ID == tpl.CreatedBy { @@ -468,7 +468,7 @@ func (q *FakeQuerier) templateWithUser(tpl database.Template) database.TemplateW break } } - var withUser database.TemplateWithUser + var withUser database.Template // This is a cheeky way to copy the fields over without explictly listing them all. d, _ := json.Marshal(tpl) _ = json.Unmarshal(d, &withUser) @@ -1869,16 +1869,16 @@ func (q *FakeQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg datab return row, nil } -func (q *FakeQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.TemplateWithUser, error) { +func (q *FakeQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.Template, error) { q.mutex.RLock() defer q.mutex.RUnlock() return q.getTemplateByIDNoLock(ctx, id) } -func (q *FakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.TemplateWithUser, error) { +func (q *FakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.Template, error) { if err := validateDatabaseType(arg); err != nil { - return database.TemplateWithUser{}, err + return database.Template{}, err } q.mutex.RLock() @@ -1896,7 +1896,7 @@ func (q *FakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg da } return q.templateWithUser(template), nil } - return database.TemplateWithUser{}, sql.ErrNoRows + return database.Template{}, sql.ErrNoRows } func (q *FakeQuerier) GetTemplateDAUs(_ context.Context, arg database.GetTemplateDAUsParams) ([]database.GetTemplateDAUsRow, error) { @@ -2112,7 +2112,7 @@ func (q *FakeQuerier) GetTemplateVersionsCreatedAfter(_ context.Context, after t return versions, nil } -func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.TemplateWithUser, error) { +func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.Template, error) { q.mutex.RLock() defer q.mutex.RUnlock() @@ -2130,7 +2130,7 @@ func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.TemplateWithUs return q.templatesWithUser(templates), nil } -func (q *FakeQuerier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.TemplateWithUser, error) { +func (q *FakeQuerier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.Template, error) { if err := validateDatabaseType(arg); err != nil { return nil, err } @@ -4992,7 +4992,7 @@ func (*FakeQuerier) UpsertTailnetCoordinator(context.Context, uuid.UUID) (databa return database.TailnetCoordinator{}, ErrUnimplemented } -func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.TemplateWithUser, error) { +func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.Template, error) { if err := validateDatabaseType(arg); err != nil { return nil, err } diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index bc2d0166ccd50..934f27ebcbbb6 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -578,14 +578,14 @@ func (m metricsStore) GetTemplateAverageBuildTime(ctx context.Context, arg datab return buildTime, err } -func (m metricsStore) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.TemplateWithUser, error) { +func (m metricsStore) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.Template, error) { start := time.Now() template, err := m.s.GetTemplateByID(ctx, id) m.queryLatencies.WithLabelValues("GetTemplateByID").Observe(time.Since(start).Seconds()) return template, err } -func (m metricsStore) GetTemplateByOrganizationAndName(ctx context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.TemplateWithUser, error) { +func (m metricsStore) GetTemplateByOrganizationAndName(ctx context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.Template, error) { start := time.Now() template, err := m.s.GetTemplateByOrganizationAndName(ctx, arg) m.queryLatencies.WithLabelValues("GetTemplateByOrganizationAndName").Observe(time.Since(start).Seconds()) @@ -655,14 +655,14 @@ func (m metricsStore) GetTemplateVersionsCreatedAfter(ctx context.Context, creat return versions, err } -func (m metricsStore) GetTemplates(ctx context.Context) ([]database.TemplateWithUser, error) { +func (m metricsStore) GetTemplates(ctx context.Context) ([]database.Template, error) { start := time.Now() templates, err := m.s.GetTemplates(ctx) m.queryLatencies.WithLabelValues("GetTemplates").Observe(time.Since(start).Seconds()) return templates, err } -func (m metricsStore) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.TemplateWithUser, error) { +func (m metricsStore) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.Template, error) { start := time.Now() templates, err := m.s.GetTemplatesWithFilter(ctx, arg) m.queryLatencies.WithLabelValues("GetTemplatesWithFilter").Observe(time.Since(start).Seconds()) @@ -1604,7 +1604,7 @@ func (m metricsStore) UpsertTailnetCoordinator(ctx context.Context, id uuid.UUID return m.s.UpsertTailnetCoordinator(ctx, id) } -func (m metricsStore) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.TemplateWithUser, error) { +func (m metricsStore) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.Template, error) { start := time.Now() templates, err := m.s.GetAuthorizedTemplates(ctx, arg, prepared) m.queryLatencies.WithLabelValues("GetAuthorizedTemplates").Observe(time.Since(start).Seconds()) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index 7d911b5961d4c..fde51b9fead0d 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -417,10 +417,10 @@ func (mr *MockStoreMockRecorder) GetAuthorizationUserRoles(arg0, arg1 interface{ } // GetAuthorizedTemplates mocks base method. -func (m *MockStore) GetAuthorizedTemplates(arg0 context.Context, arg1 database.GetTemplatesWithFilterParams, arg2 rbac.PreparedAuthorized) ([]database.TemplateWithUser, error) { +func (m *MockStore) GetAuthorizedTemplates(arg0 context.Context, arg1 database.GetTemplatesWithFilterParams, arg2 rbac.PreparedAuthorized) ([]database.Template, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAuthorizedTemplates", arg0, arg1, arg2) - ret0, _ := ret[0].([]database.TemplateWithUser) + ret0, _ := ret[0].([]database.Template) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1152,10 +1152,10 @@ func (mr *MockStoreMockRecorder) GetTemplateAverageBuildTime(arg0, arg1 interfac } // GetTemplateByID mocks base method. -func (m *MockStore) GetTemplateByID(arg0 context.Context, arg1 uuid.UUID) (database.TemplateWithUser, error) { +func (m *MockStore) GetTemplateByID(arg0 context.Context, arg1 uuid.UUID) (database.Template, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTemplateByID", arg0, arg1) - ret0, _ := ret[0].(database.TemplateWithUser) + ret0, _ := ret[0].(database.Template) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1167,10 +1167,10 @@ func (mr *MockStoreMockRecorder) GetTemplateByID(arg0, arg1 interface{}) *gomock } // GetTemplateByOrganizationAndName mocks base method. -func (m *MockStore) GetTemplateByOrganizationAndName(arg0 context.Context, arg1 database.GetTemplateByOrganizationAndNameParams) (database.TemplateWithUser, error) { +func (m *MockStore) GetTemplateByOrganizationAndName(arg0 context.Context, arg1 database.GetTemplateByOrganizationAndNameParams) (database.Template, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTemplateByOrganizationAndName", arg0, arg1) - ret0, _ := ret[0].(database.TemplateWithUser) + ret0, _ := ret[0].(database.Template) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1347,10 +1347,10 @@ func (mr *MockStoreMockRecorder) GetTemplateVersionsCreatedAfter(arg0, arg1 inte } // GetTemplates mocks base method. -func (m *MockStore) GetTemplates(arg0 context.Context) ([]database.TemplateWithUser, error) { +func (m *MockStore) GetTemplates(arg0 context.Context) ([]database.Template, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTemplates", arg0) - ret0, _ := ret[0].([]database.TemplateWithUser) + ret0, _ := ret[0].([]database.Template) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1362,10 +1362,10 @@ func (mr *MockStoreMockRecorder) GetTemplates(arg0 interface{}) *gomock.Call { } // GetTemplatesWithFilter mocks base method. -func (m *MockStore) GetTemplatesWithFilter(arg0 context.Context, arg1 database.GetTemplatesWithFilterParams) ([]database.TemplateWithUser, error) { +func (m *MockStore) GetTemplatesWithFilter(arg0 context.Context, arg1 database.GetTemplatesWithFilterParams) ([]database.Template, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTemplatesWithFilter", arg0, arg1) - ret0, _ := ret[0].([]database.TemplateWithUser) + ret0, _ := ret[0].([]database.Template) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index ca70fbb3e9fad..fda910dfa91f3 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -109,7 +109,7 @@ func (t Template) RBACObject() rbac.Object { WithGroupACL(t.GroupACL) } -func (t TemplateWithUser) RBACObject() rbac.Object { +func (t Template) RBACObject() rbac.Object { return rbac.ResourceTemplate.WithID(t.ID). InOrg(t.OrganizationID). WithACLUserList(t.UserACL). @@ -130,7 +130,7 @@ func (t Template) DeepCopy() Template { return cpy } -func (TemplateVersion) RBACObject(template TemplateWithUser) rbac.Object { +func (TemplateVersion) RBACObject(template Template) rbac.Object { // Just use the parent template resource for controlling versions return template.RBACObject() } diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index 49013e7e82dfc..fd47cd8aaff18 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -27,12 +27,12 @@ type customQuerier interface { } type templateQuerier interface { - GetAuthorizedTemplates(ctx context.Context, arg GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]TemplateWithUser, error) + GetAuthorizedTemplates(ctx context.Context, arg GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]Template, error) GetTemplateGroupRoles(ctx context.Context, id uuid.UUID) ([]TemplateGroup, error) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]TemplateUser, error) } -func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]TemplateWithUser, error) { +func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]Template, error) { authorizedFilter, err := prepared.CompileToSQL(ctx, regosql.ConvertConfig{ VariableConverter: regosql.TemplateConverter(), }) @@ -57,9 +57,9 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate return nil, err } defer rows.Close() - var items []TemplateWithUser + var items []Template for rows.Next() { - var i TemplateWithUser + var i Template if err := rows.Scan( &i.ID, &i.CreatedAt, diff --git a/coderd/database/models.go b/coderd/database/models.go index da10c4f0f2684..22704fd549fe8 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1567,7 +1567,35 @@ type TailnetCoordinator struct { HeartbeatAt time.Time `db:"heartbeat_at" json:"heartbeat_at"` } +// Joins in the username + avatar url of the created by user. type Template struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` + Deleted bool `db:"deleted" json:"deleted"` + Name string `db:"name" json:"name"` + Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` + Description string `db:"description" json:"description"` + DefaultTTL int64 `db:"default_ttl" json:"default_ttl"` + CreatedBy uuid.UUID `db:"created_by" json:"created_by"` + Icon string `db:"icon" json:"icon"` + UserACL TemplateACL `db:"user_acl" json:"user_acl"` + GroupACL TemplateACL `db:"group_acl" json:"group_acl"` + DisplayName string `db:"display_name" json:"display_name"` + AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` + MaxTTL int64 `db:"max_ttl" json:"max_ttl"` + AllowUserAutostart bool `db:"allow_user_autostart" json:"allow_user_autostart"` + AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` + FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` + InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` + LockedTTL int64 `db:"locked_ttl" json:"locked_ttl"` + CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"` + CreatedByUsername string `db:"created_by_username" json:"created_by_username"` +} + +type TemplateTable struct { ID uuid.UUID `db:"id" json:"id"` CreatedAt time.Time `db:"created_at" json:"created_at"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` @@ -1667,34 +1695,6 @@ type TemplateVersionVariable struct { Sensitive bool `db:"sensitive" json:"sensitive"` } -// Joins in the username + avatar url of the created by user. -type TemplateWithUser struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` - Deleted bool `db:"deleted" json:"deleted"` - Name string `db:"name" json:"name"` - Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` - ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` - Description string `db:"description" json:"description"` - DefaultTTL int64 `db:"default_ttl" json:"default_ttl"` - CreatedBy uuid.UUID `db:"created_by" json:"created_by"` - Icon string `db:"icon" json:"icon"` - UserACL TemplateACL `db:"user_acl" json:"user_acl"` - GroupACL TemplateACL `db:"group_acl" json:"group_acl"` - DisplayName string `db:"display_name" json:"display_name"` - AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` - MaxTTL int64 `db:"max_ttl" json:"max_ttl"` - AllowUserAutostart bool `db:"allow_user_autostart" json:"allow_user_autostart"` - AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` - FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` - InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` - LockedTTL int64 `db:"locked_ttl" json:"locked_ttl"` - CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"` - CreatedByUsername string `db:"created_by_username" json:"created_by_username"` -} - type User struct { ID uuid.UUID `db:"id" json:"id"` Email string `db:"email" json:"email"` diff --git a/coderd/database/querier.go b/coderd/database/querier.go index aafdab99f9294..31c5537cf862b 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -102,8 +102,8 @@ type sqlcQuerier interface { GetTailnetAgents(ctx context.Context, id uuid.UUID) ([]TailnetAgent, error) GetTailnetClientsForAgent(ctx context.Context, agentID uuid.UUID) ([]TailnetClient, error) GetTemplateAverageBuildTime(ctx context.Context, arg GetTemplateAverageBuildTimeParams) (GetTemplateAverageBuildTimeRow, error) - GetTemplateByID(ctx context.Context, id uuid.UUID) (TemplateWithUser, error) - GetTemplateByOrganizationAndName(ctx context.Context, arg GetTemplateByOrganizationAndNameParams) (TemplateWithUser, error) + GetTemplateByID(ctx context.Context, id uuid.UUID) (Template, error) + GetTemplateByOrganizationAndName(ctx context.Context, arg GetTemplateByOrganizationAndNameParams) (Template, error) GetTemplateDAUs(ctx context.Context, arg GetTemplateDAUsParams) ([]GetTemplateDAUsRow, error) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (TemplateVersion, error) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.UUID) (TemplateVersion, error) @@ -113,8 +113,8 @@ type sqlcQuerier interface { GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UUID) ([]TemplateVersion, error) GetTemplateVersionsByTemplateID(ctx context.Context, arg GetTemplateVersionsByTemplateIDParams) ([]TemplateVersion, error) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) - GetTemplates(ctx context.Context) ([]TemplateWithUser, error) - GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]TemplateWithUser, error) + GetTemplates(ctx context.Context) ([]Template, error) + GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]Template, error) GetUnexpiredLicenses(ctx context.Context) ([]License, error) GetUserByEmailOrUsername(ctx context.Context, arg GetUserByEmailOrUsernameParams) (User, error) GetUserByID(ctx context.Context, id uuid.UUID) (User, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 5b2800c0ce34c..e2938b48740dc 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3639,16 +3639,16 @@ const getTemplateByID = `-- name: GetTemplateByID :one SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl, created_by_avatar_url, created_by_username FROM - template_with_users AS templates + template_with_users WHERE id = $1 LIMIT 1 ` -func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (TemplateWithUser, error) { +func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Template, error) { row := q.db.QueryRowContext(ctx, getTemplateByID, id) - var i TemplateWithUser + var i Template err := row.Scan( &i.ID, &i.CreatedAt, @@ -3697,9 +3697,9 @@ type GetTemplateByOrganizationAndNameParams struct { Name string `db:"name" json:"name"` } -func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg GetTemplateByOrganizationAndNameParams) (TemplateWithUser, error) { +func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg GetTemplateByOrganizationAndNameParams) (Template, error) { row := q.db.QueryRowContext(ctx, getTemplateByOrganizationAndName, arg.OrganizationID, arg.Deleted, arg.Name) - var i TemplateWithUser + var i Template err := row.Scan( &i.ID, &i.CreatedAt, @@ -3734,15 +3734,15 @@ SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, ORDER BY (name, id) ASC ` -func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]TemplateWithUser, error) { +func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { rows, err := q.db.QueryContext(ctx, getTemplates) if err != nil { return nil, err } defer rows.Close() - var items []TemplateWithUser + var items []Template for rows.Next() { - var i TemplateWithUser + var i Template if err := rows.Scan( &i.ID, &i.CreatedAt, @@ -3820,7 +3820,7 @@ type GetTemplatesWithFilterParams struct { IDs []uuid.UUID `db:"ids" json:"ids"` } -func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]TemplateWithUser, error) { +func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]Template, error) { rows, err := q.db.QueryContext(ctx, getTemplatesWithFilter, arg.Deleted, arg.OrganizationID, @@ -3831,9 +3831,9 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate return nil, err } defer rows.Close() - var items []TemplateWithUser + var items []Template for rows.Next() { - var i TemplateWithUser + var i Template if err := rows.Scan( &i.ID, &i.CreatedAt, diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 7b04480d3967f..4012964b081d0 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -2,7 +2,7 @@ SELECT * FROM - template_with_users AS templates + template_with_users WHERE id = $1 LIMIT diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index a3782d18cb864..07bc54ab035ba 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -31,6 +31,8 @@ overrides: go_type: type: "string" rename: + template: TemplateTable + template_with_user: Template api_key: APIKey api_key_scope: APIKeyScope api_key_scope_all: APIKeyScopeAll @@ -81,3 +83,6 @@ sql: emit_db_tags: true emit_enum_valid_method: true emit_all_enum_values: true + omit_unused_structs: true + rename: + template_with_users: TemplateTest diff --git a/coderd/httpmw/templateparam.go b/coderd/httpmw/templateparam.go index 4b1f60bbb8671..1ba57167d5483 100644 --- a/coderd/httpmw/templateparam.go +++ b/coderd/httpmw/templateparam.go @@ -14,8 +14,8 @@ import ( type templateParamContextKey struct{} // TemplateParam returns the template from the ExtractTemplateParam handler. -func TemplateParam(r *http.Request) database.TemplateWithUser { - template, ok := r.Context().Value(templateParamContextKey{}).(database.TemplateWithUser) +func TemplateParam(r *http.Request) database.Template { + template, ok := r.Context().Value(templateParamContextKey{}).(database.Template) if !ok { panic("developer error: template param middleware not provided") } diff --git a/coderd/schedule/mock.go b/coderd/schedule/mock.go index 68cf778b4a49e..5c3c1e77ed803 100644 --- a/coderd/schedule/mock.go +++ b/coderd/schedule/mock.go @@ -10,7 +10,7 @@ import ( type MockTemplateScheduleStore struct { GetFn func(ctx context.Context, db database.Store, templateID uuid.UUID) (TemplateScheduleOptions, error) - SetFn func(ctx context.Context, db database.Store, template database.TemplateWithUser, options TemplateScheduleOptions) (database.TemplateWithUser, error) + SetFn func(ctx context.Context, db database.Store, template database.Template, options TemplateScheduleOptions) (database.Template, error) } var _ TemplateScheduleStore = MockTemplateScheduleStore{} @@ -23,7 +23,7 @@ func (m MockTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.Contex return NewAGPLTemplateScheduleStore().GetTemplateScheduleOptions(ctx, db, templateID) } -func (m MockTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, template database.TemplateWithUser, options TemplateScheduleOptions) (database.TemplateWithUser, error) { +func (m MockTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, template database.Template, options TemplateScheduleOptions) (database.Template, error) { if m.SetFn != nil { return m.SetFn(ctx, db, template, options) } diff --git a/coderd/schedule/template.go b/coderd/schedule/template.go index 43c2a6b183124..81ef1992ba336 100644 --- a/coderd/schedule/template.go +++ b/coderd/schedule/template.go @@ -30,7 +30,7 @@ type TemplateScheduleOptions struct { // scheduling options set by the template/site admin. type TemplateScheduleStore interface { GetTemplateScheduleOptions(ctx context.Context, db database.Store, templateID uuid.UUID) (TemplateScheduleOptions, error) - SetTemplateScheduleOptions(ctx context.Context, db database.Store, template database.TemplateWithUser, opts TemplateScheduleOptions) (database.TemplateWithUser, error) + SetTemplateScheduleOptions(ctx context.Context, db database.Store, template database.Template, opts TemplateScheduleOptions) (database.Template, error) } type agplTemplateScheduleStore struct{} @@ -62,13 +62,13 @@ func (*agplTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.Context }, nil } -func (*agplTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.TemplateWithUser, opts TemplateScheduleOptions) (database.TemplateWithUser, error) { +func (*agplTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.Template, opts TemplateScheduleOptions) (database.Template, error) { if int64(opts.DefaultTTL) == tpl.DefaultTTL { // Avoid updating the UpdatedAt timestamp if nothing will be changed. return tpl, nil } - var template database.TemplateWithUser + var template database.Template err := db.InTx(func(db database.Store) error { err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ ID: tpl.ID, diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index 62475604abcb4..ced52a58fd273 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -629,7 +629,7 @@ func ConvertUser(dbUser database.User) User { } // ConvertTemplate anonymizes a template. -func ConvertTemplate(dbTemplate database.TemplateWithUser) Template { +func ConvertTemplate(dbTemplate database.Template) Template { return Template{ ID: dbTemplate.ID, CreatedBy: dbTemplate.CreatedBy, diff --git a/coderd/templates.go b/coderd/templates.go index f3d32b40f4188..174e1f7b9c5b7 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -55,7 +55,7 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) { ctx = r.Context() template = httpmw.TemplateParam(r) auditor = *api.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ Audit: auditor, Log: api.Logger, Request: r, @@ -121,7 +121,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque organization = httpmw.OrganizationParam(r) apiKey = httpmw.APIKey(r) auditor = *api.Auditor.Load() - templateAudit, commitTemplateAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ + templateAudit, commitTemplateAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ Audit: auditor, Log: api.Logger, Request: r, @@ -144,7 +144,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque // Make a temporary struct to represent the template. This is used for // auditing if any of the following checks fail. It will be overwritten when // the template is inserted into the db. - templateAudit.New = database.TemplateWithUser{ + templateAudit.New = database.Template{ OrganizationID: organization.ID, Name: createTemplate.Name, Description: createTemplate.Description, @@ -264,7 +264,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque } var ( - dbTemplate database.TemplateWithUser + dbTemplate database.Template template codersdk.Template allowUserCancelWorkspaceJobs = ptr.NilToDefault(createTemplate.AllowUserCancelWorkspaceJobs, false) @@ -448,7 +448,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { ctx = r.Context() template = httpmw.TemplateParam(r) auditor = *api.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ Audit: auditor, Log: api.Logger, Request: r, @@ -494,7 +494,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { return } - var updated database.TemplateWithUser + var updated database.Template err := api.Database.InTx(func(tx database.Store) error { if req.Name == template.Name && req.Description == template.Description && @@ -648,7 +648,7 @@ func (api *API) templateExamples(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, ex) } -func (api *API) convertTemplates(templates []database.TemplateWithUser) []codersdk.Template { +func (api *API) convertTemplates(templates []database.Template) []codersdk.Template { apiTemplates := make([]codersdk.Template, 0, len(templates)) for _, template := range templates { @@ -664,7 +664,7 @@ func (api *API) convertTemplates(templates []database.TemplateWithUser) []coders } func (api *API) convertTemplate( - template database.TemplateWithUser, + template database.Template, ) codersdk.Template { activeCount, _ := api.metricsCache.TemplateUniqueUsers(template.ID) diff --git a/coderd/templateversions.go b/coderd/templateversions.go index fa543557ce7a6..13fc4420fc7e9 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -1043,7 +1043,7 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque ctx = r.Context() template = httpmw.TemplateParam(r) auditor = *api.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ Audit: auditor, Log: api.Logger, Request: r, diff --git a/coderd/workspaces.go b/coderd/workspaces.go index dc5d021e07745..fe26804c3c78e 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -1000,7 +1000,7 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { } type workspaceData struct { - templates []database.TemplateWithUser + templates []database.Template builds []codersdk.WorkspaceBuild users []database.User } @@ -1057,7 +1057,7 @@ func convertWorkspaces(workspaces []database.Workspace, data workspaceData) ([]c for _, workspaceBuild := range data.builds { buildByWorkspaceID[workspaceBuild.WorkspaceID] = workspaceBuild } - templateByID := map[uuid.UUID]database.TemplateWithUser{} + templateByID := map[uuid.UUID]database.Template{} for _, template := range data.templates { templateByID[template.ID] = template } @@ -1094,7 +1094,7 @@ func convertWorkspaces(workspaces []database.Workspace, data workspaceData) ([]c func convertWorkspace( workspace database.Workspace, workspaceBuild codersdk.WorkspaceBuild, - template database.TemplateWithUser, + template database.Template, owner *database.User, ) codersdk.Workspace { var autostartSchedule *string @@ -1159,7 +1159,7 @@ func convertWorkspaceTTLMillis(i sql.NullInt64) *int64 { // Calculate the time of the upcoming workspace deletion, if applicable; otherwise, return nil. // Workspaces may have impending deletions if InactivityTTL feature is turned on and the workspace is inactive. -func calculateDeletingAt(workspace database.Workspace, template database.TemplateWithUser, build codersdk.WorkspaceBuild) *time.Time { +func calculateDeletingAt(workspace database.Workspace, template database.Template, build codersdk.WorkspaceBuild) *time.Time { inactiveStatuses := []codersdk.WorkspaceStatus{codersdk.WorkspaceStatusStopped, codersdk.WorkspaceStatusCanceled, codersdk.WorkspaceStatusFailed, codersdk.WorkspaceStatusDeleted} isInactive := slices.Contains(inactiveStatuses, build.Status) // If InactivityTTL is turned off (set to 0) or if the workspace is active, there is no impending deletion diff --git a/coderd/wsbuilder/wsbuilder.go b/coderd/wsbuilder/wsbuilder.go index 3ebbc3c31620d..7f6d840f9db6d 100644 --- a/coderd/wsbuilder/wsbuilder.go +++ b/coderd/wsbuilder/wsbuilder.go @@ -50,7 +50,7 @@ type Builder struct { store database.Store // cache of objects, so we only fetch once - template *database.TemplateWithUser + template *database.Template templateVersion *database.TemplateVersion templateVersionJob *database.ProvisionerJob templateVersionParameters *[]database.TemplateVersionParameter @@ -368,7 +368,7 @@ func (b *Builder) buildTx(authFunc func(action rbac.Action, object rbac.Objecter return &workspaceBuild, &provisionerJob, nil } -func (b *Builder) getTemplate() (*database.TemplateWithUser, error) { +func (b *Builder) getTemplate() (*database.Template, error) { if b.template != nil { return b.template, nil } diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 42ef48b30b9a5..f86f9a8c3c956 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -57,7 +57,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "private_key": ActionSecret, // We don't want to expose private keys in diffs. "public_key": ActionTrack, // Public keys are ok to expose in a diff. }, - &database.TemplateWithUser{}: { + &database.Template{}: { "id": ActionTrack, "created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff. "updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff. diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index 5099371b98ced..d02612839db25 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -331,7 +331,7 @@ func (*EnterpriseTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.C }, nil } -func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.TemplateWithUser, opts schedule.TemplateScheduleOptions) (database.TemplateWithUser, error) { +func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.Template, opts schedule.TemplateScheduleOptions) (database.Template, error) { if int64(opts.DefaultTTL) == tpl.DefaultTTL && int64(opts.MaxTTL) == tpl.MaxTTL && int64(opts.FailureTTL) == tpl.FailureTTL && @@ -343,7 +343,7 @@ func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.C return tpl, nil } - var template database.TemplateWithUser + var template database.Template err := db.InTx(func(db database.Store) error { err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ ID: tpl.ID, @@ -368,7 +368,7 @@ func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.C return nil }, nil) if err != nil { - return database.TemplateWithUser{}, err + return database.Template{}, err } // Update all workspaces using the template to set the user defined schedule @@ -387,7 +387,7 @@ func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.C TemplateMaxTTL: int64(opts.MaxTTL), }) if err != nil { - return database.TemplateWithUser{}, xerrors.Errorf("update TTL of all workspaces on template to be within new template max TTL: %w", err) + return database.Template{}, xerrors.Errorf("update TTL of all workspaces on template to be within new template max TTL: %w", err) } } diff --git a/enterprise/coderd/templates.go b/enterprise/coderd/templates.go index 81935bc075f4c..dec2c1c9a6f14 100644 --- a/enterprise/coderd/templates.go +++ b/enterprise/coderd/templates.go @@ -105,7 +105,7 @@ func (api *API) patchTemplateACL(rw http.ResponseWriter, r *http.Request) { ctx = r.Context() template = httpmw.TemplateParam(r) auditor = api.AGPL.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.TemplateWithUser](rw, &audit.RequestParams{ + aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ Audit: *auditor, Log: api.Logger, Request: r, From ca1a3bb12224f625868152a26e3ad7ec3bd12863 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 18 Jul 2023 13:25:49 -0400 Subject: [PATCH 05/16] Fix dbfake --- coderd/database/dbfake/dbfake.go | 25 +++++++++++++------------ coderd/database/modelmethods.go | 7 ------- coderd/database/sqlc.yaml | 1 - 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index a214cbc4ffb9b..c91c911a0d521 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -58,7 +58,7 @@ func New() database.Store { workspaceResourceMetadata: make([]database.WorkspaceResourceMetadatum, 0), provisionerJobs: make([]database.ProvisionerJob, 0), templateVersions: make([]database.TemplateVersion, 0), - templates: make([]database.Template, 0), + templates: make([]database.TemplateTable, 0), workspaceAgentStats: make([]database.WorkspaceAgentStat, 0), workspaceAgentLogs: make([]database.WorkspaceAgentStartupLog, 0), workspaceBuilds: make([]database.WorkspaceBuild, 0), @@ -130,7 +130,7 @@ type data struct { templateVersions []database.TemplateVersion templateVersionParameters []database.TemplateVersionParameter templateVersionVariables []database.TemplateVersionVariable - templates []database.Template + templates []database.TemplateTable workspaceAgents []database.WorkspaceAgent workspaceAgentMetadata []database.WorkspaceAgentMetadatum workspaceAgentLogs []database.WorkspaceAgentStartupLog @@ -452,7 +452,7 @@ func (q *FakeQuerier) getTemplateByIDNoLock(_ context.Context, id uuid.UUID) (da return database.Template{}, sql.ErrNoRows } -func (q *FakeQuerier) templatesWithUser(tpl []database.Template) []database.Template { +func (q *FakeQuerier) templatesWithUser(tpl []database.TemplateTable) []database.Template { cpy := make([]database.Template, 0, len(tpl)) for _, t := range tpl { cpy = append(cpy, q.templateWithUser(t)) @@ -460,7 +460,7 @@ func (q *FakeQuerier) templatesWithUser(tpl []database.Template) []database.Temp return cpy } -func (q *FakeQuerier) templateWithUser(tpl database.Template) database.Template { +func (q *FakeQuerier) templateWithUser(tpl database.TemplateTable) database.Template { var user database.User for _, _user := range q.users { if _user.ID == tpl.CreatedBy { @@ -2118,9 +2118,9 @@ func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.Template, erro templates := slices.Clone(q.templates) for i := range templates { - templates[i] = templates[i].DeepCopy() + templates[i] = templates[i] } - slices.SortFunc(templates, func(i, j database.Template) bool { + slices.SortFunc(templates, func(i, j database.TemplateTable) bool { if i.Name != j.Name { return i.Name < j.Name } @@ -3470,7 +3470,7 @@ func (q *FakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTempl defer q.mutex.Unlock() //nolint:gosimple - template := database.Template{ + template := database.TemplateTable{ ID: arg.ID, CreatedAt: arg.CreatedAt, UpdatedAt: arg.UpdatedAt, @@ -5009,7 +5009,8 @@ func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.G } var templates []database.Template - for _, template := range q.templates { + for _, templateTable := range q.templates { + template := q.templateWithUser(templateTable) if prepared != nil && prepared.Authorize(ctx, template.RBACObject()) != nil { continue } @@ -5037,7 +5038,7 @@ func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.G continue } } - templates = append(templates, template.DeepCopy()) + templates = append(templates) } if len(templates) > 0 { slices.SortFunc(templates, func(i, j database.Template) bool { @@ -5046,7 +5047,7 @@ func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.G } return i.ID.String() < j.ID.String() }) - return q.templatesWithUser(templates), nil + return templates, nil } return nil, sql.ErrNoRows @@ -5056,7 +5057,7 @@ func (q *FakeQuerier) GetTemplateGroupRoles(_ context.Context, id uuid.UUID) ([] q.mutex.RLock() defer q.mutex.RUnlock() - var template database.Template + var template database.TemplateTable for _, t := range q.templates { if t.ID == id { template = t @@ -5093,7 +5094,7 @@ func (q *FakeQuerier) GetTemplateUserRoles(_ context.Context, id uuid.UUID) ([]d q.mutex.RLock() defer q.mutex.RUnlock() - var template database.Template + var template database.TemplateTable for _, t := range q.templates { if t.ID == id { template = t diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index fda910dfa91f3..bb7dfdd1bb818 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -109,13 +109,6 @@ func (t Template) RBACObject() rbac.Object { WithGroupACL(t.GroupACL) } -func (t Template) RBACObject() rbac.Object { - return rbac.ResourceTemplate.WithID(t.ID). - InOrg(t.OrganizationID). - WithACLUserList(t.UserACL). - WithGroupACL(t.GroupACL) -} - func (t GetFileTemplatesRow) RBACObject() rbac.Object { return rbac.ResourceTemplate.WithID(t.TemplateID). InOrg(t.TemplateOrganizationID). diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index 07bc54ab035ba..c121658795648 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -83,6 +83,5 @@ sql: emit_db_tags: true emit_enum_valid_method: true emit_all_enum_values: true - omit_unused_structs: true rename: template_with_users: TemplateTest From cffbb614072e62f33d9dd36fdea60e03e1ce1648 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 18 Jul 2023 13:30:50 -0400 Subject: [PATCH 06/16] Cleanup --- coderd/database/migrations/000138_join_users.down.sql | 1 + coderd/database/queries/templates.sql | 9 +++------ coderd/database/sqlc.yaml | 2 -- enterprise/audit/table.go | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/coderd/database/migrations/000138_join_users.down.sql b/coderd/database/migrations/000138_join_users.down.sql index 2b83c1aa76b38..c4753691256b7 100644 --- a/coderd/database/migrations/000138_join_users.down.sql +++ b/coderd/database/migrations/000138_join_users.down.sql @@ -1,5 +1,6 @@ BEGIN; DROP VIEW visible_users; +DROP VIEW template_with_users; COMMIT; diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 4012964b081d0..54ad458ce2f36 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -107,8 +107,7 @@ SET allow_user_cancel_workspace_jobs = $7 WHERE id = $1 -RETURNING - *; +; -- name: UpdateTemplateScheduleByID :exec UPDATE @@ -124,8 +123,7 @@ SET locked_ttl = $9 WHERE id = $1 -RETURNING - *; +; -- name: UpdateTemplateACLByID :exec UPDATE @@ -135,8 +133,7 @@ SET user_acl = $2 WHERE id = $3 -RETURNING - *; +; -- name: GetTemplateAverageBuildTime :one WITH build_times AS ( diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index c121658795648..2bb3adea0980d 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -83,5 +83,3 @@ sql: emit_db_tags: true emit_enum_valid_method: true emit_all_enum_values: true - rename: - template_with_users: TemplateTest diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index f86f9a8c3c956..c5df6de732692 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -72,7 +72,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "default_ttl": ActionTrack, "created_by": ActionTrack, "created_by_username": ActionTrack, - "created_by_avatar_url": ActionIgnore, + "created_by_avatar_url": ActionTrack, "group_acl": ActionTrack, "user_acl": ActionTrack, "allow_user_autostart": ActionTrack, From 1219f2a92c54988576501188b966b444aa693fd1 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 18 Jul 2023 13:45:19 -0400 Subject: [PATCH 07/16] Add unit test to enforce good view --- coderd/database/dbgen/dbgen.go | 8 +++- coderd/database/generate.sh | 2 + .../migrations/000138_join_users.up.sql | 1 + coderd/database/models_test.go | 45 +++++++++++++++++++ coderd/database/queries.sql.go | 6 --- docs/admin/audit-logs.md | 26 +++++------ 6 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 coderd/database/models_test.go diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index 23ccfe71ac61a..383e4b2fe5c19 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -62,8 +62,9 @@ func AuditLog(t testing.TB, db database.Store, seed database.AuditLog) database. } func Template(t testing.TB, db database.Store, seed database.Template) database.Template { - template, err := db.InsertTemplate(genCtx, database.InsertTemplateParams{ - ID: takeFirst(seed.ID, uuid.New()), + id := takeFirst(seed.ID, uuid.New()) + err := db.InsertTemplate(genCtx, database.InsertTemplateParams{ + ID: id, CreatedAt: takeFirst(seed.CreatedAt, database.Now()), UpdatedAt: takeFirst(seed.UpdatedAt, database.Now()), OrganizationID: takeFirst(seed.OrganizationID, uuid.New()), @@ -79,6 +80,9 @@ func Template(t testing.TB, db database.Store, seed database.Template) database. AllowUserCancelWorkspaceJobs: seed.AllowUserCancelWorkspaceJobs, }) require.NoError(t, err, "insert template") + + template, err := db.GetTemplateByID(context.Background(), id) + require.NoError(t, err, "get template") return template } diff --git a/coderd/database/generate.sh b/coderd/database/generate.sh index ffbf2909490c3..c13e749ef2cd3 100755 --- a/coderd/database/generate.sh +++ b/coderd/database/generate.sh @@ -57,4 +57,6 @@ SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") go run golang.org/x/tools/cmd/goimports@latest -w queries.sql.go go run ../../scripts/dbgen/main.go + # This will error if a view is broken. + go test -run=TestViewSubset ) diff --git a/coderd/database/migrations/000138_join_users.up.sql b/coderd/database/migrations/000138_join_users.up.sql index 7daabfb23f699..198dd55edf1d2 100644 --- a/coderd/database/migrations/000138_join_users.up.sql +++ b/coderd/database/migrations/000138_join_users.up.sql @@ -10,6 +10,7 @@ FROM COMMENT ON VIEW visible_users IS 'Visible fields of users are allowed to be joined with other tables for including context of other resources.'; +-- If you need to update this view, put 'DROP VIEW template_with_users;' before this. CREATE VIEW template_with_users AS diff --git a/coderd/database/models_test.go b/coderd/database/models_test.go new file mode 100644 index 0000000000000..86e3bca312f43 --- /dev/null +++ b/coderd/database/models_test.go @@ -0,0 +1,45 @@ +package database_test + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/coder/coder/coderd/database" +) + +// TestViewSubsetTemplate ensures TemplateTable is a subset of Template +func TestViewSubsetTemplate(t *testing.T) { + table := reflect.TypeOf(database.TemplateTable{}) + joined := reflect.TypeOf(database.Template{}) + + tableFields := allFields(table) + joinedFields := allFields(joined) + if !assert.Subset(t, fieldNames(joinedFields), fieldNames(tableFields), "table is not subset") { + t.Log("Some fields were added to the Template Table without updating the 'template_with_users' view.") + t.Log("See migration 000138_join_users_up.sql to create the view.") + } +} + +func fieldNames(fields []reflect.StructField) []string { + names := make([]string, len(fields)) + for i, field := range fields { + names[i] = field.Name + } + return names +} + +func allFields(rt reflect.Type) []reflect.StructField { + fields := make([]reflect.StructField, 0, rt.NumField()) + for i := 0; i < rt.NumField(); i++ { + field := rt.Field(i) + if field.Anonymous && field.Type.Kind() == reflect.Struct { + // Recurse into anonymous struct fields. + fields = append(fields, allFields(field.Type)...) + continue + } + fields = append(fields, rt.Field(i)) + } + return fields +} diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index e2938b48740dc..b656ab3182c23 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3940,8 +3940,6 @@ SET user_acl = $2 WHERE id = $3 -RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl ` type UpdateTemplateACLByIDParams struct { @@ -4009,8 +4007,6 @@ SET allow_user_cancel_workspace_jobs = $7 WHERE id = $1 -RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl ` type UpdateTemplateMetaByIDParams struct { @@ -4050,8 +4046,6 @@ SET locked_ttl = $9 WHERE id = $1 -RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl, locked_ttl ` type UpdateTemplateScheduleByIDParams struct { diff --git a/docs/admin/audit-logs.md b/docs/admin/audit-logs.md index 454d041225385..14b45ad6265b0 100644 --- a/docs/admin/audit-logs.md +++ b/docs/admin/audit-logs.md @@ -9,19 +9,19 @@ We track the following resources: -| Resource | | -| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| APIKey
login, logout, register, create, delete |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| -| AuditOAuthConvertState
|
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| -| Group
create, write, delete |
FieldTracked
avatar_urltrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
| -| GitSSHKey
create |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| -| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| -| Template
write, delete |
FieldTracked
active_version_idtrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
created_atfalse
created_bytrue
default_ttltrue
deletedfalse
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
inactivity_ttltrue
locked_ttltrue
max_ttltrue
nametrue
organization_idfalse
provisionertrue
updated_atfalse
user_acltrue
| -| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
git_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| -| User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typetrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| -| Workspace
create, write, delete |
FieldTracked
autostart_scheduletrue
created_atfalse
deletedfalse
idtrue
last_used_atfalse
locked_attrue
nametrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| -| WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| -| WorkspaceProxy
|
FieldTracked
created_attrue
deletedfalse
display_nametrue
icontrue
idtrue
nametrue
token_hashed_secrettrue
updated_atfalse
urltrue
wildcard_hostnametrue
| +| Resource | | +| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| APIKey
login, logout, register, create, delete |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| +| AuditOAuthConvertState
|
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| +| Group
create, write, delete |
FieldTracked
avatar_urltrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
| +| GitSSHKey
create |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| +| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| +| Template
write, delete |
FieldTracked
active_version_idtrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
created_atfalse
created_bytrue
created_by_avatar_urltrue
created_by_usernametrue
default_ttltrue
deletedfalse
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
inactivity_ttltrue
locked_ttltrue
max_ttltrue
nametrue
organization_idfalse
provisionertrue
updated_atfalse
user_acltrue
| +| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
git_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| +| User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typetrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| +| Workspace
create, write, delete |
FieldTracked
autostart_scheduletrue
created_atfalse
deletedfalse
idtrue
last_used_atfalse
locked_attrue
nametrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| +| WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| +| WorkspaceProxy
|
FieldTracked
created_attrue
deletedfalse
display_nametrue
icontrue
idtrue
nametrue
token_hashed_secrettrue
updated_atfalse
urltrue
wildcard_hostnametrue
| From 5b6d04641ee152c4c5777d1b0d408902f9893213 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 18 Jul 2023 13:50:23 -0400 Subject: [PATCH 08/16] Fix insert template --- coderd/metricscache/metricscache_test.go | 7 +++++-- coderd/templates.go | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/coderd/metricscache/metricscache_test.go b/coderd/metricscache/metricscache_test.go index 5cbe9b10203e1..d2a1a2a25f4ef 100644 --- a/coderd/metricscache/metricscache_test.go +++ b/coderd/metricscache/metricscache_test.go @@ -349,11 +349,14 @@ func TestCache_BuildTime(t *testing.T) { defer cache.Close() - template, err := db.InsertTemplate(ctx, database.InsertTemplateParams{ - ID: uuid.New(), + id := uuid.New() + err := db.InsertTemplate(ctx, database.InsertTemplateParams{ + ID: id, Provisioner: database.ProvisionerTypeEcho, }) require.NoError(t, err) + template, err := db.GetTemplateByID(ctx, id) + require.NoError(t, err) templateVersion, err := db.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{ ID: uuid.New(), diff --git a/coderd/templates.go b/coderd/templates.go index 174e1f7b9c5b7..1238303aa04f5 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -280,8 +280,9 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque } err = api.Database.InTx(func(tx database.Store) error { now := database.Now() + id := uuid.New() err = tx.InsertTemplate(ctx, database.InsertTemplateParams{ - ID: uuid.New(), + ID: id, CreatedAt: now, UpdatedAt: now, OrganizationID: organization.ID, @@ -300,7 +301,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque return xerrors.Errorf("insert template: %s", err) } - dbTemplate, err = tx.GetTemplateByID(ctx, dbTemplate.ID) + dbTemplate, err = tx.GetTemplateByID(ctx, id) if err != nil { return xerrors.Errorf("get template by id: %s", err) } From 664a81a31c0f4b0349eedf7daa61d7e6c1b736f2 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 18 Jul 2023 13:55:18 -0400 Subject: [PATCH 09/16] Fix typo --- coderd/database/dbfake/dbfake.go | 2 +- coderd/database/generate.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index c91c911a0d521..f0bdb81de7b59 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -469,7 +469,7 @@ func (q *FakeQuerier) templateWithUser(tpl database.TemplateTable) database.Temp } } var withUser database.Template - // This is a cheeky way to copy the fields over without explictly listing them all. + // This is a cheeky way to copy the fields over without explicitly listing them all. d, _ := json.Marshal(tpl) _ = json.Unmarshal(d, &withUser) withUser.CreatedByUsername = user.Username diff --git a/coderd/database/generate.sh b/coderd/database/generate.sh index c13e749ef2cd3..e8777a036a3cf 100755 --- a/coderd/database/generate.sh +++ b/coderd/database/generate.sh @@ -57,6 +57,6 @@ SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") go run golang.org/x/tools/cmd/goimports@latest -w queries.sql.go go run ../../scripts/dbgen/main.go - # This will error if a view is broken. + # This will error if a view is broken. go test -run=TestViewSubset ) From 457322a222c8c0f30e7bad796c3e3c9979336131 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 18 Jul 2023 14:03:30 -0400 Subject: [PATCH 10/16] Fix dbfkaE --- coderd/database/dbfake/dbfake.go | 2 +- coderd/database/migrations/000138_join_users.down.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index f0bdb81de7b59..1a3571b287ee7 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -5038,7 +5038,7 @@ func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.G continue } } - templates = append(templates) + templates = append(templates, template) } if len(templates) > 0 { slices.SortFunc(templates, func(i, j database.Template) bool { diff --git a/coderd/database/migrations/000138_join_users.down.sql b/coderd/database/migrations/000138_join_users.down.sql index c4753691256b7..754574f7b5abd 100644 --- a/coderd/database/migrations/000138_join_users.down.sql +++ b/coderd/database/migrations/000138_join_users.down.sql @@ -1,6 +1,6 @@ BEGIN; -DROP VIEW visible_users; DROP VIEW template_with_users; +DROP VIEW visible_users; COMMIT; From c2aa8c4416edef21c59a36ec9bd529741251a545 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 18 Jul 2023 15:09:52 -0400 Subject: [PATCH 11/16] Fix unit tests --- coderd/database/dbauthz/dbauthz_test.go | 2 +- coderd/provisionerdserver/provisionerdserver_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index b9e42f632bd6c..6a4f09260b7ab 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -772,7 +772,7 @@ func (s *MethodTestSuite) TestTemplate() { t1 := dbgen.Template(s.T(), db, database.Template{}) check.Args(database.UpdateTemplateACLByIDParams{ ID: t1.ID, - }).Asserts(t1, rbac.ActionCreate).Returns(t1) + }).Asserts(t1, rbac.ActionCreate) })) s.Run("UpdateTemplateActiveVersionByID", s.Subtest(func(db database.Store, check *expects) { t1 := dbgen.Template(s.T(), db, database.Template{ diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 6b881210b3f6a..b0183fd3b1604 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -1025,7 +1025,7 @@ func TestCompleteJob(t *testing.T) { Name: "template", Provisioner: database.ProvisionerTypeEcho, }) - template, err := srv.Database.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ + err := srv.Database.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ ID: template.ID, UpdatedAt: database.Now(), AllowUserAutostart: c.templateAllowAutostop, From 72097c56d7a6b4cc1337e844f582aff96c088d12 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 18 Jul 2023 15:19:06 -0400 Subject: [PATCH 12/16] Linting --- coderd/database/dbfake/dbfake.go | 3 --- coderd/database/models_test.go | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 1a3571b287ee7..9f60c60f18534 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -2117,9 +2117,6 @@ func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.Template, erro defer q.mutex.RUnlock() templates := slices.Clone(q.templates) - for i := range templates { - templates[i] = templates[i] - } slices.SortFunc(templates, func(i, j database.TemplateTable) bool { if i.Name != j.Name { return i.Name < j.Name diff --git a/coderd/database/models_test.go b/coderd/database/models_test.go index 86e3bca312f43..54d11373f9253 100644 --- a/coderd/database/models_test.go +++ b/coderd/database/models_test.go @@ -11,6 +11,7 @@ import ( // TestViewSubsetTemplate ensures TemplateTable is a subset of Template func TestViewSubsetTemplate(t *testing.T) { + t.Parallel() table := reflect.TypeOf(database.TemplateTable{}) joined := reflect.TypeOf(database.Template{}) From 4715f521c442b8373fcc3ad328a44523ce1473f1 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 19 Jul 2023 07:07:39 -0400 Subject: [PATCH 13/16] Move to in tx and ignore new fields in audit diff --- enterprise/audit/table.go | 4 +-- enterprise/coderd/provisionerdaemons.go | 40 ++++++++++++------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index c5df6de732692..d75776a437183 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -71,8 +71,8 @@ var auditableResourcesTypes = map[any]map[string]Action{ "icon": ActionTrack, "default_ttl": ActionTrack, "created_by": ActionTrack, - "created_by_username": ActionTrack, - "created_by_avatar_url": ActionTrack, + "created_by_username": ActionIgnore, + "created_by_avatar_url": ActionIgnore, "group_acl": ActionTrack, "user_acl": ActionTrack, "allow_user_autostart": ActionTrack, diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index d02612839db25..4d424a10b24a6 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -360,6 +360,26 @@ func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.C return xerrors.Errorf("update template schedule: %w", err) } + // Update all workspaces using the template to set the user defined schedule + // to be within the new bounds. This essentially does the following for each + // workspace using the template. + // if (template.ttl != NULL) { + // workspace.ttl = min(workspace.ttl, template.ttl) + // } + // + // NOTE: this does not apply to currently running workspaces as their + // schedule information is committed to the workspace_build during start. + // This limitation is displayed to the user while editing the template. + if opts.MaxTTL > 0 { + err = db.UpdateWorkspaceTTLToBeWithinTemplateMax(ctx, database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams{ + TemplateID: template.ID, + TemplateMaxTTL: int64(opts.MaxTTL), + }) + if err != nil { + return xerrors.Errorf("update TTL of all workspaces on template to be within new template max TTL: %w", err) + } + } + template, err = db.GetTemplateByID(ctx, tpl.ID) if err != nil { return xerrors.Errorf("get updated template schedule: %w", err) @@ -371,25 +391,5 @@ func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.C return database.Template{}, err } - // Update all workspaces using the template to set the user defined schedule - // to be within the new bounds. This essentially does the following for each - // workspace using the template. - // if (template.ttl != NULL) { - // workspace.ttl = min(workspace.ttl, template.ttl) - // } - // - // NOTE: this does not apply to currently running workspaces as their - // schedule information is committed to the workspace_build during start. - // This limitation is displayed to the user while editing the template. - if opts.MaxTTL > 0 { - err = db.UpdateWorkspaceTTLToBeWithinTemplateMax(ctx, database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams{ - TemplateID: template.ID, - TemplateMaxTTL: int64(opts.MaxTTL), - }) - if err != nil { - return database.Template{}, xerrors.Errorf("update TTL of all workspaces on template to be within new template max TTL: %w", err) - } - } - return template, nil } From d1159208f8a4c638cd54795ef0681291deeba386 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 19 Jul 2023 07:10:53 -0400 Subject: [PATCH 14/16] Make gen --- coderd/schedule/template.go | 7 ++++++- docs/admin/audit-logs.md | 26 +++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/coderd/schedule/template.go b/coderd/schedule/template.go index 81ef1992ba336..ff6bb39ac9e6e 100644 --- a/coderd/schedule/template.go +++ b/coderd/schedule/template.go @@ -4,6 +4,8 @@ import ( "context" "time" + "golang.org/x/xerrors" + "github.com/google/uuid" "github.com/coder/coder/coderd/database" @@ -89,11 +91,14 @@ func (*agplTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context template, err = db.GetTemplateByID(ctx, tpl.ID) if err != nil { - return err + return xerrors.Errorf("fetch updated template: %w", err) } return nil }, nil) + if err != nil { + return database.Template{}, err + } return template, err } diff --git a/docs/admin/audit-logs.md b/docs/admin/audit-logs.md index 14b45ad6265b0..868a926946e00 100644 --- a/docs/admin/audit-logs.md +++ b/docs/admin/audit-logs.md @@ -9,19 +9,19 @@ We track the following resources: -| Resource | | -| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| APIKey
login, logout, register, create, delete |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| -| AuditOAuthConvertState
|
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| -| Group
create, write, delete |
FieldTracked
avatar_urltrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
| -| GitSSHKey
create |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| -| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| -| Template
write, delete |
FieldTracked
active_version_idtrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
created_atfalse
created_bytrue
created_by_avatar_urltrue
created_by_usernametrue
default_ttltrue
deletedfalse
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
inactivity_ttltrue
locked_ttltrue
max_ttltrue
nametrue
organization_idfalse
provisionertrue
updated_atfalse
user_acltrue
| -| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
git_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| -| User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typetrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| -| Workspace
create, write, delete |
FieldTracked
autostart_scheduletrue
created_atfalse
deletedfalse
idtrue
last_used_atfalse
locked_attrue
nametrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| -| WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| -| WorkspaceProxy
|
FieldTracked
created_attrue
deletedfalse
display_nametrue
icontrue
idtrue
nametrue
token_hashed_secrettrue
updated_atfalse
urltrue
wildcard_hostnametrue
| +| Resource | | +| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| APIKey
login, logout, register, create, delete |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| +| AuditOAuthConvertState
|
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| +| Group
create, write, delete |
FieldTracked
avatar_urltrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
| +| GitSSHKey
create |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| +| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| +| Template
write, delete |
FieldTracked
active_version_idtrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
default_ttltrue
deletedfalse
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
inactivity_ttltrue
locked_ttltrue
max_ttltrue
nametrue
organization_idfalse
provisionertrue
updated_atfalse
user_acltrue
| +| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
git_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| +| User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typetrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| +| Workspace
create, write, delete |
FieldTracked
autostart_scheduletrue
created_atfalse
deletedfalse
idtrue
last_used_atfalse
locked_attrue
nametrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| +| WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| +| WorkspaceProxy
|
FieldTracked
created_attrue
deletedfalse
display_nametrue
icontrue
idtrue
nametrue
token_hashed_secrettrue
updated_atfalse
urltrue
wildcard_hostnametrue
| From fc4a4002c8b9b3fabd83f238feec4ad937f4a4df Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 19 Jul 2023 08:48:53 -0400 Subject: [PATCH 15/16] Use right ID --- enterprise/coderd/provisionerdaemons.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index 4d424a10b24a6..c02d5a1779fe6 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -372,7 +372,7 @@ func (*EnterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.C // This limitation is displayed to the user while editing the template. if opts.MaxTTL > 0 { err = db.UpdateWorkspaceTTLToBeWithinTemplateMax(ctx, database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams{ - TemplateID: template.ID, + TemplateID: tpl.ID, TemplateMaxTTL: int64(opts.MaxTTL), }) if err != nil { From 107bb45b016ca0a7769f04d3f2bab69bce42e77e Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 19 Jul 2023 14:17:02 -0400 Subject: [PATCH 16/16] clarify function name, wrap an error --- coderd/database/dbfake/dbfake.go | 14 +++++++------- coderd/schedule/template.go | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 9f60c60f18534..3813e535a8e57 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -446,21 +446,21 @@ func (q *FakeQuerier) getLatestWorkspaceBuildByWorkspaceIDNoLock(_ context.Conte func (q *FakeQuerier) getTemplateByIDNoLock(_ context.Context, id uuid.UUID) (database.Template, error) { for _, template := range q.templates { if template.ID == id { - return q.templateWithUser(template), nil + return q.templateWithUserNoLock(template), nil } } return database.Template{}, sql.ErrNoRows } -func (q *FakeQuerier) templatesWithUser(tpl []database.TemplateTable) []database.Template { +func (q *FakeQuerier) templatesWithUserNoLock(tpl []database.TemplateTable) []database.Template { cpy := make([]database.Template, 0, len(tpl)) for _, t := range tpl { - cpy = append(cpy, q.templateWithUser(t)) + cpy = append(cpy, q.templateWithUserNoLock(t)) } return cpy } -func (q *FakeQuerier) templateWithUser(tpl database.TemplateTable) database.Template { +func (q *FakeQuerier) templateWithUserNoLock(tpl database.TemplateTable) database.Template { var user database.User for _, _user := range q.users { if _user.ID == tpl.CreatedBy { @@ -1894,7 +1894,7 @@ func (q *FakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg da if template.Deleted != arg.Deleted { continue } - return q.templateWithUser(template), nil + return q.templateWithUserNoLock(template), nil } return database.Template{}, sql.ErrNoRows } @@ -2124,7 +2124,7 @@ func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.Template, erro return i.ID.String() < j.ID.String() }) - return q.templatesWithUser(templates), nil + return q.templatesWithUserNoLock(templates), nil } func (q *FakeQuerier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.Template, error) { @@ -5007,7 +5007,7 @@ func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.G var templates []database.Template for _, templateTable := range q.templates { - template := q.templateWithUser(templateTable) + template := q.templateWithUserNoLock(templateTable) if prepared != nil && prepared.Authorize(ctx, template.RBACObject()) != nil { continue } diff --git a/coderd/schedule/template.go b/coderd/schedule/template.go index ff6bb39ac9e6e..d1782871fea44 100644 --- a/coderd/schedule/template.go +++ b/coderd/schedule/template.go @@ -86,7 +86,7 @@ func (*agplTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context LockedTTL: tpl.LockedTTL, }) if err != nil { - return err + return xerrors.Errorf("update template schedule: %w", err) } template, err = db.GetTemplateByID(ctx, tpl.ID)