mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user