fix(coderd): only allow untagged provisioners to pick up untagged jobs (#12269)

Alternative solution to #6442

Modifies the behaviour of AcquireProvisionerJob and adds a special case for 'un-tagged' jobs such that they can only be picked up by 'un-tagged' provisioners.

Also adds comprehensive test coverage for AcquireJob given various combinations of tags.
This commit is contained in:
Cian Johnston
2024-02-22 15:04:31 +00:00
committed by GitHub
parent aa7a12a5ec
commit 53e8f9c0f9
5 changed files with 173 additions and 19 deletions
+27 -12
View File
@@ -748,6 +748,25 @@ var deletedUserLinkError = &pq.Error{
Routine: "exec_stmt_raise",
}
// m1 and m2 are equal iff |m1| = |m2| ^ m2 ⊆ m1
func tagsEqual(m1, m2 map[string]string) bool {
return len(m1) == len(m2) && tagsSubset(m1, m2)
}
// m2 is a subset of m1 if each key in m1 exists in m2
// with the same value
func tagsSubset(m1, m2 map[string]string) bool {
for k, v1 := range m1 {
if v2, found := m2[k]; !found || v1 != v2 {
return false
}
}
return true
}
// default tags when no tag is specified for a provisioner or job
var tagsUntagged = provisionersdk.MutateTags(uuid.Nil, nil)
func (*FakeQuerier) AcquireLock(_ context.Context, _ int64) error {
return xerrors.New("AcquireLock must only be called within a transaction")
}
@@ -783,19 +802,15 @@ func (q *FakeQuerier) AcquireProvisionerJob(_ context.Context, arg database.Acqu
}
}
missing := false
for key, value := range provisionerJob.Tags {
provided, found := tags[key]
if !found {
missing = true
break
}
if provided != value {
missing = true
break
}
// Special case for untagged provisioners: only match untagged jobs.
// Ref: coderd/database/queries/provisionerjobs.sql:24-30
// CASE WHEN nested.tags :: jsonb = '{"scope": "organization", "owner": ""}' :: jsonb
// THEN nested.tags :: jsonb = @tags :: jsonb
if tagsEqual(provisionerJob.Tags, tagsUntagged) && !tagsEqual(provisionerJob.Tags, tags) {
continue
}
if missing {
// ELSE nested.tags :: jsonb <@ @tags :: jsonb
if !tagsSubset(provisionerJob.Tags, tags) {
continue
}
provisionerJob.StartedAt = arg.StartedAt
+7 -2
View File
@@ -3936,8 +3936,13 @@ WHERE
nested.started_at IS NULL
-- Ensure the caller has the correct provisioner.
AND nested.provisioner = ANY($3 :: provisioner_type [ ])
-- Ensure the caller satisfies all job tags.
AND nested.tags <@ $4 :: jsonb
AND CASE
-- Special case for untagged provisioners: only match untagged jobs.
WHEN nested.tags :: jsonb = '{"scope": "organization", "owner": ""}' :: jsonb
THEN nested.tags :: jsonb = $4 :: jsonb
-- Ensure the caller satisfies all job tags.
ELSE nested.tags :: jsonb <@ $4 :: jsonb
END
ORDER BY
nested.created_at
FOR UPDATE
+7 -2
View File
@@ -21,8 +21,13 @@ WHERE
nested.started_at IS NULL
-- Ensure the caller has the correct provisioner.
AND nested.provisioner = ANY(@types :: provisioner_type [ ])
-- Ensure the caller satisfies all job tags.
AND nested.tags <@ @tags :: jsonb
AND CASE
-- Special case for untagged provisioners: only match untagged jobs.
WHEN nested.tags :: jsonb = '{"scope": "organization", "owner": ""}' :: jsonb
THEN nested.tags :: jsonb = @tags :: jsonb
-- Ensure the caller satisfies all job tags.
ELSE nested.tags :: jsonb <@ @tags :: jsonb
END
ORDER BY
nested.created_at
FOR UPDATE