Skip to content

feat(coderd/database): add UpsertProvisionerDaemons query #11178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/coder/coder/v2/coderd/httpapi/httpapiconstraints"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/provisionersdk"
)

var _ database.Store = (*querier)(nil)
Expand Down Expand Up @@ -2155,14 +2156,6 @@ func (q *querier) InsertOrganizationMember(ctx context.Context, arg database.Ins
return insert(q.log, q.auth, obj, q.db.InsertOrganizationMember)(ctx, arg)
}

// TODO: We need to create a ProvisionerDaemon resource type
func (q *querier) InsertProvisionerDaemon(ctx context.Context, arg database.InsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
// if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
// return database.ProvisionerDaemon{}, err
// }
return q.db.InsertProvisionerDaemon(ctx, arg)
}

// TODO: We need to create a ProvisionerJob resource type
func (q *querier) InsertProvisionerJob(ctx context.Context, arg database.InsertProvisionerJobParams) (database.ProvisionerJob, error) {
// if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil {
Expand Down Expand Up @@ -3063,6 +3056,17 @@ func (q *querier) UpsertOAuthSigningKey(ctx context.Context, value string) error
return q.db.UpsertOAuthSigningKey(ctx, value)
}

func (q *querier) UpsertProvisionerDaemon(ctx context.Context, arg database.UpsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
res := rbac.ResourceProvisionerDaemon.All()
if arg.Tags[provisionersdk.TagScope] == provisionersdk.ScopeUser {
res.Owner = arg.Tags[provisionersdk.TagOwner]
}
if err := q.authorizeContext(ctx, rbac.ActionCreate, res); err != nil {
return database.ProvisionerDaemon{}, err
}
return q.db.UpsertProvisionerDaemon(ctx, arg)
}

func (q *querier) UpsertServiceBanner(ctx context.Context, value string) error {
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceDeploymentValues); err != nil {
return err
Expand Down
25 changes: 18 additions & 7 deletions coderd/database/dbauthz/dbauthz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/provisionersdk"
"github.com/coder/coder/v2/testutil"
)

