-
Notifications
You must be signed in to change notification settings - Fork 4
feat: add organization resource #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 10 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
8b73a3e
add organization resource
aslilac 69fab48
Merge branch 'main' into lilac/add-organization-resource
aslilac 4743b9e
flesh out the organization resource
aslilac 435032b
register new resource type + gen
aslilac 71dc51b
start with tests from ethan
aslilac 3397984
ooooh, I get it, that was correct :^)
aslilac 75c0858
hmm
aslilac cc2bb2e
lets do members differently actually
aslilac d23168a
gen
aslilac 28b395a
statecheck
aslilac f2d3e3c
feedback
aslilac 236c11e
hiyo
aslilac 16d10e7
:^)
aslilac f3ff5fb
how about
aslilac 739b20c
Merge branch 'main' into lilac/add-organization-resource
aslilac a716a58
add log fields
aslilac c2e661e
fix import
aslilac 0a2f330
?
aslilac 435cb20
sure
aslilac File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,12 @@ | ||
| default: testacc | ||
|
|
||
| fmt: | ||
| go fmt ./... | ||
| terraform fmt -recursive | ||
|
|
||
| vet: | ||
| go vet ./... | ||
|
|
||
| gen: | ||
| go generate ./... | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| --- | ||
| # generated by https://github.com/hashicorp/terraform-plugin-docs | ||
| page_title: "coderd_organization Resource - terraform-provider-coderd" | ||
| subcategory: "" | ||
| description: |- | ||
| An organization on the Coder deployment | ||
| --- | ||
|
|
||
| # coderd_organization (Resource) | ||
|
|
||
| An organization on the Coder deployment | ||
|
|
||
|
|
||
|
|
||
| <!-- schema generated by tfplugindocs --> | ||
| ## Schema | ||
|
|
||
| ### Required | ||
|
|
||
| - `name` (String) Username of the organization. | ||
|
|
||
| ### Optional | ||
|
|
||
| - `description` (String) | ||
| - `display_name` (String) Display name of the organization. Defaults to name. | ||
| - `icon` (String) | ||
|
|
||
| ### Read-Only | ||
|
|
||
| - `id` (String) Organization ID |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,229 @@ | ||
| package provider | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
|
|
||
| "github.com/coder/coder/v2/codersdk" | ||
| "github.com/coder/terraform-provider-coderd/internal/codersdkvalidator" | ||
| "github.com/hashicorp/terraform-plugin-framework/path" | ||
| "github.com/hashicorp/terraform-plugin-framework/resource" | ||
| "github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
| "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" | ||
| "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" | ||
| "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" | ||
| "github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
| "github.com/hashicorp/terraform-plugin-framework/types" | ||
| "github.com/hashicorp/terraform-plugin-log/tflog" | ||
| ) | ||
|
|
||
| // Ensure provider defined types fully satisfy framework interfaces. | ||
| var _ resource.Resource = &OrganizationResource{} | ||
| var _ resource.ResourceWithImportState = &OrganizationResource{} | ||
|
|
||
| type OrganizationResource struct { | ||
| *CoderdProviderData | ||
| } | ||
|
|
||
| // OrganizationResourceModel describes the resource data model. | ||
| type OrganizationResourceModel struct { | ||
| ID UUID `tfsdk:"id"` | ||
|
|
||
| Name types.String `tfsdk:"name"` | ||
| DisplayName types.String `tfsdk:"display_name"` | ||
| Description types.String `tfsdk:"description"` | ||
| Icon types.String `tfsdk:"icon"` | ||
| } | ||
|
|
||
| func NewOrganizationResource() resource.Resource { | ||
| return &OrganizationResource{} | ||
| } | ||
|
|
||
| func (r *OrganizationResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { | ||
| resp.TypeName = req.ProviderTypeName + "_organization" | ||
| } | ||
|
|
||
| func (r *OrganizationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { | ||
| resp.Schema = schema.Schema{ | ||
| MarkdownDescription: "An organization on the Coder deployment", | ||
|
|
||
| Attributes: map[string]schema.Attribute{ | ||
| "id": schema.StringAttribute{ | ||
| CustomType: UUIDType, | ||
| Computed: true, | ||
| MarkdownDescription: "Organization ID", | ||
| PlanModifiers: []planmodifier.String{ | ||
| stringplanmodifier.UseStateForUnknown(), | ||
| }, | ||
| }, | ||
| "name": schema.StringAttribute{ | ||
| MarkdownDescription: "Username of the organization.", | ||
aslilac marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Required: true, | ||
| Validators: []validator.String{ | ||
| codersdkvalidator.Name(), | ||
| }, | ||
| }, | ||
| "display_name": schema.StringAttribute{ | ||
| MarkdownDescription: "Display name of the organization. Defaults to name.", | ||
| Computed: true, | ||
| Optional: true, | ||
| Validators: []validator.String{ | ||
| codersdkvalidator.DisplayName(), | ||
| }, | ||
aslilac marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| "description": schema.StringAttribute{ | ||
| Optional: true, | ||
| Computed: true, | ||
| Default: stringdefault.StaticString(""), | ||
| }, | ||
| "icon": schema.StringAttribute{ | ||
| Optional: true, | ||
| Computed: true, | ||
| Default: stringdefault.StaticString(""), | ||
| }, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func (r *OrganizationResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { | ||
| // Prevent panic if the provider has not been configured. | ||
| if req.ProviderData == nil { | ||
| return | ||
| } | ||
|
|
||
| data, ok := req.ProviderData.(*CoderdProviderData) | ||
|
|
||
| if !ok { | ||
| resp.Diagnostics.AddError( | ||
| "Unable to configure provider data", | ||
| fmt.Sprintf("Expected *CoderdProviderData, got: %T. Please report this issue to the provider developers.", req.ProviderData), | ||
| ) | ||
|
|
||
| return | ||
| } | ||
|
|
||
| r.CoderdProviderData = data | ||
| } | ||
|
|
||
| func (r *OrganizationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { | ||
| // Read Terraform prior state data into the model | ||
| var data OrganizationResourceModel | ||
| resp.Diagnostics.Append(req.State.Get(ctx, &data)...) | ||
| if resp.Diagnostics.HasError() { | ||
| return | ||
| } | ||
|
|
||
| orgID := data.ID.ValueUUID() | ||
| org, err := r.Client.Organization(ctx, orgID) | ||
| if err != nil { | ||
| resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get organization by ID, got error: %s", err)) | ||
| return | ||
| } | ||
|
|
||
| // We've fetched the organization ID from state, and the latest values for | ||
| // everything else from the backend. Ensure that any mutable data is synced | ||
| // with the backend. | ||
| data.Name = types.StringValue(org.Name) | ||
| data.DisplayName = types.StringValue(org.DisplayName) | ||
| data.Description = types.StringValue(org.Description) | ||
| data.Icon = types.StringValue(org.Icon) | ||
|
|
||
| // Save updated data into Terraform state | ||
| resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
| } | ||
|
|
||
| func (r *OrganizationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { | ||
| // Read Terraform plan data into the model | ||
| var data OrganizationResourceModel | ||
| resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) | ||
| if resp.Diagnostics.HasError() { | ||
| return | ||
| } | ||
|
|
||
| tflog.Trace(ctx, "creating organization") | ||
aslilac marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| org, err := r.Client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{ | ||
| Name: data.Name.ValueString(), | ||
| DisplayName: data.DisplayName.ValueString(), | ||
| Description: data.Description.ValueString(), | ||
| Icon: data.Icon.ValueString(), | ||
| }) | ||
| if err != nil { | ||
| resp.Diagnostics.AddError("Failed to create organization", err.Error()) | ||
| return | ||
| } | ||
| tflog.Trace(ctx, "successfully created organization", map[string]any{ | ||
| "id": org.ID, | ||
aslilac marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }) | ||
| // Fill in `ID` since it must be "computed". | ||
| data.ID = UUIDValue(org.ID) | ||
| // We also fill in `DisplayName`, since it's optional but the backend will | ||
| // default it. | ||
| data.DisplayName = types.StringValue(org.DisplayName) | ||
|
|
||
| // Save data into Terraform state | ||
| resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
| } | ||
|
|
||
| func (r *OrganizationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
| // Read Terraform plan data into the model | ||
| var data OrganizationResourceModel | ||
| resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) | ||
| if resp.Diagnostics.HasError() { | ||
| return | ||
| } | ||
|
|
||
| orgID := data.ID.ValueUUID() | ||
|
|
||
| // Update the organization metadata | ||
| tflog.Trace(ctx, "updating organization", map[string]any{ | ||
| "id": orgID, | ||
| "new_name": data.Name, | ||
| "new_display_name": data.DisplayName, | ||
| "new_description": data.Description, | ||
| "new_icon": data.Icon, | ||
| }) | ||
| _, err := r.Client.UpdateOrganization(ctx, orgID.String(), codersdk.UpdateOrganizationRequest{ | ||
| Name: data.Name.ValueString(), | ||
| DisplayName: data.DisplayName.ValueString(), | ||
| Description: data.Description.ValueStringPointer(), | ||
| Icon: data.Icon.ValueStringPointer(), | ||
| }) | ||
| if err != nil { | ||
| resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update organization %s, got error: %s", orgID, err)) | ||
| return | ||
| } | ||
| tflog.Trace(ctx, "successfully updated organization") | ||
|
|
||
| // Save updated data into Terraform state | ||
| resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
| } | ||
|
|
||
| func (r *OrganizationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { | ||
| // Read Terraform prior state data into the model | ||
| var data OrganizationResourceModel | ||
| resp.Diagnostics.Append(req.State.Get(ctx, &data)...) | ||
| if resp.Diagnostics.HasError() { | ||
| return | ||
| } | ||
|
|
||
| orgID := data.ID.ValueUUID() | ||
|
|
||
| tflog.Trace(ctx, "deleting organization", map[string]any{ | ||
| "id": orgID, | ||
aslilac marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }) | ||
| err := r.Client.DeleteOrganization(ctx, orgID.String()) | ||
| if err != nil { | ||
| resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete organization %s, got error: %s", orgID, err)) | ||
| return | ||
| } | ||
| tflog.Trace(ctx, "successfully deleted organization") | ||
|
|
||
| // Read Terraform prior state data into the model | ||
| resp.Diagnostics.Append(req.State.Get(ctx, &data)...) | ||
| } | ||
|
|
||
| func (r *OrganizationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { | ||
| // Terraform will eventually `Read` in the rest of the fields after we have | ||
| // set the `id` attribute. | ||
| resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) | ||
aslilac marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.