feat: add tags to provisioner keys api (#13989)

This commit is contained in:
Garrett Delfosse
2024-07-25 15:20:45 +00:00
committed by GitHub
parent ca83017dc1
commit 6161d173d3
21 changed files with 120 additions and 49 deletions
+6
View File
@@ -11024,6 +11024,12 @@ const docTemplate = `{
"organization": {
"type": "string",
"format": "uuid"
},
"tags": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
},
+6
View File
@@ -9950,6 +9950,12 @@
"organization": {
"type": "string",
"format": "uuid"
},
"tags": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
},
+1
View File
@@ -472,6 +472,7 @@ func ProvisionerKey(t testing.TB, db database.Store, orig database.ProvisionerKe
OrganizationID: takeFirst(orig.OrganizationID, uuid.New()),
Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)),
HashedSecret: orig.HashedSecret,
Tags: orig.Tags,
})
require.NoError(t, err, "insert provisioner key")
return key
+2 -7
View File
@@ -6586,6 +6586,7 @@ func (q *FakeQuerier) InsertProvisionerKey(_ context.Context, arg database.Inser
OrganizationID: arg.OrganizationID,
Name: strings.ToLower(arg.Name),
HashedSecret: arg.HashedSecret,
Tags: arg.Tags,
}
q.provisionerKeys = append(q.provisionerKeys, provisionerKey)
@@ -7276,13 +7277,7 @@ func (q *FakeQuerier) ListProvisionerKeysByOrganization(_ context.Context, organ
keys := make([]database.ProvisionerKey, 0)
for _, key := range q.provisionerKeys {
if key.OrganizationID == organizationID {
keys = append(keys, database.ProvisionerKey{
ID: key.ID,
CreatedAt: key.CreatedAt,
OrganizationID: key.OrganizationID,
Name: key.Name,
HashedSecret: key.HashedSecret,
})
keys = append(keys, key)
}
}
+2 -1
View File
@@ -754,7 +754,8 @@ CREATE TABLE provisioner_keys (
created_at timestamp with time zone NOT NULL,
organization_id uuid NOT NULL,
name character varying(64) NOT NULL,
hashed_secret bytea NOT NULL
hashed_secret bytea NOT NULL,
tags jsonb NOT NULL
);
CREATE TABLE replicas (
@@ -0,0 +1 @@
ALTER TABLE provisioner_keys DROP COLUMN tags;
@@ -0,0 +1,2 @@
ALTER TABLE provisioner_keys ADD COLUMN tags jsonb DEFAULT '{}'::jsonb NOT NULL;
ALTER TABLE provisioner_keys ALTER COLUMN tags DROP DEFAULT;
+1
View File
@@ -2191,6 +2191,7 @@ type ProvisionerKey struct {
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Name string `db:"name" json:"name"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
Tags StringMap `db:"tags" json:"tags"`
}
type Replica struct {
+16 -9
View File
@@ -5533,7 +5533,7 @@ func (q *sqlQuerier) DeleteProvisionerKey(ctx context.Context, id uuid.UUID) err
const getProvisionerKeyByID = `-- name: GetProvisionerKeyByID :one
SELECT
id, created_at, organization_id, name, hashed_secret
id, created_at, organization_id, name, hashed_secret, tags
FROM
provisioner_keys
WHERE
@@ -5549,13 +5549,14 @@ func (q *sqlQuerier) GetProvisionerKeyByID(ctx context.Context, id uuid.UUID) (P
&i.OrganizationID,
&i.Name,
&i.HashedSecret,
&i.Tags,
)
return i, err
}
const getProvisionerKeyByName = `-- name: GetProvisionerKeyByName :one
SELECT
id, created_at, organization_id, name, hashed_secret
id, created_at, organization_id, name, hashed_secret, tags
FROM
provisioner_keys
WHERE
@@ -5578,21 +5579,23 @@ func (q *sqlQuerier) GetProvisionerKeyByName(ctx context.Context, arg GetProvisi
&i.OrganizationID,
&i.Name,
&i.HashedSecret,
&i.Tags,
)
return i, err
}
const insertProvisionerKey = `-- name: InsertProvisionerKey :one
INSERT INTO
provisioner_keys (
id,
provisioner_keys (
id,
created_at,
organization_id,
name,
hashed_secret
)
name,
hashed_secret,
tags
)
VALUES
($1, $2, $3, lower($5), $4) RETURNING id, created_at, organization_id, name, hashed_secret
($1, $2, $3, lower($6), $4, $5) RETURNING id, created_at, organization_id, name, hashed_secret, tags
`
type InsertProvisionerKeyParams struct {
@@ -5600,6 +5603,7 @@ type InsertProvisionerKeyParams struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
Tags StringMap `db:"tags" json:"tags"`
Name string `db:"name" json:"name"`
}
@@ -5609,6 +5613,7 @@ func (q *sqlQuerier) InsertProvisionerKey(ctx context.Context, arg InsertProvisi
arg.CreatedAt,
arg.OrganizationID,
arg.HashedSecret,
arg.Tags,
arg.Name,
)
var i ProvisionerKey
@@ -5618,13 +5623,14 @@ func (q *sqlQuerier) InsertProvisionerKey(ctx context.Context, arg InsertProvisi
&i.OrganizationID,
&i.Name,
&i.HashedSecret,
&i.Tags,
)
return i, err
}
const listProvisionerKeysByOrganization = `-- name: ListProvisionerKeysByOrganization :many
SELECT
id, created_at, organization_id, name, hashed_secret
id, created_at, organization_id, name, hashed_secret, tags
FROM
provisioner_keys
WHERE
@@ -5646,6 +5652,7 @@ func (q *sqlQuerier) ListProvisionerKeysByOrganization(ctx context.Context, orga
&i.OrganizationID,
&i.Name,
&i.HashedSecret,
&i.Tags,
); err != nil {
return nil, err
}
+7 -6
View File
@@ -1,14 +1,15 @@
-- name: InsertProvisionerKey :one
INSERT INTO
provisioner_keys (
id,
provisioner_keys (
id,
created_at,
organization_id,
name,
hashed_secret
)
name,
hashed_secret,
tags
)
VALUES
($1, $2, $3, lower(@name), $4) RETURNING *;
($1, $2, $3, lower(@name), $4, $5) RETURNING *;
-- name: GetProvisionerKeyByID :one
SELECT
+3
View File
@@ -44,6 +44,9 @@ sql:
- column: "provisioner_daemons.tags"
go_type:
type: "StringMap"
- column: "provisioner_keys.tags"
go_type:
type: "StringMap"
- column: "provisioner_jobs.tags"
go_type:
type: "StringMap"
+6 -1
View File
@@ -14,7 +14,7 @@ import (
"github.com/coder/coder/v2/cryptorand"
)
func New(organizationID uuid.UUID, name string) (database.InsertProvisionerKeyParams, string, error) {
func New(organizationID uuid.UUID, name string, tags map[string]string) (database.InsertProvisionerKeyParams, string, error) {
id := uuid.New()
secret, err := cryptorand.HexString(64)
if err != nil {
@@ -23,12 +23,17 @@ func New(organizationID uuid.UUID, name string) (database.InsertProvisionerKeyPa
hashedSecret := HashSecret(secret)
token := fmt.Sprintf("%s:%s", id, secret)
if tags == nil {
tags = map[string]string{}
}
return database.InsertProvisionerKeyParams{
ID: id,
CreatedAt: dbtime.Now(),
OrganizationID: organizationID,
Name: name,
HashedSecret: hashedSecret,
Tags: tags,
}, token, nil
}
+7 -5
View File
@@ -274,15 +274,17 @@ func (c *Client) ServeProvisionerDaemon(ctx context.Context, req ServeProvisione
}
type ProvisionerKey struct {
ID uuid.UUID `json:"id" table:"-" format:"uuid"`
CreatedAt time.Time `json:"created_at" table:"created_at" format:"date-time"`
OrganizationID uuid.UUID `json:"organization" table:"organization_id" format:"uuid"`
Name string `json:"name" table:"name,default_sort"`
ID uuid.UUID `json:"id" table:"-" format:"uuid"`
CreatedAt time.Time `json:"created_at" table:"created_at" format:"date-time"`
OrganizationID uuid.UUID `json:"organization" table:"organization_id" format:"uuid"`
Name string `json:"name" table:"name,default_sort"`
Tags map[string]string `json:"tags" table:"tags"`
// HashedSecret - never include the access token in the API response
}
type CreateProvisionerKeyRequest struct {
Name string `json:"name"`
Name string `json:"name"`
Tags map[string]string `json:"tags"`
}
type CreateProvisionerKeyResponse struct {
+14 -8
View File
@@ -1389,7 +1389,11 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"organization": "452c1a86-a0af-475b-b03f-724878b0f387"
"organization": "452c1a86-a0af-475b-b03f-724878b0f387",
"tags": {
"property1": "string",
"property2": "string"
}
}
]
```
@@ -1404,13 +1408,15 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi
Status Code **200**
| Name | Type | Required | Restrictions | Description |
| ---------------- | ----------------- | -------- | ------------ | ----------- |
| `[array item]` | array | false | | |
| `» created_at` | string(date-time) | false | | |
| `» id` | string(uuid) | false | | |
| `» name` | string | false | | |
| `» organization` | string(uuid) | false | | |
| Name | Type | Required | Restrictions | Description |
| ------------------- | ----------------- | -------- | ------------ | ----------- |
| `[array item]` | array | false | | |
| `» created_at` | string(date-time) | false | | |
| `» id` | string(uuid) | false | | |
| `» name` | string | false | | |
| `» organization` | string(uuid) | false | | |
| `» tags` | object | false | | |
| `»» [any property]` | string | false | | |
To perform this operation, you must be authenticated. [Learn more](authentication.md).
+13 -7
View File
@@ -3995,18 +3995,24 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
"created_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"organization": "452c1a86-a0af-475b-b03f-724878b0f387"
"organization": "452c1a86-a0af-475b-b03f-724878b0f387",
"tags": {
"property1": "string",
"property2": "string"
}
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
| -------------- | ------ | -------- | ------------ | ----------- |
| `created_at` | string | false | | |
| `id` | string | false | | |
| `name` | string | false | | |
| `organization` | string | false | | |
| Name | Type | Required | Restrictions | Description |
| ------------------ | ------ | -------- | ------------ | ----------- |
| `created_at` | string | false | | |
| `id` | string | false | | |
| `name` | string | false | | |
| `organization` | string | false | | |
| `tags` | object | false | | |
| » `[any property]` | string | false | | |
## codersdk.ProvisionerLogLevel
+19 -2
View File
@@ -33,7 +33,10 @@ func (r *RootCmd) provisionerKeys() *serpent.Command {
}
func (r *RootCmd) provisionerKeysCreate() *serpent.Command {
orgContext := agpl.NewOrganizationContext()
var (
orgContext = agpl.NewOrganizationContext()
rawTags []string
)
client := new(codersdk.Client)
cmd := &serpent.Command{
@@ -51,8 +54,14 @@ func (r *RootCmd) provisionerKeysCreate() *serpent.Command {
return xerrors.Errorf("current organization: %w", err)
}
tags, err := agpl.ParseProvisionerTags(rawTags)
if err != nil {
return err
}
res, err := client.CreateProvisionerKey(ctx, org.ID, codersdk.CreateProvisionerKeyRequest{
Name: inv.Args[0],
Tags: tags,
})
if err != nil {
return xerrors.Errorf("create provisioner key: %w", err)
@@ -69,7 +78,15 @@ func (r *RootCmd) provisionerKeysCreate() *serpent.Command {
},
}
cmd.Options = serpent.OptionSet{}
cmd.Options = serpent.OptionSet{
{
Flag: "tag",
FlagShorthand: "t",
Env: "CODER_PROVISIONERD_TAGS",
Description: "Tags to filter provisioner jobs by.",
Value: serpent.StringArrayOf(&rawTags),
},
}
orgContext.AttachOptions(cmd)
return cmd
+3 -1
View File
@@ -41,7 +41,7 @@ func TestProvisionerKeys(t *testing.T) {
ctx := testutil.Context(t, testutil.WaitMedium)
inv, conf := newCLI(
t,
"provisioner", "keys", "create", name,
"provisioner", "keys", "create", name, "--tag", "foo=bar",
)
pty := ptytest.New(t)
@@ -77,8 +77,10 @@ func TestProvisionerKeys(t *testing.T) {
require.Contains(t, line, "NAME")
require.Contains(t, line, "CREATED AT")
require.Contains(t, line, "ORGANIZATION ID")
require.Contains(t, line, "TAGS")
line = pty.ReadLine(ctx)
require.Contains(t, line, strings.ToLower(name))
require.Contains(t, line, "map[foo:bar]")
inv, conf = newCLI(
t,
+1 -1
View File
@@ -559,7 +559,7 @@ func TestProvisionerDaemonServe(t *testing.T) {
t.Run("ProvisionerKeyAuth", func(t *testing.T) {
t.Parallel()
insertParams, token, err := provisionerkey.New(uuid.Nil, "dont-TEST-me")
insertParams, token, err := provisionerkey.New(uuid.Nil, "dont-TEST-me", nil)
require.NoError(t, err)
tcs := []struct {
+2 -1
View File
@@ -54,7 +54,7 @@ func (api *API) postProvisionerKey(rw http.ResponseWriter, r *http.Request) {
return
}
params, token, err := provisionerkey.New(organization.ID, req.Name)
params, token, err := provisionerkey.New(organization.ID, req.Name, req.Tags)
if err != nil {
httpapi.InternalServerError(rw, err)
return
@@ -142,6 +142,7 @@ func convertProvisionerKeys(dbKeys []database.ProvisionerKey) []codersdk.Provisi
CreatedAt: dbKey.CreatedAt,
OrganizationID: dbKey.OrganizationID,
Name: dbKey.Name,
Tags: dbKey.Tags,
// HashedSecret - never include the access token in the API response
})
}
@@ -69,9 +69,13 @@ func TestProvisionerKeys(t *testing.T) {
require.NoError(t, err, "org admin list provisioner keys")
require.Len(t, keys, 0, "org admin list provisioner keys")
tags := map[string]string{
"my": "way",
}
// org admin can create a provisioner key
_, err = orgAdmin.CreateProvisionerKey(ctx, owner.OrganizationID, codersdk.CreateProvisionerKeyRequest{
Name: "Key", // case insensitive
Tags: tags,
})
require.NoError(t, err, "org admin create provisioner key")
@@ -97,6 +101,8 @@ func TestProvisionerKeys(t *testing.T) {
keys, err = orgAdmin.ListProvisionerKeys(ctx, owner.OrganizationID)
require.NoError(t, err, "org admin list provisioner keys")
require.Len(t, keys, 1, "org admin list provisioner keys")
require.Equal(t, "key", keys[0].Name, "org admin list provisioner keys name matches")
require.EqualValues(t, tags, keys[0].Tags, "org admin list provisioner keys tags match")
// org admin can delete a provisioner key
err = orgAdmin.DeleteProvisionerKey(ctx, owner.OrganizationID, "key") // using lowercase here works
+2
View File
@@ -237,6 +237,7 @@ export interface CreateOrganizationRequest {
// From codersdk/provisionerdaemons.go
export interface CreateProvisionerKeyRequest {
readonly name: string;
readonly tags: Record<string, string>;
}
// From codersdk/provisionerdaemons.go
@@ -1002,6 +1003,7 @@ export interface ProvisionerKey {
readonly created_at: string;
readonly organization: string;
readonly name: string;
readonly tags: Record<string, string>;
}
// From codersdk/workspaceproxy.go