mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: wire up usage tracking for managed agents (#19096)
Wires up the usage collector and publisher to coderd. Relates to coder/internal#814
This commit is contained in:
@@ -163,7 +163,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
agentID, err := uuid.FromBytes(createResp.Agent.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
agent, err := api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), agentID) //nolint:gocritic // this is a test.
|
||||
agent, err := api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), agentID)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.agentName, agent.Name)
|
||||
@@ -621,7 +621,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
agentID, err := uuid.FromBytes(createResp.Agent.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
apps, err := api.Database.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), agentID) //nolint:gocritic // this is a test.
|
||||
apps, err := api.Database.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), agentID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Sort the apps for determinism
|
||||
@@ -751,7 +751,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
agentID, err := uuid.FromBytes(createResp.Agent.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
apps, err := db.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), agentID) //nolint:gocritic // this is a test.
|
||||
apps, err := db.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), agentID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 1)
|
||||
require.Equal(t, "k5jd7a99-duplicate-slug", apps[0].Slug)
|
||||
@@ -789,7 +789,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Then: It is deleted.
|
||||
_, err = db.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), childAgent.ID) //nolint:gocritic // this is a test.
|
||||
_, err = db.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), childAgent.ID)
|
||||
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||
})
|
||||
|
||||
@@ -830,10 +830,10 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Then: The correct one is deleted.
|
||||
_, err = api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), childAgentOne.ID) //nolint:gocritic // this is a test.
|
||||
_, err = api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), childAgentOne.ID)
|
||||
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||
|
||||
_, err = api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), childAgentTwo.ID) //nolint:gocritic // this is a test.
|
||||
_, err = api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), childAgentTwo.ID)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@@ -871,7 +871,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
var notAuthorizedError dbauthz.NotAuthorizedError
|
||||
require.ErrorAs(t, err, ¬AuthorizedError)
|
||||
|
||||
_, err = db.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), childAgentOne.ID) //nolint:gocritic // this is a test.
|
||||
_, err = db.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), childAgentOne.ID)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@@ -912,7 +912,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify that the apps were created
|
||||
apps, err := api.Database.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), subAgentID) //nolint:gocritic // this is a test.
|
||||
apps, err := api.Database.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), subAgentID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 2)
|
||||
|
||||
@@ -923,7 +923,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Then: The agent is deleted
|
||||
_, err = api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), subAgentID) //nolint:gocritic // this is a test.
|
||||
_, err = api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), subAgentID)
|
||||
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||
|
||||
// And: The apps are *retained* to avoid causing issues
|
||||
@@ -1068,7 +1068,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
agentID, err := uuid.FromBytes(createResp.Agent.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
subAgent, err := api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), agentID) //nolint:gocritic // this is a test.
|
||||
subAgent, err := api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), agentID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(tt.expectedApps), len(subAgent.DisplayApps), "display apps count mismatch")
|
||||
@@ -1118,14 +1118,14 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify display apps
|
||||
subAgent, err := api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), agentID) //nolint:gocritic // this is a test.
|
||||
subAgent, err := api.Database.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), agentID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, subAgent.DisplayApps, 2)
|
||||
require.Equal(t, database.DisplayAppVscode, subAgent.DisplayApps[0])
|
||||
require.Equal(t, database.DisplayAppWebTerminal, subAgent.DisplayApps[1])
|
||||
|
||||
// Verify regular apps
|
||||
apps, err := api.Database.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), agentID) //nolint:gocritic // this is a test.
|
||||
apps, err := api.Database.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), agentID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 1)
|
||||
require.Equal(t, "v4qhkq17-custom-app", apps[0].Slug)
|
||||
@@ -1190,7 +1190,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
})
|
||||
|
||||
// When: We list the sub agents.
|
||||
listResp, err := api.ListSubAgents(ctx, &proto.ListSubAgentsRequest{}) //nolint:gocritic // this is a test.
|
||||
listResp, err := api.ListSubAgents(ctx, &proto.ListSubAgentsRequest{})
|
||||
require.NoError(t, err)
|
||||
|
||||
listedChildAgents := listResp.Agents
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/oauth2provider"
|
||||
"github.com/coder/coder/v2/coderd/pproflabel"
|
||||
"github.com/coder/coder/v2/coderd/prebuilds"
|
||||
"github.com/coder/coder/v2/coderd/usage"
|
||||
"github.com/coder/coder/v2/coderd/wsbuilder"
|
||||
|
||||
"github.com/andybalholm/brotli"
|
||||
@@ -200,6 +201,7 @@ type Options struct {
|
||||
TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
|
||||
UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
|
||||
AccessControlStore *atomic.Pointer[dbauthz.AccessControlStore]
|
||||
UsageInserter *atomic.Pointer[usage.Inserter]
|
||||
// CoordinatorResumeTokenProvider is used to provide and validate resume
|
||||
// tokens issued by and passed to the coordinator DRPC API.
|
||||
CoordinatorResumeTokenProvider tailnet.ResumeTokenProvider
|
||||
@@ -428,6 +430,13 @@ func New(options *Options) *API {
|
||||
v := schedule.NewAGPLUserQuietHoursScheduleStore()
|
||||
options.UserQuietHoursScheduleStore.Store(&v)
|
||||
}
|
||||
if options.UsageInserter == nil {
|
||||
options.UsageInserter = &atomic.Pointer[usage.Inserter]{}
|
||||
}
|
||||
if options.UsageInserter.Load() == nil {
|
||||
inserter := usage.NewAGPLInserter()
|
||||
options.UsageInserter.Store(&inserter)
|
||||
}
|
||||
if options.OneTimePasscodeValidityPeriod == 0 {
|
||||
options.OneTimePasscodeValidityPeriod = 20 * time.Minute
|
||||
}
|
||||
@@ -590,6 +599,7 @@ func New(options *Options) *API {
|
||||
UserQuietHoursScheduleStore: options.UserQuietHoursScheduleStore,
|
||||
AccessControlStore: options.AccessControlStore,
|
||||
BuildUsageChecker: &buildUsageChecker,
|
||||
UsageInserter: options.UsageInserter,
|
||||
FileCache: files.New(options.PrometheusRegistry, options.Authorizer),
|
||||
Experiments: experiments,
|
||||
WebpushDispatcher: options.WebPushDispatcher,
|
||||
@@ -1690,6 +1700,9 @@ type API struct {
|
||||
// BuildUsageChecker is a pointer as it's passed around to multiple
|
||||
// components.
|
||||
BuildUsageChecker *atomic.Pointer[wsbuilder.UsageChecker]
|
||||
// UsageInserter is a pointer to an atomic pointer because it is passed to
|
||||
// multiple components.
|
||||
UsageInserter *atomic.Pointer[usage.Inserter]
|
||||
|
||||
UpdatesProvider tailnet.WorkspaceUpdatesProvider
|
||||
|
||||
@@ -1905,6 +1918,7 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
|
||||
&api.Auditor,
|
||||
api.TemplateScheduleStore,
|
||||
api.UserQuietHoursScheduleStore,
|
||||
api.UsageInserter,
|
||||
api.DeploymentValues,
|
||||
provisionerdserver.Options{
|
||||
OIDCConfig: api.OIDCConfig,
|
||||
|
||||
@@ -213,6 +213,8 @@ var (
|
||||
// Provisionerd creates workspaces resources monitor
|
||||
rbac.ResourceWorkspaceAgentResourceMonitor.Type: {policy.ActionCreate},
|
||||
rbac.ResourceWorkspaceAgentDevcontainers.Type: {policy.ActionCreate},
|
||||
// Provisionerd creates usage events
|
||||
rbac.ResourceUsageEvent.Type: {policy.ActionCreate},
|
||||
}),
|
||||
Org: map[string][]rbac.Permission{},
|
||||
User: []rbac.Permission{},
|
||||
@@ -510,17 +512,19 @@ var (
|
||||
Scope: rbac.ScopeAll,
|
||||
}.WithCachedASTValue()
|
||||
|
||||
subjectUsageTracker = rbac.Subject{
|
||||
Type: rbac.SubjectTypeUsageTracker,
|
||||
FriendlyName: "Usage Tracker",
|
||||
subjectUsagePublisher = rbac.Subject{
|
||||
Type: rbac.SubjectTypeUsagePublisher,
|
||||
FriendlyName: "Usage Publisher",
|
||||
ID: uuid.Nil.String(),
|
||||
Roles: rbac.Roles([]rbac.Role{
|
||||
{
|
||||
Identifier: rbac.RoleIdentifier{Name: "usage-tracker"},
|
||||
DisplayName: "Usage Tracker",
|
||||
Identifier: rbac.RoleIdentifier{Name: "usage-publisher"},
|
||||
DisplayName: "Usage Publisher",
|
||||
Site: rbac.Permissions(map[string][]policy.Action{
|
||||
rbac.ResourceLicense.Type: {policy.ActionRead},
|
||||
rbac.ResourceUsageEvent.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate},
|
||||
rbac.ResourceLicense.Type: {policy.ActionRead},
|
||||
// The usage publisher doesn't create events, just
|
||||
// reads/processes them.
|
||||
rbac.ResourceUsageEvent.Type: {policy.ActionRead, policy.ActionUpdate},
|
||||
}),
|
||||
Org: map[string][]rbac.Permission{},
|
||||
User: []rbac.Permission{},
|
||||
@@ -604,10 +608,10 @@ func AsFileReader(ctx context.Context) context.Context {
|
||||
return As(ctx, subjectFileReader)
|
||||
}
|
||||
|
||||
// AsUsageTracker returns a context with an actor that has permissions required
|
||||
// for creating, reading, and updating usage events.
|
||||
func AsUsageTracker(ctx context.Context) context.Context {
|
||||
return As(ctx, subjectUsageTracker)
|
||||
// AsUsagePublisher returns a context with an actor that has permissions
|
||||
// required for creating, reading, and updating usage events.
|
||||
func AsUsagePublisher(ctx context.Context) context.Context {
|
||||
return As(ctx, subjectUsagePublisher)
|
||||
}
|
||||
|
||||
var AsRemoveActor = rbac.Subject{
|
||||
@@ -3038,7 +3042,7 @@ func (q *querier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTe
|
||||
}
|
||||
|
||||
func (q *querier) GetUnexpiredLicenses(ctx context.Context) ([]database.License, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceLicense); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetUnexpiredLicenses(ctx)
|
||||
|
||||
@@ -758,6 +758,18 @@ func (s *MethodTestSuite) TestLicense() {
|
||||
check.Args().Asserts(l, policy.ActionRead).
|
||||
Returns([]database.License{l})
|
||||
}))
|
||||
s.Run("GetUnexpiredLicenses", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||
l := database.License{
|
||||
ID: 1,
|
||||
Exp: time.Now().Add(time.Hour * 24 * 30),
|
||||
UUID: uuid.New(),
|
||||
}
|
||||
db.EXPECT().GetUnexpiredLicenses(gomock.Any()).
|
||||
Return([]database.License{l}, nil).
|
||||
AnyTimes()
|
||||
check.Args().Asserts(rbac.ResourceLicense, policy.ActionRead).
|
||||
Returns([]database.License{l})
|
||||
}))
|
||||
s.Run("InsertLicense", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args(database.InsertLicenseParams{}).
|
||||
Asserts(rbac.ResourceLicense, policy.ActionCreate)
|
||||
@@ -3770,9 +3782,6 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||
s.Run("GetActiveUserCount", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args(false).Asserts(rbac.ResourceSystem, policy.ActionRead).Returns(int64(0))
|
||||
}))
|
||||
s.Run("GetUnexpiredLicenses", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args().Asserts(rbac.ResourceSystem, policy.ActionRead)
|
||||
}))
|
||||
s.Run("GetAuthorizationUserRoles", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
check.Args(u.ID).Asserts(rbac.ResourceSystem, policy.ActionRead)
|
||||
|
||||
@@ -337,7 +337,6 @@ func TestRefreshToken(t *testing.T) {
|
||||
require.Equal(t, 1, validateCalls, "token is validated")
|
||||
require.Equal(t, 1, refreshCalls, "token is refreshed")
|
||||
require.NotEqualf(t, link.OAuthAccessToken, updated.OAuthAccessToken, "token is updated")
|
||||
//nolint:gocritic // testing
|
||||
dbLink, err := db.GetExternalAuthLink(dbauthz.AsSystemRestricted(context.Background()), database.GetExternalAuthLinkParams{
|
||||
ProviderID: link.ProviderID,
|
||||
UserID: link.UserID,
|
||||
|
||||
@@ -45,7 +45,6 @@ func TestCancelledFetch(t *testing.T) {
|
||||
cache := files.New(prometheus.NewRegistry(), &coderdtest.FakeAuthorizer{})
|
||||
|
||||
// Cancel the context for the first call; should fail.
|
||||
//nolint:gocritic // Unit testing
|
||||
ctx, cancel := context.WithCancel(dbauthz.AsFileReader(testutil.Context(t, testutil.WaitShort)))
|
||||
cancel()
|
||||
_, err := cache.Acquire(ctx, dbM, fileID)
|
||||
@@ -71,7 +70,6 @@ func TestCancelledConcurrentFetch(t *testing.T) {
|
||||
|
||||
cache := files.LeakCache{Cache: files.New(prometheus.NewRegistry(), &coderdtest.FakeAuthorizer{})}
|
||||
|
||||
//nolint:gocritic // Unit testing
|
||||
ctx := dbauthz.AsFileReader(testutil.Context(t, testutil.WaitShort))
|
||||
|
||||
// Cancel the context for the first call; should fail.
|
||||
@@ -99,7 +97,6 @@ func TestConcurrentFetch(t *testing.T) {
|
||||
})
|
||||
|
||||
cache := files.New(prometheus.NewRegistry(), &coderdtest.FakeAuthorizer{})
|
||||
//nolint:gocritic // Unit testing
|
||||
ctx := dbauthz.AsFileReader(testutil.Context(t, testutil.WaitShort))
|
||||
|
||||
// Expect 2 calls to Acquire before we continue the test
|
||||
@@ -151,7 +148,6 @@ func TestCacheRBAC(t *testing.T) {
|
||||
Scope: rbac.ScopeAll,
|
||||
})
|
||||
|
||||
//nolint:gocritic // Unit testing
|
||||
cacheReader := dbauthz.AsFileReader(ctx)
|
||||
|
||||
t.Run("NoRolesOpen", func(t *testing.T) {
|
||||
@@ -207,7 +203,6 @@ func cachePromMetricName(metric string) string {
|
||||
|
||||
func TestConcurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
//nolint:gocritic // Unit testing
|
||||
ctx := dbauthz.AsFileReader(t.Context())
|
||||
|
||||
const fileSize = 10
|
||||
@@ -268,7 +263,6 @@ func TestConcurrency(t *testing.T) {
|
||||
|
||||
func TestRelease(t *testing.T) {
|
||||
t.Parallel()
|
||||
//nolint:gocritic // Unit testing
|
||||
ctx := dbauthz.AsFileReader(t.Context())
|
||||
|
||||
const fileSize = 10
|
||||
|
||||
@@ -328,7 +328,6 @@ func TestGroupSyncTable(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
//nolint:gocritic // testing
|
||||
defOrg, err := db.GetDefaultOrganization(dbauthz.AsSystemRestricted(ctx))
|
||||
require.NoError(t, err)
|
||||
SetupOrganization(t, s, db, user, defOrg.ID, def)
|
||||
@@ -527,7 +526,6 @@ func TestApplyGroupDifference(t *testing.T) {
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
//nolint:gocritic // testing
|
||||
ctx = dbauthz.AsSystemRestricted(ctx)
|
||||
|
||||
org := dbgen.Organization(t, db, database.Organization{})
|
||||
|
||||
@@ -273,7 +273,6 @@ func TestRoleSyncTable(t *testing.T) {
|
||||
}
|
||||
|
||||
// Also assert site wide roles
|
||||
//nolint:gocritic // unit testing assertions
|
||||
allRoles, err := db.GetAuthorizationUserRoles(dbauthz.AsSystemRestricted(ctx), user.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -754,7 +754,6 @@ func TestTemplateInsights_Golden(t *testing.T) {
|
||||
Database: db,
|
||||
AppStatBatchSize: workspaceapps.DefaultStatsDBReporterBatchSize,
|
||||
})
|
||||
//nolint:gocritic // This is a test.
|
||||
err = reporter.ReportAppStats(dbauthz.AsSystemRestricted(ctx), stats)
|
||||
require.NoError(t, err, "want no error inserting app stats")
|
||||
|
||||
@@ -1646,7 +1645,6 @@ func TestUserActivityInsights_Golden(t *testing.T) {
|
||||
Database: db,
|
||||
AppStatBatchSize: workspaceapps.DefaultStatsDBReporterBatchSize,
|
||||
})
|
||||
//nolint:gocritic // This is a test.
|
||||
err = reporter.ReportAppStats(dbauthz.AsSystemRestricted(ctx), stats)
|
||||
require.NoError(t, err, "want no error inserting app stats")
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ func TestBufferedUpdates(t *testing.T) {
|
||||
|
||||
// setup
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, ps := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -108,7 +107,6 @@ func TestBuildPayload(t *testing.T) {
|
||||
|
||||
// SETUP
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, _ := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -166,7 +164,6 @@ func TestStopBeforeRun(t *testing.T) {
|
||||
|
||||
// SETUP
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, ps := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -187,7 +184,6 @@ func TestRunStopRace(t *testing.T) {
|
||||
|
||||
// SETUP
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitMedium))
|
||||
store, ps := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
|
||||
@@ -37,7 +37,6 @@ func TestMetrics(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it relies on business-logic only implemented in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -226,7 +225,6 @@ func TestPendingUpdatesMetric(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// SETUP
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -320,7 +318,6 @@ func TestInflightDispatchesMetric(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// SETUP
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -400,7 +397,6 @@ func TestCustomMethodMetricCollection(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it relies on business-logic only implemented in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
|
||||
@@ -70,7 +70,6 @@ func TestBasicNotificationRoundtrip(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it relies on business-logic only implemented in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -137,7 +136,6 @@ func TestSMTPDispatch(t *testing.T) {
|
||||
|
||||
// SETUP
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -203,7 +201,6 @@ func TestWebhookDispatch(t *testing.T) {
|
||||
|
||||
// SETUP
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -287,7 +284,6 @@ func TestBackpressure(t *testing.T) {
|
||||
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitShort))
|
||||
|
||||
const method = database.NotificationMethodWebhook
|
||||
@@ -416,7 +412,6 @@ func TestRetries(t *testing.T) {
|
||||
}
|
||||
|
||||
const maxAttempts = 3
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -516,7 +511,6 @@ func TestExpiredLeaseIsRequeued(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it relies on business-logic only implemented in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -536,7 +530,6 @@ func TestExpiredLeaseIsRequeued(t *testing.T) {
|
||||
|
||||
noopInterceptor := newNoopStoreSyncer(store)
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
mgrCtx, cancelManagerCtx := context.WithCancel(dbauthz.AsNotifier(context.Background()))
|
||||
t.Cleanup(cancelManagerCtx)
|
||||
|
||||
@@ -645,7 +638,6 @@ func TestNotifierPaused(t *testing.T) {
|
||||
|
||||
// Setup.
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -1323,7 +1315,6 @@ func TestNotificationTemplates_Golden(t *testing.T) {
|
||||
return &db, &api.Logger, &user
|
||||
}()
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
|
||||
_, pubsub := dbtestutil.NewDB(t)
|
||||
@@ -1406,13 +1397,11 @@ func TestNotificationTemplates_Golden(t *testing.T) {
|
||||
// as appearance changes are enterprise features and we do not want to mix those
|
||||
// can't use the api
|
||||
if tc.appName != "" {
|
||||
// nolint:gocritic // Unit test.
|
||||
err = (*db).UpsertApplicationName(dbauthz.AsSystemRestricted(ctx), "Custom Application")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if tc.logoURL != "" {
|
||||
// nolint:gocritic // Unit test.
|
||||
err = (*db).UpsertLogoURL(dbauthz.AsSystemRestricted(ctx), "https://custom.application/logo.png")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -1510,7 +1499,6 @@ func TestNotificationTemplates_Golden(t *testing.T) {
|
||||
}()
|
||||
|
||||
_, pubsub := dbtestutil.NewDB(t)
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
|
||||
// Spin up the mock webhook server
|
||||
@@ -1650,7 +1638,6 @@ func TestDisabledByDefaultBeforeEnqueue(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it is testing business-logic implemented in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, _ := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -1676,7 +1663,6 @@ func TestDisabledBeforeEnqueue(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it is testing business-logic implemented in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, _ := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -1712,7 +1698,6 @@ func TestDisabledAfterEnqueue(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it is testing business-logic implemented in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -1769,7 +1754,6 @@ func TestCustomNotificationMethod(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it relies on business-logic only implemented in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -1873,7 +1857,6 @@ func TestNotificationsTemplates(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it relies on business-logic only implemented in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
api := coderdtest.New(t, createOpts(t))
|
||||
|
||||
@@ -1910,7 +1893,6 @@ func TestNotificationDuplicates(t *testing.T) {
|
||||
t.Skip("This test requires postgres; it is testing the dedupe hash trigger in the database")
|
||||
}
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -2007,7 +1989,6 @@ func TestNotificationTargetMatrix(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, pubsub := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -2051,7 +2032,6 @@ func TestNotificationOneTimePasswordDeliveryTargets(t *testing.T) {
|
||||
t.Run("Inbox", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, _ := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -2076,7 +2056,6 @@ func TestNotificationOneTimePasswordDeliveryTargets(t *testing.T) {
|
||||
t.Run("SMTP", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, _ := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
@@ -2100,7 +2079,6 @@ func TestNotificationOneTimePasswordDeliveryTargets(t *testing.T) {
|
||||
t.Run("Webhook", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// nolint:gocritic // Unit test.
|
||||
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
|
||||
store, _ := dbtestutil.NewDB(t)
|
||||
logger := testutil.Logger(t)
|
||||
|
||||
@@ -505,7 +505,6 @@ func TestReportFailedWorkspaceBuilds(t *testing.T) {
|
||||
func setup(t *testing.T) (context.Context, slog.Logger, database.Store, pubsub.Pubsub, *notificationstest.FakeEnqueuer, *quartz.Mock) {
|
||||
t.Helper()
|
||||
|
||||
// nolint:gocritic // reportFailedWorkspaceBuilds is called by system.
|
||||
ctx := dbauthz.AsSystemRestricted(context.Background())
|
||||
logger := slogtest.Make(t, &slogtest.Options{})
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
|
||||
@@ -128,7 +128,6 @@ func TestCollectInsights(t *testing.T) {
|
||||
AppStatBatchSize: workspaceapps.DefaultStatsDBReporterBatchSize,
|
||||
})
|
||||
refTime := time.Now().Add(-3 * time.Minute).Truncate(time.Minute)
|
||||
//nolint:gocritic // This is a test.
|
||||
err = reporter.ReportAppStats(dbauthz.AsSystemRestricted(context.Background()), []workspaceapps.StatsReport{
|
||||
{
|
||||
UserID: user.ID,
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
|
||||
"cdr.dev/slog"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/usage"
|
||||
"github.com/coder/coder/v2/coderd/util/slice"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk/drpcsdk"
|
||||
@@ -121,6 +122,7 @@ type server struct {
|
||||
DeploymentValues *codersdk.DeploymentValues
|
||||
NotificationsEnqueuer notifications.Enqueuer
|
||||
PrebuildsOrchestrator *atomic.Pointer[prebuilds.ReconciliationOrchestrator]
|
||||
UsageInserter *atomic.Pointer[usage.Inserter]
|
||||
|
||||
OIDCConfig promoauth.OAuth2Config
|
||||
|
||||
@@ -174,6 +176,7 @@ func NewServer(
|
||||
auditor *atomic.Pointer[audit.Auditor],
|
||||
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore],
|
||||
userQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore],
|
||||
usageInserter *atomic.Pointer[usage.Inserter],
|
||||
deploymentValues *codersdk.DeploymentValues,
|
||||
options Options,
|
||||
enqueuer notifications.Enqueuer,
|
||||
@@ -195,6 +198,9 @@ func NewServer(
|
||||
if userQuietHoursScheduleStore == nil {
|
||||
return nil, xerrors.New("userQuietHoursScheduleStore is nil")
|
||||
}
|
||||
if usageInserter == nil {
|
||||
return nil, xerrors.New("usageCollector is nil")
|
||||
}
|
||||
if deploymentValues == nil {
|
||||
return nil, xerrors.New("deploymentValues is nil")
|
||||
}
|
||||
@@ -244,6 +250,7 @@ func NewServer(
|
||||
heartbeatInterval: options.HeartbeatInterval,
|
||||
heartbeatFn: options.HeartbeatFn,
|
||||
PrebuildsOrchestrator: prebuildsOrchestrator,
|
||||
UsageInserter: usageInserter,
|
||||
}
|
||||
|
||||
if s.heartbeatFn == nil {
|
||||
@@ -2030,6 +2037,20 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
|
||||
sidebarAppID = uuid.NullUUID{}
|
||||
}
|
||||
|
||||
if hasAITask && workspaceBuild.Transition == database.WorkspaceTransitionStart {
|
||||
// Insert usage event for managed agents.
|
||||
usageInserter := s.UsageInserter.Load()
|
||||
if usageInserter != nil {
|
||||
event := usage.DCManagedAgentsV1{
|
||||
Count: 1,
|
||||
}
|
||||
err = (*usageInserter).InsertDiscreteUsageEvent(ctx, db, event)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert %q event: %w", event.EventType(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasExternalAgent := false
|
||||
for _, resource := range jobType.WorkspaceBuild.Resources {
|
||||
if resource.Type == "coder_external_agent" {
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -30,7 +31,9 @@ import (
|
||||
|
||||
"github.com/coder/coder/v2/buildinfo"
|
||||
"github.com/coder/coder/v2/coderd/audit"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
@@ -44,6 +47,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/schedule"
|
||||
"github.com/coder/coder/v2/coderd/schedule/cron"
|
||||
"github.com/coder/coder/v2/coderd/telemetry"
|
||||
"github.com/coder/coder/v2/coderd/usage"
|
||||
"github.com/coder/coder/v2/coderd/wspubsub"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
@@ -67,6 +71,13 @@ func testUserQuietHoursScheduleStore() *atomic.Pointer[schedule.UserQuietHoursSc
|
||||
return ptr
|
||||
}
|
||||
|
||||
func testUsageInserter() *atomic.Pointer[usage.Inserter] {
|
||||
ptr := &atomic.Pointer[usage.Inserter]{}
|
||||
inserter := usage.NewAGPLInserter()
|
||||
ptr.Store(&inserter)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func TestAcquireJob_LongPoll(t *testing.T) {
|
||||
t.Parallel()
|
||||
//nolint:dogsled
|
||||
@@ -681,12 +692,20 @@ func TestUpdateJob(t *testing.T) {
|
||||
t.Run("NotRunning", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, nil)
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
Input: json.RawMessage("{}"),
|
||||
ID: version.JobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||
TemplateVersionID: version.ID,
|
||||
})),
|
||||
OrganizationID: pd.OrganizationID,
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
@@ -700,12 +719,20 @@ func TestUpdateJob(t *testing.T) {
|
||||
t.Run("NotOwner", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, nil)
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
Input: json.RawMessage("{}"),
|
||||
ID: version.JobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||
TemplateVersionID: version.ID,
|
||||
})),
|
||||
OrganizationID: pd.OrganizationID,
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
@@ -730,38 +757,57 @@ func TestUpdateJob(t *testing.T) {
|
||||
require.ErrorContains(t, err, "you don't own this job")
|
||||
})
|
||||
|
||||
setupJob := func(t *testing.T, db database.Store, srvID, orgID uuid.UUID, tags database.StringMap) uuid.UUID {
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
OrganizationID: orgID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Input: json.RawMessage("{}"),
|
||||
Tags: tags,
|
||||
})
|
||||
setupJob := func(t *testing.T, db database.Store, srvID, orgID uuid.UUID, tags database.StringMap) (templateVersionID, jobID uuid.UUID) {
|
||||
templateVersionID = uuid.New()
|
||||
jobID = uuid.New()
|
||||
err := db.InTx(func(db database.Store) error {
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
ID: templateVersionID,
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: orgID,
|
||||
JobID: jobID,
|
||||
})
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: version.JobID,
|
||||
OrganizationID: orgID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||
TemplateVersionID: version.ID,
|
||||
})),
|
||||
Tags: tags,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert provisioner job: %w", err)
|
||||
}
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: srvID,
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
StartedAt: sql.NullTime{
|
||||
Time: dbtime.Now(),
|
||||
Valid: true,
|
||||
},
|
||||
OrganizationID: orgID,
|
||||
ProvisionerTags: must(json.Marshal(job.Tags)),
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("acquire provisioner job: %w", err)
|
||||
}
|
||||
return nil
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: srvID,
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
StartedAt: sql.NullTime{
|
||||
Time: dbtime.Now(),
|
||||
Valid: true,
|
||||
},
|
||||
OrganizationID: orgID,
|
||||
ProvisionerTags: must(json.Marshal(job.Tags)),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return job.ID
|
||||
return templateVersionID, jobID
|
||||
}
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
_, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
_, err := srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: job.String(),
|
||||
})
|
||||
@@ -771,7 +817,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
t.Run("Logs", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, ps, pd := setup(t, false, &overrides{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
_, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
|
||||
published := make(chan struct{})
|
||||
|
||||
@@ -796,23 +842,14 @@ func TestUpdateJob(t *testing.T) {
|
||||
t.Run("Readme", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
versionID := uuid.New()
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
err := db.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{
|
||||
ID: versionID,
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
JobID: job,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
templateVersionID, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
_, err := srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: job.String(),
|
||||
Readme: []byte("# hello world"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
version, err := db.GetTemplateVersionByID(ctx, versionID)
|
||||
version, err := db.GetTemplateVersionByID(ctx, templateVersionID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "# hello world", version.Readme)
|
||||
})
|
||||
@@ -825,16 +862,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
versionID := uuid.New()
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
err := db.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{
|
||||
ID: versionID,
|
||||
CreatedBy: user.ID,
|
||||
JobID: job,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
templateVersionID, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
firstTemplateVariable := &sdkproto.TemplateVariable{
|
||||
Name: "first",
|
||||
Type: "string",
|
||||
@@ -863,7 +891,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, response.VariableValues, 2)
|
||||
|
||||
templateVariables, err := db.GetTemplateVersionVariables(ctx, versionID)
|
||||
templateVariables, err := db.GetTemplateVersionVariables(ctx, templateVersionID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, templateVariables, 2)
|
||||
require.Equal(t, templateVariables[0].Value, firstTemplateVariable.DefaultValue)
|
||||
@@ -875,16 +903,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
versionID := uuid.New()
|
||||
err := db.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{
|
||||
CreatedBy: user.ID,
|
||||
ID: versionID,
|
||||
JobID: job,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
templateVersionID, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
firstTemplateVariable := &sdkproto.TemplateVariable{
|
||||
Name: "first",
|
||||
Type: "string",
|
||||
@@ -909,7 +928,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
|
||||
// Even though there is an error returned, variables are stored in the database
|
||||
// to show the schema in the site UI.
|
||||
templateVariables, err := db.GetTemplateVersionVariables(ctx, versionID)
|
||||
templateVariables, err := db.GetTemplateVersionVariables(ctx, templateVersionID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, templateVariables, 2)
|
||||
require.Equal(t, templateVariables[0].Value, firstTemplateVariable.DefaultValue)
|
||||
@@ -923,18 +942,9 @@ func TestUpdateJob(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
versionID := uuid.New()
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
err := db.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{
|
||||
ID: versionID,
|
||||
CreatedBy: user.ID,
|
||||
JobID: job,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
srv, db, _, pd := setup(t, false, nil)
|
||||
templateVersionID, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
_, err := srv.UpdateJob(ctx, &proto.UpdateJobRequest{
|
||||
JobId: job.String(),
|
||||
WorkspaceTags: map[string]string{
|
||||
"bird": "tweety",
|
||||
@@ -943,7 +953,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
workspaceTags, err := db.GetTemplateVersionWorkspaceTags(ctx, versionID)
|
||||
workspaceTags, err := db.GetTemplateVersionWorkspaceTags(ctx, templateVersionID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, workspaceTags, 2)
|
||||
require.Equal(t, workspaceTags[0].Key, "bird")
|
||||
@@ -955,7 +965,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
t.Run("LogSizeLimit", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
_, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
|
||||
// Create a log message that exceeds the 1MB limit
|
||||
largeOutput := strings.Repeat("a", 1048577) // 1MB + 1 byte
|
||||
@@ -979,7 +989,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
t.Run("IncrementalLogSizeOverflow", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
_, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
|
||||
// Send logs that together exceed the limit
|
||||
mediumOutput := strings.Repeat("b", 524289) // Half a MB + 1 byte
|
||||
@@ -1020,7 +1030,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
t.Run("LogSizeTracking", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
_, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
|
||||
logOutput := "test log message"
|
||||
expectedSize := int32(len(logOutput)) // #nosec G115 - Log length is 16.
|
||||
@@ -1045,7 +1055,7 @@ func TestUpdateJob(t *testing.T) {
|
||||
t.Run("LogOverflowStopsProcessing", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
_, job := setupJob(t, db, pd.ID, pd.OrganizationID, pd.Tags)
|
||||
|
||||
// First: trigger overflow
|
||||
largeOutput := strings.Repeat("a", 1048577) // 1MB + 1 byte
|
||||
@@ -1108,12 +1118,20 @@ func TestFailJob(t *testing.T) {
|
||||
t.Run("NotOwner", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, nil)
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
Input: json.RawMessage("{}"),
|
||||
ID: version.JobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
||||
TemplateVersionID: version.ID,
|
||||
})),
|
||||
OrganizationID: pd.OrganizationID,
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
@@ -1139,13 +1157,21 @@ func TestFailJob(t *testing.T) {
|
||||
})
|
||||
t.Run("AlreadyCompleted", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
srv, db, _, pd := setup(t, false, nil)
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Input: json.RawMessage("{}"),
|
||||
ID: version.JobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
||||
TemplateVersionID: version.ID,
|
||||
})),
|
||||
OrganizationID: pd.OrganizationID,
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
@@ -1310,14 +1336,22 @@ func TestCompleteJob(t *testing.T) {
|
||||
t.Run("NotOwner", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, nil)
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
ID: version.JobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
Input: json.RawMessage("{}"),
|
||||
Tags: pd.Tags,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||
TemplateVersionID: version.ID,
|
||||
})),
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
@@ -1361,10 +1395,12 @@ func TestCompleteJob(t *testing.T) {
|
||||
OrganizationID: pd.OrganizationID,
|
||||
ID: jobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Input: []byte(`{"template_version_id": "` + versionID.String() + `"}`),
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
Tags: pd.Tags,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
||||
TemplateVersionID: versionID,
|
||||
})),
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
@@ -1410,14 +1446,22 @@ func TestCompleteJob(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
org := dbgen.Organization(t, db, database.Organization{})
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: org.ID,
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
OrganizationID: org.ID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Input: json.RawMessage("{}"),
|
||||
Tags: pd.Tags,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||
TemplateVersionID: version.ID,
|
||||
})),
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
@@ -1628,25 +1672,49 @@ func TestCompleteJob(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
jobID := uuid.New()
|
||||
versionID := uuid.New()
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
err := db.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{
|
||||
tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
ID: versionID,
|
||||
JobID: jobID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
JobID: jobID,
|
||||
})
|
||||
template := dbgen.Template(t, db, database.Template{
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
ActiveVersionID: tv.ID,
|
||||
})
|
||||
err := db.UpdateTemplateVersionByID(ctx, database.UpdateTemplateVersionByIDParams{
|
||||
ID: tv.ID,
|
||||
TemplateID: uuid.NullUUID{
|
||||
UUID: template.ID,
|
||||
Valid: true,
|
||||
},
|
||||
UpdatedAt: dbtime.Now(),
|
||||
Name: tv.Name,
|
||||
Message: tv.Message,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
workspace := dbgen.Workspace(t, db, database.WorkspaceTable{
|
||||
OwnerID: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
TemplateID: template.ID,
|
||||
})
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: jobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Input: []byte(`{"template_version_id": "` + versionID.String() + `"}`),
|
||||
Input: json.RawMessage("{}"),
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_ = dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||
WorkspaceID: workspace.ID,
|
||||
TemplateVersionID: tv.ID,
|
||||
InitiatorID: user.ID,
|
||||
JobID: jobID,
|
||||
})
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
OrganizationID: pd.OrganizationID,
|
||||
WorkerID: uuid.NullUUID{
|
||||
@@ -1697,11 +1765,13 @@ func TestCompleteJob(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: jobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Input: []byte(`{"template_version_id": "` + versionID.String() + `"}`),
|
||||
ID: jobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
||||
TemplateVersionID: versionID,
|
||||
})),
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
@@ -1766,10 +1836,12 @@ func TestCompleteJob(t *testing.T) {
|
||||
OrganizationID: pd.OrganizationID,
|
||||
ID: jobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Input: []byte(`{"template_version_id": "` + versionID.String() + `"}`),
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
Tags: pd.Tags,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
||||
TemplateVersionID: versionID,
|
||||
})),
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
@@ -2091,12 +2163,20 @@ func TestCompleteJob(t *testing.T) {
|
||||
t.Run("TemplateDryRun", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Input: json.RawMessage("{}"),
|
||||
ID: version.JobID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||
TemplateVersionID: version.ID,
|
||||
})),
|
||||
OrganizationID: pd.OrganizationID,
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
@@ -2191,8 +2271,10 @@ func TestCompleteJob(t *testing.T) {
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}},
|
||||
provisionerJobParams: database.InsertProvisionerJobParams{
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
Input: json.RawMessage("{}"),
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{
|
||||
TemplateVersionID: templateVersionID,
|
||||
})),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -2349,22 +2431,26 @@ func TestCompleteJob(t *testing.T) {
|
||||
OrganizationID: pd.OrganizationID,
|
||||
})
|
||||
tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
ID: templateVersionID,
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
|
||||
JobID: job.ID,
|
||||
})
|
||||
workspace := dbgen.Workspace(t, db, database.WorkspaceTable{
|
||||
TemplateID: tpl.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
OwnerID: user.ID,
|
||||
})
|
||||
_ = dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||
ID: workspaceBuildID,
|
||||
JobID: job.ID,
|
||||
WorkspaceID: workspace.ID,
|
||||
TemplateVersionID: tv.ID,
|
||||
})
|
||||
|
||||
if jobParams.Type == database.ProvisionerJobTypeWorkspaceBuild {
|
||||
workspace := dbgen.Workspace(t, db, database.WorkspaceTable{
|
||||
TemplateID: tpl.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
OwnerID: user.ID,
|
||||
})
|
||||
_ = dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||
ID: workspaceBuildID,
|
||||
JobID: job.ID,
|
||||
WorkspaceID: workspace.ID,
|
||||
TemplateVersionID: tv.ID,
|
||||
})
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
@@ -2672,7 +2758,10 @@ func TestCompleteJob(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
fakeUsageInserter, usageInserterPtr := newFakeUsageInserter()
|
||||
srv, db, _, pd := setup(t, false, &overrides{
|
||||
usageInserter: usageInserterPtr,
|
||||
})
|
||||
|
||||
importJobID := uuid.New()
|
||||
tvID := uuid.New()
|
||||
@@ -2741,6 +2830,10 @@ func TestCompleteJob(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.True(t, version.HasAITask.Valid) // We ALWAYS expect a value to be set, therefore not nil, i.e. valid = true.
|
||||
require.Equal(t, tc.expected, version.HasAITask.Bool)
|
||||
|
||||
// We never expect a usage event to be collected for
|
||||
// template imports.
|
||||
require.Empty(t, fakeUsageInserter.collectedEvents)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -2750,22 +2843,27 @@ func TestCompleteJob(t *testing.T) {
|
||||
// will be set as well in that case.
|
||||
t.Run("WorkspaceBuild", func(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
input *proto.CompletedJob_WorkspaceBuild
|
||||
expected bool
|
||||
name string
|
||||
transition database.WorkspaceTransition
|
||||
input *proto.CompletedJob_WorkspaceBuild
|
||||
expectHasAiTask bool
|
||||
expectUsageEvent bool
|
||||
}
|
||||
|
||||
sidebarAppID := uuid.NewString()
|
||||
for _, tc := range []testcase{
|
||||
{
|
||||
name: "has_ai_task is false by default",
|
||||
input: &proto.CompletedJob_WorkspaceBuild{
|
||||
name: "has_ai_task is false by default",
|
||||
transition: database.WorkspaceTransitionStart,
|
||||
input: &proto.CompletedJob_WorkspaceBuild{
|
||||
// No AiTasks defined.
|
||||
},
|
||||
expected: false,
|
||||
expectHasAiTask: false,
|
||||
expectUsageEvent: false,
|
||||
},
|
||||
{
|
||||
name: "has_ai_task is set to true",
|
||||
name: "has_ai_task is set to true",
|
||||
transition: database.WorkspaceTransitionStart,
|
||||
input: &proto.CompletedJob_WorkspaceBuild{
|
||||
AiTasks: []*sdkproto.AITask{
|
||||
{
|
||||
@@ -2792,11 +2890,13 @@ func TestCompleteJob(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
expectHasAiTask: true,
|
||||
expectUsageEvent: true,
|
||||
},
|
||||
// Checks regression for https://github.com/coder/coder/issues/18776
|
||||
{
|
||||
name: "non-existing app",
|
||||
name: "non-existing app",
|
||||
transition: database.WorkspaceTransitionStart,
|
||||
input: &proto.CompletedJob_WorkspaceBuild{
|
||||
AiTasks: []*sdkproto.AITask{
|
||||
{
|
||||
@@ -2808,13 +2908,49 @@ func TestCompleteJob(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
expectHasAiTask: false,
|
||||
expectUsageEvent: false,
|
||||
},
|
||||
{
|
||||
name: "has_ai_task is set to true, but transition is not start",
|
||||
transition: database.WorkspaceTransitionStop,
|
||||
input: &proto.CompletedJob_WorkspaceBuild{
|
||||
AiTasks: []*sdkproto.AITask{
|
||||
{
|
||||
Id: uuid.NewString(),
|
||||
SidebarApp: &sdkproto.AITaskSidebarApp{
|
||||
Id: sidebarAppID,
|
||||
},
|
||||
},
|
||||
},
|
||||
Resources: []*sdkproto.Resource{
|
||||
{
|
||||
Agents: []*sdkproto.Agent{
|
||||
{
|
||||
Id: uuid.NewString(),
|
||||
Name: "a",
|
||||
Apps: []*sdkproto.App{
|
||||
{
|
||||
Id: sidebarAppID,
|
||||
Slug: "test-app",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectHasAiTask: true,
|
||||
expectUsageEvent: false,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
fakeUsageInserter, usageInserterPtr := newFakeUsageInserter()
|
||||
srv, db, _, pd := setup(t, false, &overrides{
|
||||
usageInserter: usageInserterPtr,
|
||||
})
|
||||
|
||||
importJobID := uuid.New()
|
||||
tvID := uuid.New()
|
||||
@@ -2868,7 +3004,7 @@ func TestCompleteJob(t *testing.T) {
|
||||
WorkspaceID: workspaceTable.ID,
|
||||
TemplateVersionID: version.ID,
|
||||
InitiatorID: user.ID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
Transition: tc.transition,
|
||||
})
|
||||
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
@@ -2899,11 +3035,22 @@ func TestCompleteJob(t *testing.T) {
|
||||
build, err = db.GetWorkspaceBuildByID(ctx, build.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, build.HasAITask.Valid) // We ALWAYS expect a value to be set, therefore not nil, i.e. valid = true.
|
||||
require.Equal(t, tc.expected, build.HasAITask.Bool)
|
||||
require.Equal(t, tc.expectHasAiTask, build.HasAITask.Bool)
|
||||
|
||||
if tc.expected {
|
||||
if tc.expectHasAiTask {
|
||||
require.Equal(t, sidebarAppID, build.AITaskSidebarAppID.UUID.String())
|
||||
}
|
||||
|
||||
if tc.expectUsageEvent {
|
||||
// Check that a usage event was collected.
|
||||
require.Len(t, fakeUsageInserter.collectedEvents, 1)
|
||||
require.Equal(t, usage.DCManagedAgentsV1{
|
||||
Count: 1,
|
||||
}, fakeUsageInserter.collectedEvents[0])
|
||||
} else {
|
||||
// Check that no usage event was collected.
|
||||
require.Empty(t, fakeUsageInserter.collectedEvents)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -3835,6 +3982,7 @@ type overrides struct {
|
||||
externalAuthConfigs []*externalauth.Config
|
||||
templateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
|
||||
userQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
|
||||
usageInserter *atomic.Pointer[usage.Inserter]
|
||||
clock *quartz.Mock
|
||||
acquireJobLongPollDuration time.Duration
|
||||
heartbeatFn func(ctx context.Context) error
|
||||
@@ -3855,13 +4003,14 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||
var externalAuthConfigs []*externalauth.Config
|
||||
tss := testTemplateScheduleStore()
|
||||
uqhss := testUserQuietHoursScheduleStore()
|
||||
usageInserter := testUsageInserter()
|
||||
clock := quartz.NewReal()
|
||||
pollDur := time.Duration(0)
|
||||
if ov == nil {
|
||||
ov = &overrides{}
|
||||
}
|
||||
if ov.ctx == nil {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(dbauthz.AsProvisionerd(context.Background()))
|
||||
t.Cleanup(cancel)
|
||||
ov.ctx = ctx
|
||||
}
|
||||
@@ -3892,6 +4041,15 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||
require.True(t, swapped)
|
||||
}
|
||||
}
|
||||
if ov.usageInserter != nil {
|
||||
tUsageInserter := usageInserter.Load()
|
||||
// keep the initial test value if the override hasn't set the atomic pointer.
|
||||
usageInserter = ov.usageInserter
|
||||
if usageInserter.Load() == nil {
|
||||
swapped := usageInserter.CompareAndSwap(nil, tUsageInserter)
|
||||
require.True(t, swapped)
|
||||
}
|
||||
}
|
||||
if ov.clock != nil {
|
||||
clock = ov.clock
|
||||
}
|
||||
@@ -3929,6 +4087,10 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||
var op atomic.Pointer[agplprebuilds.ReconciliationOrchestrator]
|
||||
op.Store(&prebuildsOrchestrator)
|
||||
|
||||
// Use an authz wrapped database for the server to ensure permission checks
|
||||
// work.
|
||||
authorizer := rbac.NewStrictCachingAuthorizer(prometheus.NewRegistry())
|
||||
serverDB := dbauthz.New(db, authorizer, logger, coderdtest.AccessControlStorePointer())
|
||||
srv, err := provisionerdserver.NewServer(
|
||||
ov.ctx,
|
||||
proto.CurrentVersion.String(),
|
||||
@@ -3938,7 +4100,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||
slogtest.Make(t, &slogtest.Options{IgnoreErrors: ignoreLogErrors}),
|
||||
[]database.ProvisionerType{database.ProvisionerTypeEcho},
|
||||
provisionerdserver.Tags(daemon.Tags),
|
||||
db,
|
||||
serverDB,
|
||||
ps,
|
||||
provisionerdserver.NewAcquirer(ov.ctx, logger.Named("acquirer"), db, ps),
|
||||
telemetry.NewNoop(),
|
||||
@@ -3947,6 +4109,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||
auditPtr,
|
||||
tss,
|
||||
uqhss,
|
||||
usageInserter,
|
||||
deploymentValues,
|
||||
provisionerdserver.Options{
|
||||
ExternalAuthConfigs: externalAuthConfigs,
|
||||
@@ -4061,3 +4224,22 @@ func (s *fakeStream) cancel() {
|
||||
s.canceled = true
|
||||
s.c.Broadcast()
|
||||
}
|
||||
|
||||
type fakeUsageInserter struct {
|
||||
collectedEvents []usage.Event
|
||||
}
|
||||
|
||||
var _ usage.Inserter = &fakeUsageInserter{}
|
||||
|
||||
func newFakeUsageInserter() (*fakeUsageInserter, *atomic.Pointer[usage.Inserter]) {
|
||||
ptr := &atomic.Pointer[usage.Inserter]{}
|
||||
fake := &fakeUsageInserter{}
|
||||
var inserter usage.Inserter = fake
|
||||
ptr.Store(&inserter)
|
||||
return fake, ptr
|
||||
}
|
||||
|
||||
func (f *fakeUsageInserter) InsertDiscreteUsageEvent(_ context.Context, _ database.Store, event usage.DiscreteEvent) error {
|
||||
f.collectedEvents = append(f.collectedEvents, event)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ const (
|
||||
SubjectTypeNotifier SubjectType = "notifier"
|
||||
SubjectTypeSubAgentAPI SubjectType = "sub_agent_api"
|
||||
SubjectTypeFileReader SubjectType = "file_reader"
|
||||
SubjectTypeUsageTracker SubjectType = "usage_tracker"
|
||||
SubjectTypeUsagePublisher SubjectType = "usage_publisher"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
type Inserter interface {
|
||||
// InsertDiscreteUsageEvent writes a discrete usage event to the database
|
||||
// within the given transaction.
|
||||
// The caller context must be authorized to create usage events in the
|
||||
// database.
|
||||
InsertDiscreteUsageEvent(ctx context.Context, tx database.Store, event DiscreteEvent) error
|
||||
}
|
||||
|
||||
|
||||
@@ -335,7 +335,6 @@ func TestUserOAuth2Github(t *testing.T) {
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
// nolint:gocritic // Unit test
|
||||
count, err := db.GetUserCount(dbauthz.AsSystemRestricted(ctx), false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), count)
|
||||
@@ -897,7 +896,6 @@ func TestUserOAuth2Github(t *testing.T) {
|
||||
require.Empty(t, links)
|
||||
|
||||
// Make sure a user_link cannot be created with a deleted user.
|
||||
// nolint:gocritic // Unit test
|
||||
_, err = db.InsertUserLink(dbauthz.AsSystemRestricted(ctx), database.InsertUserLinkParams{
|
||||
UserID: deleted.ID,
|
||||
LoginType: "github",
|
||||
|
||||
@@ -1544,7 +1544,6 @@ func TestUsersFilter(t *testing.T) {
|
||||
}
|
||||
userClient, userData := coderdtest.CreateAnotherUser(t, client, first.OrganizationID, roles...)
|
||||
// Set the last seen for each user to a unique day
|
||||
// nolint:gocritic // Unit test
|
||||
_, err := api.Database.UpdateUserLastSeenAt(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLastSeenAtParams{
|
||||
ID: userData.ID,
|
||||
LastSeenAt: lastSeenNow.Add(-1 * time.Hour * 24 * time.Duration(i)),
|
||||
@@ -1572,7 +1571,6 @@ func TestUsersFilter(t *testing.T) {
|
||||
|
||||
// Add users with different creation dates for testing date filters
|
||||
for i := 0; i < 3; i++ {
|
||||
// nolint:gocritic // Using system context is necessary to seed data in tests
|
||||
user1, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{
|
||||
ID: uuid.New(),
|
||||
Email: fmt.Sprintf("before%d@coder.com", i),
|
||||
@@ -1594,7 +1592,6 @@ func TestUsersFilter(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
users = append(users, sdkUser1)
|
||||
|
||||
// nolint:gocritic //Using system context is necessary to seed data in tests
|
||||
user2, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{
|
||||
ID: uuid.New(),
|
||||
Email: fmt.Sprintf("during%d@coder.com", i),
|
||||
@@ -1615,7 +1612,6 @@ func TestUsersFilter(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
users = append(users, sdkUser2)
|
||||
|
||||
// nolint:gocritic // Using system context is necessary to seed data in tests
|
||||
user3, err := api.Database.InsertUser(dbauthz.AsSystemRestricted(ctx), database.InsertUserParams{
|
||||
ID: uuid.New(),
|
||||
Email: fmt.Sprintf("after%d@coder.com", i),
|
||||
@@ -1912,7 +1908,6 @@ func TestGetUsers(t *testing.T) {
|
||||
Email: "test2@coder.com",
|
||||
Username: "test2",
|
||||
})
|
||||
// nolint:gocritic // Unit test
|
||||
err := db.UpdateUserGithubComUserID(dbauthz.AsSystemRestricted(ctx), database.UpdateUserGithubComUserIDParams{
|
||||
ID: first.UserID,
|
||||
GithubComUserID: sql.NullInt64{
|
||||
|
||||
@@ -562,7 +562,6 @@ func TestWorkspaceAgentConnectRPC(t *testing.T) {
|
||||
seed := database.WorkspaceTable{OrganizationID: user.OrganizationID, OwnerID: user.UserID}
|
||||
wsb := dbfake.WorkspaceBuild(t, db, seed).WithAgent().Do()
|
||||
// When: the workspace is marked as soft-deleted
|
||||
// nolint:gocritic // this is a test
|
||||
err := db.UpdateWorkspaceDeletedByID(
|
||||
dbauthz.AsProvisionerd(ctx),
|
||||
database.UpdateWorkspaceDeletedByIDParams{ID: wsb.Workspace.ID, Deleted: true},
|
||||
@@ -633,7 +632,6 @@ func TestWorkspaceAgentClientCoordinate_BadVersion(t *testing.T) {
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
agentToken, err := uuid.Parse(r.AgentToken)
|
||||
require.NoError(t, err)
|
||||
//nolint: gocritic // testing
|
||||
ao, err := db.GetWorkspaceAgentAndLatestBuildByAuthToken(dbauthz.AsSystemRestricted(ctx), agentToken)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -724,7 +722,7 @@ func TestWorkspaceAgentClientCoordinate_ResumeToken(t *testing.T) {
|
||||
agentTokenUUID, err := uuid.Parse(r.AgentToken)
|
||||
require.NoError(t, err)
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
agentAndBuild, err := api.Database.GetWorkspaceAgentAndLatestBuildByAuthToken(dbauthz.AsSystemRestricted(ctx), agentTokenUUID) //nolint
|
||||
agentAndBuild, err := api.Database.GetWorkspaceAgentAndLatestBuildByAuthToken(dbauthz.AsSystemRestricted(ctx), agentTokenUUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Connect with no resume token, and ensure that the peer ID is set to a
|
||||
@@ -796,7 +794,7 @@ func TestWorkspaceAgentClientCoordinate_ResumeToken(t *testing.T) {
|
||||
agentTokenUUID, err := uuid.Parse(r.AgentToken)
|
||||
require.NoError(t, err)
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
agentAndBuild, err := api.Database.GetWorkspaceAgentAndLatestBuildByAuthToken(dbauthz.AsSystemRestricted(ctx), agentTokenUUID) //nolint
|
||||
agentAndBuild, err := api.Database.GetWorkspaceAgentAndLatestBuildByAuthToken(dbauthz.AsSystemRestricted(ctx), agentTokenUUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Connect with no resume token, and ensure that the peer ID is set to a
|
||||
|
||||
@@ -55,7 +55,6 @@ func TestWorkspaceBuild(t *testing.T) {
|
||||
Auditor: auditor,
|
||||
})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
//nolint:gocritic // testing
|
||||
up, err := db.UpdateUserProfile(dbauthz.AsSystemRestricted(ctx), database.UpdateUserProfileParams{
|
||||
ID: user.UserID,
|
||||
Email: coderdtest.FirstUserParams.Email,
|
||||
@@ -518,7 +517,6 @@ func TestWorkspaceBuildsProvisionerState(t *testing.T) {
|
||||
OrganizationID: first.OrganizationID,
|
||||
}).Do()
|
||||
|
||||
// nolint:gocritic // For testing
|
||||
daemons, err := store.GetProvisionerDaemons(dbauthz.AsSystemReadProvisionerDaemons(ctx))
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, daemons, "Provisioner daemons should be empty for this test")
|
||||
|
||||
@@ -1427,7 +1427,6 @@ func TestWorkspaceFilterAllStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// For this test, we do not care about permissions.
|
||||
// nolint:gocritic // unit testing
|
||||
ctx := dbauthz.AsSystemRestricted(context.Background())
|
||||
db, pubsub := dbtestutil.NewDB(t)
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
@@ -2215,15 +2214,12 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
||||
after := coderdtest.CreateWorkspace(t, client, template.ID)
|
||||
_ = coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, after.LatestBuild.ID)
|
||||
|
||||
//nolint:gocritic // Unit testing context
|
||||
err := api.Database.UpdateWorkspaceLastUsedAt(dbauthz.AsSystemRestricted(ctx), database.UpdateWorkspaceLastUsedAtParams{
|
||||
ID: before.ID,
|
||||
LastUsedAt: now.UTC().Add(time.Hour * -1),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Unit testing context
|
||||
//nolint:gocritic // Unit testing context
|
||||
err = api.Database.UpdateWorkspaceLastUsedAt(dbauthz.AsSystemRestricted(ctx), database.UpdateWorkspaceLastUsedAtParams{
|
||||
ID: after.ID,
|
||||
LastUsedAt: now.UTC().Add(time.Hour * 1),
|
||||
@@ -2916,14 +2912,14 @@ func TestWorkspaceUpdateTTL(t *testing.T) {
|
||||
|
||||
// This is a hack, but the max_deadline isn't precisely configurable
|
||||
// without a lot of unnecessary hassle.
|
||||
dbBuild, err := db.GetWorkspaceBuildByID(dbauthz.AsSystemRestricted(ctx), build.ID) //nolint:gocritic // test
|
||||
dbBuild, err := db.GetWorkspaceBuildByID(dbauthz.AsSystemRestricted(ctx), build.ID)
|
||||
require.NoError(t, err)
|
||||
dbJob, err := db.GetProvisionerJobByID(dbauthz.AsSystemRestricted(ctx), dbBuild.JobID) //nolint:gocritic // test
|
||||
dbJob, err := db.GetProvisionerJobByID(dbauthz.AsSystemRestricted(ctx), dbBuild.JobID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, dbJob.CompletedAt.Valid)
|
||||
initialDeadline := dbJob.CompletedAt.Time.Add(deadline)
|
||||
expectedMaxDeadline := dbJob.CompletedAt.Time.Add(maxDeadline)
|
||||
err = db.UpdateWorkspaceBuildDeadlineByID(dbauthz.AsSystemRestricted(ctx), database.UpdateWorkspaceBuildDeadlineByIDParams{ //nolint:gocritic // test
|
||||
err = db.UpdateWorkspaceBuildDeadlineByID(dbauthz.AsSystemRestricted(ctx), database.UpdateWorkspaceBuildDeadlineByIDParams{
|
||||
ID: build.ID,
|
||||
Deadline: initialDeadline,
|
||||
MaxDeadline: expectedMaxDeadline,
|
||||
@@ -4507,14 +4503,12 @@ func TestOIDCRemoved(t *testing.T) {
|
||||
user, userData := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, rbac.ScopedRoleOrgAdmin(first.OrganizationID))
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
//nolint:gocritic // unit test
|
||||
_, err := db.UpdateUserLoginType(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLoginTypeParams{
|
||||
NewLoginType: database.LoginTypeOIDC,
|
||||
UserID: userData.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
//nolint:gocritic // unit test
|
||||
_, err = db.InsertUserLink(dbauthz.AsSystemRestricted(ctx), database.InsertUserLinkParams{
|
||||
UserID: userData.ID,
|
||||
LoginType: database.LoginTypeOIDC,
|
||||
@@ -4603,7 +4597,6 @@ func TestWorkspaceFilterHasAITask(t *testing.T) {
|
||||
})
|
||||
|
||||
if aiTaskPrompt != nil {
|
||||
//nolint:gocritic // unit test
|
||||
err := db.InsertWorkspaceBuildParameters(dbauthz.AsSystemRestricted(ctx), database.InsertWorkspaceBuildParametersParams{
|
||||
WorkspaceBuildID: build.ID,
|
||||
Name: []string{provider.TaskPromptParameterName},
|
||||
@@ -4806,7 +4799,6 @@ func TestMultipleAITasksDisallowed(t *testing.T) {
|
||||
ws := coderdtest.CreateWorkspace(t, client, template.ID)
|
||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws.LatestBuild.ID)
|
||||
|
||||
//nolint: gocritic // testing
|
||||
ctx := dbauthz.AsSystemRestricted(t.Context())
|
||||
pj, err := db.GetProvisionerJobByID(ctx, ws.LatestBuild.Job.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user