mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
feat: add tags to provisioner keys api (#13989)
This commit is contained in:
Generated
+6
@@ -11024,6 +11024,12 @@ const docTemplate = `{
|
||||
"organization": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Generated
+6
@@ -9950,6 +9950,12 @@
|
||||
"organization": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+2
-1
@@ -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;
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Generated
+14
-8
@@ -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).
|
||||
|
||||
|
||||
Generated
+13
-7
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Generated
+2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user