Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9fd275a

Browse files
committedAug 2, 2024·
chore: publishing setup
1 parent 557da95 commit 9fd275a

File tree

19 files changed

+371
-36
lines changed

19 files changed

+371
-36
lines changed
 

‎.github/CODEOWNERS

Lines changed: 0 additions & 1 deletion
This file was deleted.

‎.github/dependabot.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,3 @@ updates:
1010
directory: "/"
1111
schedule:
1212
interval: "daily"
13-
# TODO: Dependabot only updates hashicorp GHAs in the template repository, the following lines can be removed for consumers of this template
14-
allow:
15-
- dependency-name: "hashicorp/*"

‎.github/workflows/release.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
11+
12+
jobs:
13+
goreleaser:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
- name: Unshallow
19+
run: git fetch --prune --unshallow
20+
- name: Setup Go
21+
uses: actions/setup-go@v5
22+
with:
23+
go-version: 1.22.4
24+
- name: Import GPG Key
25+
id: import_gpg
26+
uses: crazy-max/ghaction-import-gpg@v6.1.0
27+
with:
28+
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
29+
passphrase: ${{ secrets.GPG_PASSPHRASE }}
30+
- name: Run GoReleaser
31+
uses: goreleaser/goreleaser-action@v6.0.0
32+
with:
33+
version: latest
34+
args: release --clean
35+
env:
36+
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
37+
# GitHub sets this automatically
38+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39+

‎.goreleaser.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Visit https://goreleaser.com for documentation on how to customize this
22
# behavior.
3+
version: 2
34
before:
45
hooks:
56
# this is just an example and not a requirement for provider building/publishing
@@ -41,7 +42,7 @@ checksum:
4142
signs:
4243
- artifacts: checksum
4344
args:
44-
# if you are using this in a GitHub action or some other automated pipeline, you
45+
# if you are using this in a GitHub action or some other automated pipeline, you
4546
# need to pass the batch flag to indicate its not interactive.
4647
- "--batch"
4748
- "--local-user"
@@ -57,4 +58,4 @@ release:
5758
# If you want to manually examine the release before its live, uncomment this line:
5859
# draft: true
5960
changelog:
60-
skip: true
61+
disable: true

‎LICENSE

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
Copyright (c) 2021 HashiCorp, Inc.
2-
31
Mozilla Public License Version 2.0
42
==================================
53

‎docs/data-sources/group.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "coderd_group Data Source - coderd"
3+
page_title: "coderd_group Data Source - terraform-provider-coderd"
44
subcategory: ""
55
description: |-
66
An existing group on the coder deployment.

‎docs/data-sources/organization.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "coderd_organization Data Source - coderd"
3+
page_title: "coderd_organization Data Source - terraform-provider-coderd"
44
subcategory: ""
55
description: |-
66
An existing organization on the coder deployment.

‎docs/data-sources/template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "coderd_template Data Source - coderd"
3+
page_title: "coderd_template Data Source - terraform-provider-coderd"
44
subcategory: ""
55
description: |-
66
An existing template on the Coder deployment.

‎docs/data-sources/user.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "coderd_user Data Source - coderd"
3+
page_title: "coderd_user Data Source - terraform-provider-coderd"
44
subcategory: ""
55
description: |-
66
An existing user on the coder deployment

‎docs/resources/group.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "coderd_group Resource - coderd"
3+
page_title: "coderd_group Resource - terraform-provider-coderd"
44
subcategory: ""
55
description: |-
66
A group on the Coder deployment. If you want to have a group resource with unmanaged members, but still want to read the members in Terraform, use the data.coderd_group data source.

‎docs/resources/template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "coderd_template Resource - coderd"
3+
page_title: "coderd_template Resource - terraform-provider-coderd"
44
subcategory: ""
55
description: |-
66
A Coder template

‎docs/resources/user.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "coderd_user Resource - coderd"
3+
page_title: "coderd_user Resource - terraform-provider-coderd"
44
subcategory: ""
55
description: |-
66
A user on the Coder deployment.