Expand Down Expand Up @@ -1370,8 +1371,10 @@ func (s *MethodTestSuite) TestWorkspace() {

func (s *MethodTestSuite) TestExtraMethods() {
s.Run("GetProvisionerDaemons", s.Subtest(func(db database.Store, check *expects) {
d, err := db.InsertProvisionerDaemon(context.Background(), database.InsertProvisionerDaemonParams{
ID: uuid.New(),
d, err := db.UpsertProvisionerDaemon(context.Background(), database.UpsertProvisionerDaemonParams{
Tags: database.StringMap(map[string]string{
provisionersdk.TagScope: provisionersdk.ScopeOrganization,
}),
})
s.NoError(err, "insert provisioner daemon")
check.Args().Asserts(d, rbac.ActionRead)
Expand Down Expand Up @@ -1650,11 +1653,19 @@ func (s *MethodTestSuite) TestSystemFunctions() {
JobID: j.ID,
}).Asserts( /*rbac.ResourceSystem, rbac.ActionCreate*/ )
}))
s.Run("InsertProvisionerDaemon", s.Subtest(func(db database.Store, check *expects) {
// TODO: we need to create a ProvisionerDaemon resource
check.Args(database.InsertProvisionerDaemonParams{
ID: uuid.New(),
}).Asserts( /*rbac.ResourceSystem, rbac.ActionCreate*/ )
s.Run("UpsertProvisionerDaemon", s.Subtest(func(db database.Store, check *expects) {
pd := rbac.ResourceProvisionerDaemon.All()
check.Args(database.UpsertProvisionerDaemonParams{
Tags: database.StringMap(map[string]string{
provisionersdk.TagScope: provisionersdk.ScopeOrganization,
}),
}).Asserts(pd, rbac.ActionCreate)
check.Args(database.UpsertProvisionerDaemonParams{
Tags: database.StringMap(map[string]string{
provisionersdk.TagScope: provisionersdk.ScopeUser,
provisionersdk.TagOwner: "11111111-1111-1111-1111-111111111111",
}),
}).Asserts(pd.WithOwner("11111111-1111-1111-1111-111111111111"), rbac.ActionCreate)
}))
s.Run("InsertTemplateVersionParameter", s.Subtest(func(db database.Store, check *expects) {
v := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{})
Expand Down
57 changes: 38 additions & 19 deletions coderd/database/dbmem/dbmem.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/coder/coder/v2/coderd/rbac/regosql"
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisionersdk"
)

var validProxyByHostnameRegex = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
Expand Down Expand Up @@ -4936,25 +4937,6 @@ func (q *FakeQuerier) InsertOrganizationMember(_ context.Context, arg database.I
return organizationMember, nil
}

func (q *FakeQuerier) InsertProvisionerDaemon(_ context.Context, arg database.InsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
if err := validateDatabaseType(arg); err != nil {
return database.ProvisionerDaemon{}, err
}

q.mutex.Lock()
defer q.mutex.Unlock()

daemon := database.ProvisionerDaemon{
ID: arg.ID,
Name: arg.Name,
Provisioners: arg.Provisioners,
Tags: arg.Tags,
LastSeenAt: arg.LastSeenAt,
}
q.provisionerDaemons = append(q.provisionerDaemons, daemon)
return daemon, nil
}

func (q *FakeQuerier) InsertProvisionerJob(_ context.Context, arg database.InsertProvisionerJobParams) (database.ProvisionerJob, error) {
if err := validateDatabaseType(arg); err != nil {
return database.ProvisionerJob{}, err
Expand Down Expand Up @@ -6961,6 +6943,43 @@ func (q *FakeQuerier) UpsertOAuthSigningKey(_ context.Context, value string) err
return nil
}

func (q *FakeQuerier) UpsertProvisionerDaemon(_ context.Context, arg database.UpsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
err := validateDatabaseType(arg)
if err != nil {
return database.ProvisionerDaemon{}, err
}

q.mutex.Lock()
defer q.mutex.Unlock()
for _, d := range q.provisionerDaemons {
if d.Name == arg.Name {
if d.Tags[provisionersdk.TagScope] == provisionersdk.ScopeOrganization && arg.Tags[provisionersdk.TagOwner] != "" {
continue
}
if d.Tags[provisionersdk.TagScope] == provisionersdk.ScopeUser && arg.Tags[provisionersdk.TagOwner] != d.Tags[provisionersdk.TagOwner] {
continue
}
d.Provisioners = arg.Provisioners
d.Tags = arg.Tags
d.Version = arg.Version
d.LastSeenAt = arg.LastSeenAt
return d, nil
}
}
d := database.ProvisionerDaemon{
ID: uuid.New(),
CreatedAt: arg.CreatedAt,
Name: arg.Name,
Provisioners: arg.Provisioners,
Tags: arg.Tags,
ReplicaID: uuid.NullUUID{},
LastSeenAt: arg.LastSeenAt,
Version: arg.Version,
}
q.provisionerDaemons = append(q.provisionerDaemons, d)
return d, nil
}

func (q *FakeQuerier) UpsertServiceBanner(_ context.Context, data string) error {
q.mutex.RLock()
defer q.mutex.RUnlock()
Expand Down
14 changes: 7 additions & 7 deletions coderd/database/dbmetrics/dbmetrics.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 15 additions & 15 deletions coderd/database/dbmock/dbmock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 22 additions & 15 deletions coderd/database/dbpurge/dbpurge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/coder/coder/v2/coderd/database/dbpurge"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/provisionersdk"
"github.com/coder/coder/v2/testutil"
)

Expand Down Expand Up @@ -209,39 +210,45 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
now := dbtime.Now()

// given
_, err := db.InsertProvisionerDaemon(ctx, database.InsertProvisionerDaemonParams{
_, err := db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
// Provisioner daemon created 14 days ago, and checked in just before 7 days deadline.
ID: uuid.New(),
Name: "external-0",
Provisioners: []database.ProvisionerType{"echo"},
Tags: database.StringMap{provisionersdk.TagScope: provisionersdk.ScopeOrganization},
CreatedAt: now.Add(-14 * 24 * time.Hour),
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-7 * 24 * time.Hour).Add(time.Minute)},
})
require.NoError(t, err)
_, err = db.InsertProvisionerDaemon(ctx, database.InsertProvisionerDaemonParams{
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
// Provisioner daemon created 8 days ago, and checked in last time an hour after creation.
ID: uuid.New(),
Name: "external-1",
Provisioners: []database.ProvisionerType{"echo"},
Tags: database.StringMap{provisionersdk.TagScope: provisionersdk.ScopeOrganization},
CreatedAt: now.Add(-8 * 24 * time.Hour),
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-8 * 24 * time.Hour).Add(time.Hour)},
})
require.NoError(t, err)
_, err = db.InsertProvisionerDaemon(ctx, database.InsertProvisionerDaemonParams{
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
// Provisioner daemon created 9 days ago, and never checked in.
ID: uuid.New(),
Name: "external-2",
Name: "alice-provisioner",
Provisioners: []database.ProvisionerType{"echo"},
CreatedAt: now.Add(-9 * 24 * time.Hour),
Tags: database.StringMap{
provisionersdk.TagScope: provisionersdk.ScopeUser,
provisionersdk.TagOwner: uuid.NewString(),
},
CreatedAt: now.Add(-9 * 24 * time.Hour),
})
require.NoError(t, err)
_, err = db.InsertProvisionerDaemon(ctx, database.InsertProvisionerDaemonParams{
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
// Provisioner daemon created 6 days ago, and never checked in.
ID: uuid.New(),
Name: "external-3",
Name: "bob-provisioner",
Provisioners: []database.ProvisionerType{"echo"},
CreatedAt: now.Add(-6 * 24 * time.Hour),
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-6 * 24 * time.Hour)},
Tags: database.StringMap{
provisionersdk.TagScope: provisionersdk.ScopeUser,
provisionersdk.TagOwner: uuid.NewString(),
},
CreatedAt: now.Add(-6 * 24 * time.Hour),
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-6 * 24 * time.Hour)},
})
require.NoError(t, err)

