Skip to content

Commit e90acf0

Browse files
authored
feat: add coderd_provisioner_key resource (#141)
1 parent 1c5fef0 commit e90acf0

6 files changed

+304
-6
lines changed

docs/resources/provisioner_key.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "coderd_provisioner_key Resource - terraform-provider-coderd"
4+
subcategory: ""
5+
description: |-
6+
A provisioner key for a Coder deployment.
7+
---
8+
9+
# coderd_provisioner_key (Resource)
10+
11+
A provisioner key for a Coder deployment.
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Required
19+
20+
- `name` (String) The name of the key.
21+
- `organization_id` (String) The organization that provisioners connected with this key will be connected to.
22+
23+
### Optional
24+
25+
- `tags` (Map of String) The tags that provisioners connected with this key will accept jobs for.
26+
27+
### Read-Only
28+
29+
- `key` (String, Sensitive) The acquired provisioner key

internal/provider/license_resource_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestAccLicenseResource(t *testing.T) {
2424
t.Skip("No license found for license resource tests, skipping")
2525
}
2626

27-
cfg1 := testAccLicenseResourceconfig{
27+
cfg1 := testAccLicenseResourceConfig{
2828
URL: client.URL.String(),
2929
Token: client.SessionToken(),
3030
License: license,
@@ -42,13 +42,13 @@ func TestAccLicenseResource(t *testing.T) {
4242
})
4343
}
4444

45-
type testAccLicenseResourceconfig struct {
45+
type testAccLicenseResourceConfig struct {
4646
URL string
4747
Token string
4848
License string
4949
}
5050

51-
func (c testAccLicenseResourceconfig) String(t *testing.T) string {
51+
func (c testAccLicenseResourceConfig) String(t *testing.T) string {
5252
t.Helper()
5353
tpl := `
5454
provider coderd {

internal/provider/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ func (p *CoderdProvider) Resources(ctx context.Context) []func() resource.Resour
139139
NewWorkspaceProxyResource,
140140
NewLicenseResource,
141141
NewOrganizationResource,
142+
NewProvisionerKeyResource,
142143
}
143144
}
144145

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/resource"
8+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
9+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
10+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
11+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
12+
"github.com/hashicorp/terraform-plugin-framework/types"
13+
14+
"github.com/coder/coder/v2/codersdk"
15+
)
16+
17+
// Ensure provider defined types fully satisfy framework interfaces.
18+
var _ resource.Resource = &ProvisionerKeyResource{}
19+
20+
func NewProvisionerKeyResource() resource.Resource {
21+
return &ProvisionerKeyResource{}
22+
}
23+
24+
// ProvisionerKeyResource defines the resource implementation.
25+
type ProvisionerKeyResource struct {
26+
*CoderdProviderData
27+
}
28+
29+
// ProvisionerKeyResourceModel describes the resource data model.
30+
type ProvisionerKeyResourceModel struct {
31+
OrganizationID UUID `tfsdk:"organization_id"`
32+
Name types.String `tfsdk:"name"`
33+
Tags types.Map `tfsdk:"tags"`
34+
Key types.String `tfsdk:"key"`
35+
}
36+
37+
func (r *ProvisionerKeyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
38+
resp.TypeName = req.ProviderTypeName + "_provisioner_key"
39+
}
40+
41+
func (r *ProvisionerKeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
42+
resp.Schema = schema.Schema{
43+
MarkdownDescription: "A provisioner key for a Coder deployment.",
44+
45+
Attributes: map[string]schema.Attribute{
46+
"organization_id": schema.StringAttribute{
47+
CustomType: UUIDType,
48+
MarkdownDescription: "The organization that provisioners connected with this key will be connected to.",
49+
Required: true,
50+
PlanModifiers: []planmodifier.String{
51+
stringplanmodifier.RequiresReplace(),
52+
},
53+
},
54+
"name": schema.StringAttribute{
55+
MarkdownDescription: "The name of the key.",
56+
Required: true,
57+
PlanModifiers: []planmodifier.String{
58+
stringplanmodifier.RequiresReplace(),
59+
},
60+
},
61+
"tags": schema.MapAttribute{
62+
MarkdownDescription: "The tags that provisioners connected with this key will accept jobs for.",
63+
ElementType: types.StringType,
64+
Optional: true,
65+
PlanModifiers: []planmodifier.Map{
66+
mapplanmodifier.RequiresReplace(),
67+
},
68+
},
69+
"key": schema.StringAttribute{
70+
MarkdownDescription: "The acquired provisioner key",
71+
Computed: true,
72+
Sensitive: true,
73+
},
74+
},
75+
}
76+
}
77+
78+
func (r *ProvisionerKeyResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
79+
// Prevent panic if the provider has not been configured.
80+
if req.ProviderData == nil {
81+
return
82+
}
83+
84+
data, ok := req.ProviderData.(*CoderdProviderData)
85+
86+
if !ok {
87+
resp.Diagnostics.AddError(
88+
"Unexpected Resource Configure Type",
89+
fmt.Sprintf("Expected *CoderdProviderData, got: %T. Please report this issue to the provider developers.", req.ProviderData),
90+
)
91+
92+
return
93+
}
94+
95+
r.CoderdProviderData = data
96+
}
97+
98+
func (r *ProvisionerKeyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
99+
// Read Terraform plan data into the model
100+
var data ProvisionerKeyResourceModel
101+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
102+
if resp.Diagnostics.HasError() {
103+
return
104+
}
105+
106+
var tags map[string]string
107+
resp.Diagnostics.Append(data.Tags.ElementsAs(ctx, &tags, false)...)
108+
createKeyResult, err := r.Client.CreateProvisionerKey(ctx, data.OrganizationID.ValueUUID(), codersdk.CreateProvisionerKeyRequest{
109+
Name: data.Name.ValueString(),
110+
Tags: tags,
111+
})
112+
if err != nil {
113+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create provisioner_key, got error: %s", err))
114+
return
115+
}
116+
117+
data.Key = types.StringValue(createKeyResult.Key)
118+
// Save data into Terraform state
119+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
120+
}
121+
122+
func (r *ProvisionerKeyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
123+
// Read Terraform prior state data into the model
124+
var data ProvisionerKeyResourceModel
125+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
126+
if resp.Diagnostics.HasError() {
127+
return
128+
}
129+
130+
// Provisioner keys are immutable, no reading necessary.
131+
132+
// Save updated data into Terraform state
133+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
134+
}
135+
136+
func (r *ProvisionerKeyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
137+
// Provisioner keys are immutable, updating is always invalid.
138+
resp.Diagnostics.AddError("Invalid Update", "Terraform is attempting to update a resource which must be replaced")
139+
}
140+
141+
func (r *ProvisionerKeyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
142+
// Read Terraform prior state data into the model
143+
var data ProvisionerKeyResourceModel
144+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
145+
if resp.Diagnostics.HasError() {
146+
return
147+
}
148+
149+
err := r.Client.DeleteProvisionerKey(ctx, data.OrganizationID.ValueUUID(), data.Name.ValueString())
150+
if err != nil {
151+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete provisionerkey, got error: %s", err))
152+
return
153+
}
154+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"os"
6+
"strings"
7+
"testing"
8+
"text/template"
9+
10+
"github.com/coder/terraform-provider-coderd/integration"
11+
"github.com/google/uuid"
12+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
13+
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
14+
"github.com/hashicorp/terraform-plugin-testing/plancheck"
15+
"github.com/hashicorp/terraform-plugin-testing/statecheck"
16+
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
17+
"github.com/stretchr/testify/require"
18+
)
19+
20+
func TestAccProvisionerKeyResource(t *testing.T) {
21+
if os.Getenv("TF_ACC") == "" {
22+
t.Skip("Acceptance tests are disabled.")
23+
}
24+
ctx := context.Background()
25+
client := integration.StartCoder(ctx, t, "provisioner_key_acc", true)
26+
orgs, err := client.Organizations(ctx)
27+
require.NoError(t, err)
28+
firstOrg := orgs[0].ID
29+
30+
cfg1 := testAccProvisionerKeyResourceConfig{
31+
URL: client.URL.String(),
32+
Token: client.SessionToken(),
33+
34+
OrganizationID: firstOrg,
35+
Name: "example-provisioner-key",
36+
}
37+
38+
cfg2 := cfg1
39+
cfg2.Tags = map[string]string{
40+
"wibble": "wobble",
41+
}
42+
43+
cfg3 := cfg2
44+
cfg3.Name = "different-provisioner-key"
45+
46+
resource.Test(t, resource.TestCase{
47+
IsUnitTest: true,
48+
PreCheck: func() { testAccPreCheck(t) },
49+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
50+
Steps: []resource.TestStep{
51+
{
52+
Config: cfg1.String(t),
53+
},
54+
{
55+
Config: cfg2.String(t),
56+
ConfigPlanChecks: resource.ConfigPlanChecks{
57+
PreApply: []plancheck.PlanCheck{
58+
plancheck.ExpectResourceAction("coderd_provisioner_key.test", plancheck.ResourceActionReplace),
59+
},
60+
},
61+
ConfigStateChecks: []statecheck.StateCheck{
62+
statecheck.ExpectKnownValue("coderd_provisioner_key.test", tfjsonpath.New("tags").AtMapKey("wibble"), knownvalue.StringExact("wobble")),
63+
},
64+
},
65+
{
66+
Config: cfg3.String(t),
67+
ConfigPlanChecks: resource.ConfigPlanChecks{
68+
PreApply: []plancheck.PlanCheck{
69+
plancheck.ExpectResourceAction("coderd_provisioner_key.test", plancheck.ResourceActionReplace),
70+
},
71+
},
72+
},
73+
},
74+
})
75+
}
76+
77+
type testAccProvisionerKeyResourceConfig struct {
78+
URL string
79+
Token string
80+
81+
OrganizationID uuid.UUID
82+
Name string
83+
Tags map[string]string
84+
}
85+
86+
func (c testAccProvisionerKeyResourceConfig) String(t *testing.T) string {
87+
t.Helper()
88+
89+
tpl := `
90+
provider coderd {
91+
url = "{{.URL}}"
92+
token = "{{.Token}}"
93+
}
94+
95+
resource "coderd_provisioner_key" "test" {
96+
organization_id = "{{.OrganizationID}}"
97+
name = "{{.Name}}"
98+
99+
tags = {
100+
{{- range $key, $value := .Tags}}
101+
{{$key}} = "{{$value}}"
102+
{{- end}}
103+
}
104+
}
105+
`
106+
107+
buf := strings.Builder{}
108+
tmpl, err := template.New("provisionerKeyResource").Parse(tpl)
109+
require.NoError(t, err)
110+
111+
err = tmpl.Execute(&buf, c)
112+
require.NoError(t, err)
113+
return buf.String()
114+
}

internal/provider/template_resource_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ func TestAccTemplateResourceEnterprise(t *testing.T) {
424424
t.Skip("Acceptance tests are disabled.")
425425
}
426426
ctx := context.Background()
427-
client := integration.StartCoder(ctx, t, "template_acc", true)
427+
client := integration.StartCoder(ctx, t, "template_resource_acc", true)
428428
firstUser, err := client.User(ctx, codersdk.Me)
429429
require.NoError(t, err)
430430

@@ -565,7 +565,7 @@ func TestAccTemplateResourceAGPL(t *testing.T) {
565565
t.Skip("Acceptance tests are disabled.")
566566
}
567567
ctx := context.Background()
568-
client := integration.StartCoder(ctx, t, "template_acc", false)
568+
client := integration.StartCoder(ctx, t, "template_resource_agpl_acc", false)
569569
firstUser, err := client.User(ctx, codersdk.Me)
570570
require.NoError(t, err)
571571

@@ -689,7 +689,7 @@ resource "coderd_template" "sample" {
689689
}`
690690

691691
ctx := context.Background()
692-
client := integration.StartCoder(ctx, t, "template_acc", false)
692+
client := integration.StartCoder(ctx, t, "template_resource_variables_acc", false)
693693

694694
cfg = fmt.Sprintf(cfg, client.URL.String(), client.SessionToken())
695695

0 commit comments

Comments
 (0)