‎docs/resources/workspace_proxy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "coderd_workspace_proxy Resource - coderd"
3+
page_title: "coderd_workspace_proxy Resource - terraform-provider-coderd"
44
subcategory: ""
55
description: |-
66
A Workspace Proxy for the Coder deployment.

‎internal/provider/group_resource.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// Copyright (c) HashiCorp, Inc.
2-
// SPDX-License-Identifier: MPL-2.0
3-
41
package provider
52

63
import (

‎internal/provider/group_resource_test.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// Copyright (c) HashiCorp, Inc.
2-
// SPDX-License-Identifier: MPL-2.0
3-
41
package provider
52

63
import (
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/coder/coder/v2/coderd/util/slice"
8+
"github.com/coder/coder/v2/codersdk"
9+
"github.com/google/uuid"
10+
"github.com/hashicorp/terraform-plugin-framework/attr"
11+
"github.com/hashicorp/terraform-plugin-framework/path"
12+
"github.com/hashicorp/terraform-plugin-framework/resource"
13+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
14+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
15+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
16+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
17+
"github.com/hashicorp/terraform-plugin-framework/types"
18+
"github.com/hashicorp/terraform-plugin-log/tflog"
19+
)
20+
21+
// Ensure provider defined types fully satisfy framework interfaces.
22+
var _ resource.Resource = &OrganizationResource{}
23+
var _ resource.ResourceWithImportState = &OrganizationResource{}
24+
25+
func NewOrganizationResource() resource.Resource {
26+
return &OrganizationResource{}
27+
}
28+
29+
// OrganizationResource defines the resource implementation.
30+
type OrganizationResource struct {
31+
data *CoderdProviderData
32+
}
33+
34+
// OrganizationResourceModel describes the resource data model.
35+
type OrganizationResourceModel struct {
36+
ID UUID `tfsdk:"id"`
37+
38+
Name types.String `tfsdk:"name"`
39+
DisplayName types.String `tfsdk:"display_name"`
40+
Description types.String `tfsdk:"description"`
41+
Icon types.String `tfsdk:"icon"`
42+
Members types.Set `tfsdk:"members"`
43+
}
44+
45+
func (r *OrganizationResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
46+
resp.TypeName = req.ProviderTypeName + "_organization"
47+
}
48+
49+
func (r *OrganizationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
50+
resp.Schema = schema.Schema{
51+
MarkdownDescription: "An organization on the coder deployment.",
52+
53+
Attributes: map[string]schema.Attribute{
54+
"id": schema.StringAttribute{
55+
CustomType: UUIDType,
56+
Computed: true,
57+
PlanModifiers: []planmodifier.String{
58+
stringplanmodifier.UseStateForUnknown(),
59+
},
60+
},
61+
"name": schema.StringAttribute{
62+
Required: true,
63+
},
64+
"display_name": schema.StringAttribute{
65+
Optional: true,
66+
Computed: true,
67+
},
68+
"description": schema.StringAttribute{
69+
Optional: true,
70+
Computed: true,
71+
Default: stringdefault.StaticString(""),
72+
},
73+
"icon": schema.StringAttribute{
74+
Optional: true,
75+
Computed: true,
76+
Default: stringdefault.StaticString(""),
77+
},
78+
"members": schema.SetAttribute{
79+
MarkdownDescription: "Members of the organization, by ID. If null, members will not be added or removed by Terraform.",
80+
ElementType: UUIDType,
81+
Optional: true,
82+
},
83+
// TODO: Custom roles, premium license gated
84+
},
85+
}
86+
}
87+
88+
func (r *OrganizationResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
89+
// Prevent panic if the provider has not been configured.
90+
if req.ProviderData == nil {
91+
return
92+
}
93+
94+
data, ok := req.ProviderData.(*CoderdProviderData)
95+
96+
if !ok {
97+
resp.Diagnostics.AddError(
98+
"Unexpected Resource Configure Type",
99+
fmt.Sprintf("Expected *CoderdProviderData, got: %T. Please report this issue to the provider developers.", req.ProviderData),
100+
)
101+
102+
return
103+
}
104+
105+
r.data = data
106+
}
107+
108+
func (r *OrganizationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
109+
var data OrganizationResourceModel
110+
111+
// Read Terraform plan data into the model
112+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
113+
114+
if resp.Diagnostics.HasError() {
115+
return
116+
}
117+
118+
client := r.data.Client
119+
120+
displayName := data.Name.ValueString()
121+
if data.DisplayName.ValueString() != "" {
122+
displayName = data.DisplayName.ValueString()
123+
}
124+
125+
tflog.Trace(ctx, "creating organization")
126+
org, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
127+
Name: data.Name.ValueString(),
128+
DisplayName: displayName,
129+
Description: data.Description.ValueString(),
130+
Icon: data.Icon.ValueString(),
131+
})
132+
if err != nil {
133+
resp.Diagnostics.AddError("Failed to create organization", err.Error())
134+
return
135+
}
136+
tflog.Trace(ctx, "successfully created organization", map[string]any{
137+
"id": org.ID,
138+
})
139+
data.ID = UUIDValue(org.ID)
140+
data.DisplayName = types.StringValue(org.DisplayName)
141+
142+
tflog.Trace(ctx, "setting organization members")
143+
var members []UUID
144+
resp.Diagnostics.Append(data.Members.ElementsAs(ctx, &members, false)...)
145+
if resp.Diagnostics.HasError() {
146+
return
147+
}
148+
for _, memberID := range members {
149+
_, err = client.PostOrganizationMember(ctx, org.ID, memberID.ValueString())
150+
if err != nil {
151+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to add member %s to organization %s, got error: %s", memberID, org.ID, err))
152+
return
153+
}
154+
}
155+
156+
me, err := client.User(ctx, codersdk.Me)
157+
if err != nil {
158+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get current user, got error: %s", err))
159+
return
160+
}
161+
162+
// If the logged-in user isn't in the members list, remove them from the organization (as they were added by default)
163+
// Ideally, future Coder versions won't add the logged-in user by default.
164+
if !slice.Contains(members, UUIDValue(me.ID)) {
165+
err = client.DeleteOrganizationMember(ctx, org.ID, codersdk.Me)
166+
if err != nil {
167+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete self from new organization: %s", err))
168+
}
169+
}
170+
171+
tflog.Trace(ctx, "successfully set organization members")
172+
// Save data into Terraform state
173+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
174+
}
175+
176+
func (r *OrganizationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
177+
var data OrganizationResourceModel
178+
179+
// Read Terraform prior state data into the model
180+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
181+
182+
if resp.Diagnostics.HasError() {
183+
return
184+
}
185+
186+
client := r.data.Client
187+
188+
orgID := data.ID.ValueUUID()
189+
org, err := client.Organization(ctx, orgID)
190+
if err != nil {
191+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get organization by ID, got error: %s", err))
192+
}
193+
194+
data.Name = types.StringValue(org.Name)
195+
data.DisplayName = types.StringValue(org.DisplayName)
196+
data.Description = types.StringValue(org.Description)
197+
data.Icon = types.StringValue(org.Icon)
198+
if !data.Members.IsNull() {
199+
members, err := client.OrganizationMembers(ctx, orgID)
200+
if err != nil {
201+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get organization members, got error: %s", err))
202+
return
203+
}
204+
memberIDs := make([]attr.Value, 0, len(members))
205+
for _, member := range members {
206+
memberIDs = append(memberIDs, UUIDValue(member.UserID))
207+
}
208+
data.Members = types.SetValueMust(UUIDType, memberIDs)
209+
}
210+
211+
// Save updated data into Terraform state
212+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
213+
}
214+
215+
func (r *OrganizationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
216+
var data OrganizationResourceModel
217+
218+
// Read Terraform plan data into the model
219+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
220+
221+
if resp.Diagnostics.HasError() {
222+
return
223+
}
224+
225+
client := r.data.Client
226+
orgID := data.ID.ValueUUID()
227+
228+
orgMembers, err := client.OrganizationMembers(ctx, orgID)
229+
if err != nil {
230+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get organization members , got error: %s", err))
231+
return
232+
}
233+
234+
if !data.Members.IsNull() {
235+
var plannedMembers []UUID
236+
resp.Diagnostics.Append(data.Members.ElementsAs(ctx, &plannedMembers, false)...)
237+
if resp.Diagnostics.HasError() {
238+
return
239+
}
240+
curMembers := make([]uuid.UUID, 0, len(orgMembers))
241+
for _, member := range orgMembers {
242+
curMembers = append(curMembers, member.UserID)
243+
}
244+
add, remove := memberDiff(curMembers, plannedMembers)
245+
tflog.Trace(ctx, "updating organization members", map[string]any{
246+
"new_members": add,
247+
"removed_members": remove,
248+
})
249+
for _, memberID := range add {
250+
_, err := client.PostOrganizationMember(ctx, orgID, memberID)
251+
if err != nil {
252+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to add member %s to organization %s, got error: %s", memberID, orgID, err))
253+
return
254+
}
255+
}
256+
for _, memberID := range remove {
257+
err := client.DeleteOrganizationMember(ctx, orgID, memberID)
258+
if err != nil {
259+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to remove member %s from organization %s, got error: %s", memberID, orgID, err))
260+
return
261+
}
262+
}
263+
tflog.Trace(ctx, "successfully updated organization members")
264+
}
265+
266+
tflog.Trace(ctx, "updating organization", map[string]any{
267+
"id": orgID,
268+
"new_name": data.Name,
269+
"new_display_name": data.DisplayName,
270+
"new_description": data.Description,
271+
"new_icon": data.Icon,
272+
})
273+
_, err = client.UpdateOrganization(ctx, orgID.String(), codersdk.UpdateOrganizationRequest{
274+
Name: data.Name.ValueString(),
275+
DisplayName: data.DisplayName.ValueString(),
276+
Description: data.Description.ValueStringPointer(),
277+
Icon: data.Icon.ValueStringPointer(),
278+
})
279+
if err != nil {
280+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update organization %s, got error: %s", orgID, err))
281+
return
282+
}
283+
tflog.Trace(ctx, "successfully updated organization")
284+
285+
// Save updated data into Terraform state
286+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
287+
}
288+
289+
func (r *OrganizationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
290+
var data OrganizationResourceModel
291+
292+
// Read Terraform prior state data into the model
293+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
294+
295+
if resp.Diagnostics.HasError() {
296+
return
297+
}
298+
299+
client := r.data.Client
300+
orgID := data.ID.ValueUUID()
301+
302+
tflog.Trace(ctx, "deleting organization", map[string]any{
303+
"id": orgID,
304+
})
305+
306+
err := client.DeleteOrganization(ctx, orgID.String())
307+
if err != nil {
308+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete organization %s, got error: %s", orgID, err))
309+
return
310+
}
311+
tflog.Trace(ctx, "successfully deleted organization")
312+
313+
// Read Terraform prior state data into the model
314+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
315+
}
316+
317+
func (r *OrganizationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
318+
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
319+
}