Expand All @@ -257,8 +264,8 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
}
return containsProvisionerDaemon(daemons, "external-0") &&
!containsProvisionerDaemon(daemons, "external-1") &&
!containsProvisionerDaemon(daemons, "external-2") &&
containsProvisionerDaemon(daemons, "external-3")
!containsProvisionerDaemon(daemons, "alice-provisioner") &&
containsProvisionerDaemon(daemons, "bob-provisioner")
}, testutil.WaitShort, testutil.IntervalFast)
}

Expand Down
7 changes: 4 additions & 3 deletions coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DROP INDEX IF EXISTS idx_provisioner_daemons_name_owner_key;

ALTER TABLE ONLY provisioner_daemons
ADD CONSTRAINT provisioner_daemons_name_key UNIQUE (name);
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ALTER TABLE ONLY provisioner_daemons
DROP CONSTRAINT IF EXISTS provisioner_daemons_name_key;

CREATE UNIQUE INDEX IF NOT EXISTS idx_provisioner_daemons_name_owner_key
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, do we need an index since Day 1? What was the upper limit of provisioner daemons we hit in scaletests?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to at least keep the existing unique index on name.
However, this will allow multiple user-level provisioners to be named "my-provisioner".
Otherwise, each user will have to choose a unique provisioner name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, I ignored the use case with multiple users 👍 Thanks

ON provisioner_daemons
USING btree (name, lower((tags->>'owner')::text));

COMMENT ON INDEX idx_provisioner_daemons_name_owner_key
IS 'Relax uniqueness constraint for provisioner daemon names';

2 changes: 1 addition & 1 deletion coderd/database/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading