mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
feat: update workspace deadline when template policy changes (#8964)
This commit is contained in:
@ -784,6 +784,14 @@ func (q *querier) GetActiveUserCount(ctx context.Context) (int64, error) {
|
||||
return q.db.GetActiveUserCount(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, templateID uuid.UUID) ([]database.WorkspaceBuild, error) {
|
||||
// This is a system-only function.
|
||||
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil {
|
||||
return []database.WorkspaceBuild{}, err
|
||||
}
|
||||
return q.db.GetActiveWorkspaceBuildsByTemplateID(ctx, templateID)
|
||||
}
|
||||
|
||||
func (q *querier) GetAllTailnetAgents(ctx context.Context) ([]database.TailnetAgent, error) {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTailnetCoordinator); err != nil {
|
||||
return []database.TailnetAgent{}, err
|
||||
|
@ -918,6 +918,34 @@ func (q *FakeQuerier) GetActiveUserCount(_ context.Context) (int64, error) {
|
||||
return active, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, templateID uuid.UUID) ([]database.WorkspaceBuild, error) {
|
||||
workspaceIDs := func() []uuid.UUID {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
ids := []uuid.UUID{}
|
||||
for _, workspace := range q.workspaces {
|
||||
if workspace.TemplateID == templateID {
|
||||
ids = append(ids, workspace.ID)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}()
|
||||
|
||||
builds, err := q.GetLatestWorkspaceBuildsByWorkspaceIDs(ctx, workspaceIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filteredBuilds := []database.WorkspaceBuild{}
|
||||
for _, build := range builds {
|
||||
if build.Transition == database.WorkspaceTransitionStart {
|
||||
filteredBuilds = append(filteredBuilds, build)
|
||||
}
|
||||
}
|
||||
return filteredBuilds, nil
|
||||
}
|
||||
|
||||
func (*FakeQuerier) GetAllTailnetAgents(_ context.Context) ([]database.TailnetAgent, error) {
|
||||
return nil, ErrUnimplemented
|
||||
}
|
||||
|
@ -237,6 +237,13 @@ func (m metricsStore) GetActiveUserCount(ctx context.Context) (int64, error) {
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (m metricsStore) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, templateID uuid.UUID) ([]database.WorkspaceBuild, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetActiveWorkspaceBuildsByTemplateID(ctx, templateID)
|
||||
m.queryLatencies.WithLabelValues("GetActiveWorkspaceBuildsByTemplateID").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetAllTailnetAgents(ctx context.Context) ([]database.TailnetAgent, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetAllTailnetAgents(ctx)
|
||||
|
@ -371,6 +371,21 @@ func (mr *MockStoreMockRecorder) GetActiveUserCount(arg0 interface{}) *gomock.Ca
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveUserCount", reflect.TypeOf((*MockStore)(nil).GetActiveUserCount), arg0)
|
||||
}
|
||||
|
||||
// GetActiveWorkspaceBuildsByTemplateID mocks base method.
|
||||
func (m *MockStore) GetActiveWorkspaceBuildsByTemplateID(arg0 context.Context, arg1 uuid.UUID) ([]database.WorkspaceBuild, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetActiveWorkspaceBuildsByTemplateID", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.WorkspaceBuild)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetActiveWorkspaceBuildsByTemplateID indicates an expected call of GetActiveWorkspaceBuildsByTemplateID.
|
||||
func (mr *MockStoreMockRecorder) GetActiveWorkspaceBuildsByTemplateID(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveWorkspaceBuildsByTemplateID", reflect.TypeOf((*MockStore)(nil).GetActiveWorkspaceBuildsByTemplateID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetAllTailnetAgents mocks base method.
|
||||
func (m *MockStore) GetAllTailnetAgents(arg0 context.Context) ([]database.TailnetAgent, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -48,6 +48,7 @@ type sqlcQuerier interface {
|
||||
GetAPIKeysByUserID(ctx context.Context, arg GetAPIKeysByUserIDParams) ([]APIKey, error)
|
||||
GetAPIKeysLastUsedAfter(ctx context.Context, lastUsed time.Time) ([]APIKey, error)
|
||||
GetActiveUserCount(ctx context.Context) (int64, error)
|
||||
GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, templateID uuid.UUID) ([]WorkspaceBuild, error)
|
||||
GetAllTailnetAgents(ctx context.Context) ([]TailnetAgent, error)
|
||||
GetAllTailnetClients(ctx context.Context) ([]TailnetClient, error)
|
||||
GetAppSecurityKey(ctx context.Context) (string, error)
|
||||
|
@ -7884,6 +7884,72 @@ func (q *sqlQuerier) InsertWorkspaceBuildParameters(ctx context.Context, arg Ins
|
||||
return err
|
||||
}
|
||||
|
||||
const getActiveWorkspaceBuildsByTemplateID = `-- name: GetActiveWorkspaceBuildsByTemplateID :many
|
||||
SELECT wb.id, wb.created_at, wb.updated_at, wb.workspace_id, wb.template_version_id, wb.build_number, wb.transition, wb.initiator_id, wb.provisioner_state, wb.job_id, wb.deadline, wb.reason, wb.daily_cost, wb.max_deadline, wb.initiator_by_avatar_url, wb.initiator_by_username
|
||||
FROM (
|
||||
SELECT
|
||||
workspace_id, MAX(build_number) as max_build_number
|
||||
FROM
|
||||
workspace_build_with_user AS workspace_builds
|
||||
WHERE
|
||||
workspace_id IN (
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
template_id = $1
|
||||
)
|
||||
GROUP BY
|
||||
workspace_id
|
||||
) m
|
||||
JOIN
|
||||
workspace_build_with_user AS wb
|
||||
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number
|
||||
WHERE
|
||||
wb.transition = 'start'::workspace_transition
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, templateID uuid.UUID) ([]WorkspaceBuild, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getActiveWorkspaceBuildsByTemplateID, templateID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceBuild
|
||||
for rows.Next() {
|
||||
var i WorkspaceBuild
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.WorkspaceID,
|
||||
&i.TemplateVersionID,
|
||||
&i.BuildNumber,
|
||||
&i.Transition,
|
||||
&i.InitiatorID,
|
||||
&i.ProvisionerState,
|
||||
&i.JobID,
|
||||
&i.Deadline,
|
||||
&i.Reason,
|
||||
&i.DailyCost,
|
||||
&i.MaxDeadline,
|
||||
&i.InitiatorByAvatarUrl,
|
||||
&i.InitiatorByUsername,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getLatestWorkspaceBuildByWorkspaceID = `-- name: GetLatestWorkspaceBuildByWorkspaceID :one
|
||||
SELECT
|
||||
id, created_at, updated_at, workspace_id, template_version_id, build_number, transition, initiator_id, provisioner_state, job_id, deadline, reason, daily_cost, max_deadline, initiator_by_avatar_url, initiator_by_username
|
||||
|
@ -144,3 +144,27 @@ SET
|
||||
WHERE
|
||||
id = $1;
|
||||
|
||||
-- name: GetActiveWorkspaceBuildsByTemplateID :many
|
||||
SELECT wb.*
|
||||
FROM (
|
||||
SELECT
|
||||
workspace_id, MAX(build_number) as max_build_number
|
||||
FROM
|
||||
workspace_build_with_user AS workspace_builds
|
||||
WHERE
|
||||
workspace_id IN (
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
workspaces
|
||||
WHERE
|
||||
template_id = $1
|
||||
)
|
||||
GROUP BY
|
||||
workspace_id
|
||||
) m
|
||||
JOIN
|
||||
workspace_build_with_user AS wb
|
||||
ON m.workspace_id = wb.workspace_id AND m.max_build_number = wb.build_number
|
||||
WHERE
|
||||
wb.transition = 'start'::workspace_transition;
|
||||
|
@ -4,9 +4,12 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/tracing"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -72,6 +75,13 @@ type AutostopTime struct {
|
||||
// Deadline is a cost saving measure, while max deadline is a
|
||||
// compliance/updating measure.
|
||||
func CalculateAutostop(ctx context.Context, params CalculateAutostopParams) (AutostopTime, error) {
|
||||
ctx, span := tracing.StartSpan(ctx,
|
||||
trace.WithAttributes(attribute.String("coder.workspace_id", params.Workspace.ID.String())),
|
||||
trace.WithAttributes(attribute.String("coder.template_id", params.Workspace.TemplateID.String())),
|
||||
)
|
||||
defer span.End()
|
||||
defer span.End()
|
||||
|
||||
var (
|
||||
db = params.Database
|
||||
workspace = params.Workspace
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/tracing"
|
||||
)
|
||||
|
||||
const MaxTemplateRestartRequirementWeeks = 16
|
||||
@ -122,6 +123,9 @@ func NewAGPLTemplateScheduleStore() TemplateScheduleStore {
|
||||
}
|
||||
|
||||
func (*agplTemplateScheduleStore) Get(ctx context.Context, db database.Store, templateID uuid.UUID) (TemplateScheduleOptions, error) {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
tpl, err := db.GetTemplateByID(ctx, templateID)
|
||||
if err != nil {
|
||||
return TemplateScheduleOptions{}, err
|
||||
@ -148,6 +152,9 @@ func (*agplTemplateScheduleStore) Get(ctx context.Context, db database.Store, te
|
||||
}
|
||||
|
||||
func (*agplTemplateScheduleStore) Set(ctx context.Context, db database.Store, tpl database.Template, opts TemplateScheduleOptions) (database.Template, error) {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
if int64(opts.DefaultTTL) == tpl.DefaultTTL {
|
||||
// Avoid updating the UpdatedAt timestamp if nothing will be changed.
|
||||
return tpl, nil
|
||||
|
Reference in New Issue
Block a user