feat(coderd): update API to allow filtering provisioner daemons by tags (#15448)

This PR provides new parameters to an endpoint that will be necessary
for #15048
This commit is contained in:
Sas Swart
2024-11-15 11:33:22 +02:00
committed by GitHub
parent 40802958e9
commit 814dd6f854
27 changed files with 389 additions and 80 deletions

View File

@ -3,10 +3,12 @@ package coderd_test
import (
"bytes"
"context"
"database/sql"
"fmt"
"io"
"net/http"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
@ -18,6 +20,7 @@ import (
"github.com/coder/coder/v2/buildinfo"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/provisionerkey"
"github.com/coder/coder/v2/coderd/rbac"
@ -768,7 +771,7 @@ func TestGetProvisionerDaemons(t *testing.T) {
require.NoError(t, err)
srv.DRPCConn().Close()
daemons, err := orgAdmin.OrganizationProvisionerDaemons(ctx, org.ID)
daemons, err := orgAdmin.OrganizationProvisionerDaemons(ctx, org.ID, nil)
require.NoError(t, err)
require.Len(t, daemons, 1)
@ -794,4 +797,207 @@ func TestGetProvisionerDaemons(t *testing.T) {
_, err = outsideOrg.ListProvisionerKeyDaemons(ctx, org.ID)
require.Error(t, err)
})
t.Run("filtered by tags", func(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
tagsToFilterBy map[string]string
provisionerDaemonTags map[string]string
expectToGetDaemon bool
}{
{
name: "only an empty tagset finds an untagged provisioner",
tagsToFilterBy: map[string]string{"scope": "organization", "owner": ""},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": ""},
expectToGetDaemon: true,
},
{
name: "an exact match with a single optional tag finds a provisioner daemon",
tagsToFilterBy: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem"},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem"},
expectToGetDaemon: true,
},
{
name: "a subset of filter tags finds a daemon with a superset of tags",
tagsToFilterBy: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem"},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem", "datacenter": "chicago"},
expectToGetDaemon: true,
},
{
name: "an exact match with two additional tags finds a provisioner daemon",
tagsToFilterBy: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem", "datacenter": "chicago"},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem", "datacenter": "chicago"},
expectToGetDaemon: true,
},
{
name: "a user scoped filter tag set finds a user scoped provisioner daemon",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa"},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa"},
expectToGetDaemon: true,
},
{
name: "a user scoped filter tag set finds a user scoped provisioner daemon with an additional tag",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa"},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem"},
expectToGetDaemon: true,
},
{
name: "user-scoped provisioner with tags and user-scoped filter with tags",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem"},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem"},
expectToGetDaemon: true,
},
{
name: "user-scoped provisioner with multiple tags and user-scoped filter with a subset of tags",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem"},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem", "datacenter": "chicago"},
expectToGetDaemon: true,
},
{
name: "user-scoped provisioner with multiple tags and user-scoped filter with multiple tags",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem", "datacenter": "chicago"},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem", "datacenter": "chicago"},
expectToGetDaemon: true,
},
{
name: "untagged provisioner and tagged filter",
tagsToFilterBy: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem"},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": ""},
expectToGetDaemon: false,
},
{
name: "tagged provisioner and untagged filter",
tagsToFilterBy: map[string]string{"scope": "organization", "owner": ""},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem"},
expectToGetDaemon: false,
},
{
name: "tagged provisioner and double-tagged filter",
tagsToFilterBy: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem", "datacenter": "chicago"},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem"},
expectToGetDaemon: false,
},
{
name: "double-tagged provisioner and double-tagged filter with differing tags",
tagsToFilterBy: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem", "datacenter": "chicago"},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": "", "environment": "on-prem", "datacenter": "new_york"},
expectToGetDaemon: false,
},
{
name: "user-scoped provisioner and untagged filter",
tagsToFilterBy: map[string]string{"scope": "organization", "owner": ""},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa"},
expectToGetDaemon: false,
},
{
name: "user-scoped provisioner and different user-scoped filter",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "bbb"},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa"},
expectToGetDaemon: false,
},
{
name: "org-scoped provisioner and user-scoped filter",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa"},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": ""},
expectToGetDaemon: false,
},
{
name: "user-scoped provisioner and org-scoped filter with tags",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem"},
provisionerDaemonTags: map[string]string{"scope": "organization", "owner": ""},
expectToGetDaemon: false,
},
{
name: "user-scoped provisioner and user-scoped filter with tags",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem"},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa"},
expectToGetDaemon: false,
},
{
name: "user-scoped provisioner with tags and user-scoped filter with multiple tags",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem", "datacenter": "chicago"},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem"},
expectToGetDaemon: false,
},
{
name: "user-scoped provisioner with tags and user-scoped filter with differing tags",
tagsToFilterBy: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem", "datacenter": "new_york"},
provisionerDaemonTags: map[string]string{"scope": "user", "owner": "aaa", "environment": "on-prem", "datacenter": "chicago"},
expectToGetDaemon: false,
},
}
for _, tt := range testCases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
dv := coderdtest.DeploymentValues(t)
client, db, _ := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
Options: &coderdtest.Options{
DeploymentValues: dv,
},
ProvisionerDaemonPSK: "provisionersftw",
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureExternalProvisionerDaemons: 1,
codersdk.FeatureMultipleOrganizations: 1,
},
},
})
ctx := testutil.Context(t, testutil.WaitShort)
org := coderdenttest.CreateOrganization(t, client, coderdenttest.CreateOrganizationOptions{
IncludeProvisionerDaemon: false,
})
orgAdmin, _ := coderdtest.CreateAnotherUser(t, client, org.ID, rbac.ScopedRoleOrgMember(org.ID))
daemonCreatedAt := time.Now()
//nolint:gocritic // We're not testing auth on the following in this test
provisionerKey, err := db.InsertProvisionerKey(dbauthz.AsSystemRestricted(ctx), database.InsertProvisionerKeyParams{
Name: "Test Provisioner Key",
ID: uuid.New(),
CreatedAt: daemonCreatedAt,
OrganizationID: org.ID,
HashedSecret: []byte{},
Tags: tt.provisionerDaemonTags,
})
require.NoError(t, err, "should be able to create a provisioner key")
//nolint:gocritic // We're not testing auth on the following in this test
pd, err := db.UpsertProvisionerDaemon(dbauthz.AsSystemRestricted(ctx), database.UpsertProvisionerDaemonParams{
CreatedAt: daemonCreatedAt,
Name: "Test Provisioner Daemon",
Provisioners: []database.ProvisionerType{},
Tags: tt.provisionerDaemonTags,
LastSeenAt: sql.NullTime{
Time: daemonCreatedAt,
Valid: true,
},
Version: "",
OrganizationID: org.ID,
APIVersion: "",
KeyID: provisionerKey.ID,
})
require.NoError(t, err, "should be able to create provisioner daemon")
daemonAsCreated := db2sdk.ProvisionerDaemon(pd)
allDaemons, err := orgAdmin.OrganizationProvisionerDaemons(ctx, org.ID, nil)
require.NoError(t, err)
require.Len(t, allDaemons, 1)
daemonsAsFound, err := orgAdmin.OrganizationProvisionerDaemons(ctx, org.ID, tt.tagsToFilterBy)
if tt.expectToGetDaemon {
require.NoError(t, err)
require.Len(t, daemonsAsFound, 1)
require.Equal(t, daemonAsCreated.Tags, daemonsAsFound[0].Tags, "found daemon should have the same tags as created daemon")
require.Equal(t, daemonsAsFound[0].KeyID, provisionerKey.ID)
} else {
require.NoError(t, err)
assert.Empty(t, daemonsAsFound, "should not have found daemon")
}
})
}
})
}