mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat: allow users to pause prebuilt workspace reconciliation (#18700)
This PR provides two commands: * `coder prebuilds pause` * `coder prebuilds resume` These allow the suspension of all prebuilds activity, intended for use if prebuilds are misbehaving.
This commit is contained in:
@ -2304,6 +2304,10 @@ func (q *querier) GetPrebuildMetrics(ctx context.Context) ([]database.GetPrebuil
|
||||
return q.db.GetPrebuildMetrics(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetPrebuildsSettings(ctx context.Context) (string, error) {
|
||||
return q.db.GetPrebuildsSettings(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetPresetByID(ctx context.Context, presetID uuid.UUID) (database.GetPresetByIDRow, error) {
|
||||
empty := database.GetPresetByIDRow{}
|
||||
|
||||
@ -5101,6 +5105,13 @@ func (q *querier) UpsertOAuthSigningKey(ctx context.Context, value string) error
|
||||
return q.db.UpsertOAuthSigningKey(ctx, value)
|
||||
}
|
||||
|
||||
func (q *querier) UpsertPrebuildsSettings(ctx context.Context, value string) error {
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
return q.db.UpsertPrebuildsSettings(ctx, value)
|
||||
}
|
||||
|
||||
func (q *querier) UpsertProvisionerDaemon(ctx context.Context, arg database.UpsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
|
||||
res := rbac.ResourceProvisionerDaemon.InOrg(arg.OrganizationID)
|
||||
if arg.Tags[provisionersdk.TagScope] == provisionersdk.ScopeUser {
|
||||
|
@ -5071,6 +5071,12 @@ func (s *MethodTestSuite) TestPrebuilds() {
|
||||
check.Args().
|
||||
Asserts(rbac.ResourceWorkspace.All(), policy.ActionRead)
|
||||
}))
|
||||
s.Run("GetPrebuildsSettings", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args().Asserts()
|
||||
}))
|
||||
s.Run("UpsertPrebuildsSettings", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args("foo").Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate)
|
||||
}))
|
||||
s.Run("CountInProgressPrebuilds", s.Subtest(func(_ database.Store, check *expects) {
|
||||
check.Args().
|
||||
Asserts(rbac.ResourceWorkspace.All(), policy.ActionRead).
|
||||
|
@ -297,6 +297,7 @@ type data struct {
|
||||
presets []database.TemplateVersionPreset
|
||||
presetParameters []database.TemplateVersionPresetParameter
|
||||
presetPrebuildSchedules []database.TemplateVersionPresetPrebuildSchedule
|
||||
prebuildsSettings []byte
|
||||
}
|
||||
|
||||
func tryPercentileCont(fs []float64, p float64) float64 {
|
||||
@ -4277,7 +4278,14 @@ func (*FakeQuerier) GetPrebuildMetrics(_ context.Context) ([]database.GetPrebuil
|
||||
return make([]database.GetPrebuildMetricsRow, 0), nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetPresetByID(ctx context.Context, presetID uuid.UUID) (database.GetPresetByIDRow, error) {
|
||||
func (q *FakeQuerier) GetPrebuildsSettings(_ context.Context) (string, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
return string(slices.Clone(q.prebuildsSettings)), nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetPresetByID(_ context.Context, presetID uuid.UUID) (database.GetPresetByIDRow, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
@ -12316,6 +12324,14 @@ func (q *FakeQuerier) UpsertOAuthSigningKey(_ context.Context, value string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpsertPrebuildsSettings(_ context.Context, value string) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
q.prebuildsSettings = []byte(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpsertProvisionerDaemon(_ context.Context, arg database.UpsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return database.ProvisionerDaemon{}, err
|
||||
|
@ -1103,6 +1103,13 @@ func (m queryMetricsStore) GetPrebuildMetrics(ctx context.Context) ([]database.G
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetPrebuildsSettings(ctx context.Context) (string, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetPrebuildsSettings(ctx)
|
||||
m.queryLatencies.WithLabelValues("GetPrebuildsSettings").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetPresetByID(ctx context.Context, presetID uuid.UUID) (database.GetPresetByIDRow, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetPresetByID(ctx, presetID)
|
||||
@ -3175,6 +3182,13 @@ func (m queryMetricsStore) UpsertOAuthSigningKey(ctx context.Context, value stri
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) UpsertPrebuildsSettings(ctx context.Context, value string) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpsertPrebuildsSettings(ctx, value)
|
||||
m.queryLatencies.WithLabelValues("UpsertPrebuildsSettings").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) UpsertProvisionerDaemon(ctx context.Context, arg database.UpsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.UpsertProvisionerDaemon(ctx, arg)
|
||||
|
@ -2283,6 +2283,21 @@ func (mr *MockStoreMockRecorder) GetPrebuildMetrics(ctx any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPrebuildMetrics", reflect.TypeOf((*MockStore)(nil).GetPrebuildMetrics), ctx)
|
||||
}
|
||||
|
||||
// GetPrebuildsSettings mocks base method.
|
||||
func (m *MockStore) GetPrebuildsSettings(ctx context.Context) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetPrebuildsSettings", ctx)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetPrebuildsSettings indicates an expected call of GetPrebuildsSettings.
|
||||
func (mr *MockStoreMockRecorder) GetPrebuildsSettings(ctx any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPrebuildsSettings", reflect.TypeOf((*MockStore)(nil).GetPrebuildsSettings), ctx)
|
||||
}
|
||||
|
||||
// GetPresetByID mocks base method.
|
||||
func (m *MockStore) GetPresetByID(ctx context.Context, presetID uuid.UUID) (database.GetPresetByIDRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -6719,6 +6734,20 @@ func (mr *MockStoreMockRecorder) UpsertOAuthSigningKey(ctx, value any) *gomock.C
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertOAuthSigningKey", reflect.TypeOf((*MockStore)(nil).UpsertOAuthSigningKey), ctx, value)
|
||||
}
|
||||
|
||||
// UpsertPrebuildsSettings mocks base method.
|
||||
func (m *MockStore) UpsertPrebuildsSettings(ctx context.Context, value string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpsertPrebuildsSettings", ctx, value)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpsertPrebuildsSettings indicates an expected call of UpsertPrebuildsSettings.
|
||||
func (mr *MockStoreMockRecorder) UpsertPrebuildsSettings(ctx, value any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertPrebuildsSettings", reflect.TypeOf((*MockStore)(nil).UpsertPrebuildsSettings), ctx, value)
|
||||
}
|
||||
|
||||
// UpsertProvisionerDaemon mocks base method.
|
||||
func (m *MockStore) UpsertProvisionerDaemon(ctx context.Context, arg database.UpsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
3
coderd/database/dump.sql
generated
3
coderd/database/dump.sql
generated
@ -242,7 +242,8 @@ CREATE TYPE resource_type AS ENUM (
|
||||
'idp_sync_settings_group',
|
||||
'idp_sync_settings_role',
|
||||
'workspace_agent',
|
||||
'workspace_app'
|
||||
'workspace_app',
|
||||
'prebuilds_settings'
|
||||
);
|
||||
|
||||
CREATE TYPE startup_script_behavior AS ENUM (
|
||||
|
@ -0,0 +1 @@
|
||||
-- No-op, enum values can't be dropped.
|
@ -0,0 +1,2 @@
|
||||
ALTER TYPE resource_type
|
||||
ADD VALUE IF NOT EXISTS 'prebuilds_settings';
|
@ -1894,6 +1894,7 @@ const (
|
||||
ResourceTypeIdpSyncSettingsRole ResourceType = "idp_sync_settings_role"
|
||||
ResourceTypeWorkspaceAgent ResourceType = "workspace_agent"
|
||||
ResourceTypeWorkspaceApp ResourceType = "workspace_app"
|
||||
ResourceTypePrebuildsSettings ResourceType = "prebuilds_settings"
|
||||
)
|
||||
|
||||
func (e *ResourceType) Scan(src interface{}) error {
|
||||
@ -1956,7 +1957,8 @@ func (e ResourceType) Valid() bool {
|
||||
ResourceTypeIdpSyncSettingsGroup,
|
||||
ResourceTypeIdpSyncSettingsRole,
|
||||
ResourceTypeWorkspaceAgent,
|
||||
ResourceTypeWorkspaceApp:
|
||||
ResourceTypeWorkspaceApp,
|
||||
ResourceTypePrebuildsSettings:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -1988,6 +1990,7 @@ func AllResourceTypeValues() []ResourceType {
|
||||
ResourceTypeIdpSyncSettingsRole,
|
||||
ResourceTypeWorkspaceAgent,
|
||||
ResourceTypeWorkspaceApp,
|
||||
ResourceTypePrebuildsSettings,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,6 +236,7 @@ type sqlcQuerier interface {
|
||||
GetOrganizationsByUserID(ctx context.Context, arg GetOrganizationsByUserIDParams) ([]Organization, error)
|
||||
GetParameterSchemasByJobID(ctx context.Context, jobID uuid.UUID) ([]ParameterSchema, error)
|
||||
GetPrebuildMetrics(ctx context.Context) ([]GetPrebuildMetricsRow, error)
|
||||
GetPrebuildsSettings(ctx context.Context) (string, error)
|
||||
GetPresetByID(ctx context.Context, presetID uuid.UUID) (GetPresetByIDRow, error)
|
||||
GetPresetByWorkspaceBuildID(ctx context.Context, workspaceBuildID uuid.UUID) (TemplateVersionPreset, error)
|
||||
GetPresetParametersByPresetID(ctx context.Context, presetID uuid.UUID) ([]TemplateVersionPresetParameter, error)
|
||||
@ -651,6 +652,7 @@ type sqlcQuerier interface {
|
||||
UpsertNotificationsSettings(ctx context.Context, value string) error
|
||||
UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error
|
||||
UpsertOAuthSigningKey(ctx context.Context, value string) error
|
||||
UpsertPrebuildsSettings(ctx context.Context, value string) error
|
||||
UpsertProvisionerDaemon(ctx context.Context, arg UpsertProvisionerDaemonParams) (ProvisionerDaemon, error)
|
||||
UpsertRuntimeConfig(ctx context.Context, arg UpsertRuntimeConfigParams) error
|
||||
UpsertTailnetAgent(ctx context.Context, arg UpsertTailnetAgentParams) (TailnetAgent, error)
|
||||
|
@ -9604,6 +9604,18 @@ func (q *sqlQuerier) GetOAuthSigningKey(ctx context.Context) (string, error) {
|
||||
return value, err
|
||||
}
|
||||
|
||||
const getPrebuildsSettings = `-- name: GetPrebuildsSettings :one
|
||||
SELECT
|
||||
COALESCE((SELECT value FROM site_configs WHERE key = 'prebuilds_settings'), '{}') :: text AS prebuilds_settings
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetPrebuildsSettings(ctx context.Context) (string, error) {
|
||||
row := q.db.QueryRowContext(ctx, getPrebuildsSettings)
|
||||
var prebuilds_settings string
|
||||
err := row.Scan(&prebuilds_settings)
|
||||
return prebuilds_settings, err
|
||||
}
|
||||
|
||||
const getRuntimeConfig = `-- name: GetRuntimeConfig :one
|
||||
SELECT value FROM site_configs WHERE site_configs.key = $1
|
||||
`
|
||||
@ -9786,6 +9798,16 @@ func (q *sqlQuerier) UpsertOAuthSigningKey(ctx context.Context, value string) er
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertPrebuildsSettings = `-- name: UpsertPrebuildsSettings :exec
|
||||
INSERT INTO site_configs (key, value) VALUES ('prebuilds_settings', $1)
|
||||
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'prebuilds_settings'
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) UpsertPrebuildsSettings(ctx context.Context, value string) error {
|
||||
_, err := q.db.ExecContext(ctx, upsertPrebuildsSettings, value)
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertRuntimeConfig = `-- name: UpsertRuntimeConfig :exec
|
||||
INSERT INTO site_configs (key, value) VALUES ($1, $2)
|
||||
ON CONFLICT (key) DO UPDATE SET value = $2 WHERE site_configs.key = $1
|
||||
|
@ -96,6 +96,15 @@ SELECT
|
||||
INSERT INTO site_configs (key, value) VALUES ('notifications_settings', $1)
|
||||
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'notifications_settings';
|
||||
|
||||
-- name: GetPrebuildsSettings :one
|
||||
SELECT
|
||||
COALESCE((SELECT value FROM site_configs WHERE key = 'prebuilds_settings'), '{}') :: text AS prebuilds_settings
|
||||
;
|
||||
|
||||
-- name: UpsertPrebuildsSettings :exec
|
||||
INSERT INTO site_configs (key, value) VALUES ('prebuilds_settings', $1)
|
||||
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'prebuilds_settings';
|
||||
|
||||
-- name: GetRuntimeConfig :one
|
||||
SELECT value FROM site_configs WHERE site_configs.key = $1;
|
||||
|
||||
|
@ -35,6 +35,11 @@ type NotificationsSettings struct {
|
||||
NotifierPaused bool `db:"notifier_paused" json:"notifier_paused"`
|
||||
}
|
||||
|
||||
type PrebuildsSettings struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
ReconciliationPaused bool `db:"reconciliation_paused" json:"reconciliation_paused"`
|
||||
}
|
||||
|
||||
type Actions []policy.Action
|
||||
|
||||
func (a *Actions) Scan(src interface{}) error {
|
||||
|
Reference in New Issue
Block a user