‎internal/provider/provider_test.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// Copyright (c) HashiCorp, Inc.
2-
// SPDX-License-Identifier: MPL-2.0
3-
41
package provider
52

63
import (

‎main.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// Copyright (c) HashiCorp, Inc.
2-
// SPDX-License-Identifier: MPL-2.0
3-
41
package main
52

63
import (
@@ -20,7 +17,7 @@ import (
2017

2118
// Run the docs generation tool, check its repository for more information on how it works and how docs
2219
// can be customized.
23-
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate -provider-name coderd
20+
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
2421

2522
var (
2623
// these will be set by the goreleaser configuration
@@ -38,10 +35,7 @@ func main() {
3835
flag.Parse()
3936

4037
opts := providerserver.ServeOpts{
41-
// TODO: Update this string with the published name of your provider.
42-
// Also update the tfplugindocs generate command to either remove the
43-
// -provider-name flag or set its value to the updated provider name.
44-
Address: "registry.terraform.io/hashicorp/scaffolding",
38+
Address: "registry.terraform.io/coder/coderd",
4539
Debug: debug,
4640
}
4741

‎tools/tools.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// Copyright (c) HashiCorp, Inc.
2-
// SPDX-License-Identifier: MPL-2.0
3-
41
//go:build tools
52

63
package tools

0 commit comments

Comments
 (0)
Please sign in to comment.