From fcf26bff87d47229e20b4dbc9a0219b8fe5e31a7 Mon Sep 17 00:00:00 2001 From: Ethan <39577870+ethanndickson@users.noreply.github.com> Date: Mon, 26 Aug 2024 21:29:33 +1000 Subject: [PATCH 1/5] docs: fix missing required attribute in example (#80) --- docs/resources/template.md | 1 + examples/resources/coderd_template/resource.tf | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/resources/template.md b/docs/resources/template.md index 4300c55..4077d55 100644 --- a/docs/resources/template.md +++ b/docs/resources/template.md @@ -47,6 +47,7 @@ resource "coderd_template" "ubuntu-main" { id = coderd_user.coder1.id role = "admin" }] + groups = [] } } ``` diff --git a/examples/resources/coderd_template/resource.tf b/examples/resources/coderd_template/resource.tf index b8d05bd..3cbc8fe 100644 --- a/examples/resources/coderd_template/resource.tf +++ b/examples/resources/coderd_template/resource.tf @@ -32,5 +32,6 @@ resource "coderd_template" "ubuntu-main" { id = coderd_user.coder1.id role = "admin" }] + groups = [] } } From 5edb8e19490053362113c1b3d77c84373b9ca342 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 22:56:10 +1000 Subject: [PATCH 2/5] chore(deps): bump github.com/coder/coder/v2 from 2.14.1 to 2.14.2 (#81) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/coder/coder/v2](https://github.com/coder/coder) from 2.14.1 to 2.14.2.
Release notes

Sourced from github.com/coder/coder/v2's releases.

v2.14.2

Changelog

[!NOTE] This is a mainline Coder release. We advise enterprise customers without a staging environment to install our latest stable release while we refine this version. Learn more about our Release Schedule.

Changes

Compare: v2.14.1...v2.14.2

Container image

Install/upgrade

Refer to our docs to install or upgrade Coder, or use a release asset below.

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/coder/coder/v2&package-manager=go_modules&previous-version=2.14.1&new-version=2.14.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bb2e38f..0cd0df5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22.5 require ( cdr.dev/slog v1.6.2-0.20240126064726-20367d4aede6 - github.com/coder/coder/v2 v2.14.1 + github.com/coder/coder/v2 v2.14.2 github.com/docker/docker v27.1.1+incompatible github.com/docker/go-connections v0.4.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 0381db4..ca3e824 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/coder/coder/v2 v2.14.1 h1:tSYe7H4pNRL8hh9ynwHK7QYTOvG5hcuZJEOkn4ZPASE= -github.com/coder/coder/v2 v2.14.1/go.mod h1:dO79BI5XlP8rrtne1JpRcVehe27bNMXdZKyn1NsWbjA= +github.com/coder/coder/v2 v2.14.2 h1:RNNDDwjNK5D1XMQlK7LWrS4niVclkl1FXoaOaW7N5rs= +github.com/coder/coder/v2 v2.14.2/go.mod h1:dO79BI5XlP8rrtne1JpRcVehe27bNMXdZKyn1NsWbjA= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc= github.com/coder/serpent v0.7.0 h1:zGpD2GlF3lKIVkMjNGKbkip88qzd5r/TRcc30X/SrT0= From ed773f5b392583f2e111e935b8e59e8bebfc4d7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 18:26:39 +0300 Subject: [PATCH 3/5] chore(deps): bump github.com/docker/docker from 27.1.1+incompatible to 27.1.2+incompatible (#75) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0cd0df5..eebc6ee 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.5 require ( cdr.dev/slog v1.6.2-0.20240126064726-20367d4aede6 github.com/coder/coder/v2 v2.14.2 - github.com/docker/docker v27.1.1+incompatible + github.com/docker/docker v27.1.2+incompatible github.com/docker/go-connections v0.4.0 github.com/google/uuid v1.6.0 github.com/hashicorp/terraform-plugin-docs v0.19.4 diff --git a/go.sum b/go.sum index ca3e824..b7bc394 100644 --- a/go.sum +++ b/go.sum @@ -103,8 +103,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= -github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.1.2+incompatible h1:AhGzR1xaQIy53qCkxARaFluI00WPGtXn0AJuoQsVYTY= +github.com/docker/docker v27.1.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= From 4d366f72aa9d22d857dd2c92814a02b836645e95 Mon Sep 17 00:00:00 2001 From: Ethan <39577870+ethanndickson@users.noreply.github.com> Date: Wed, 28 Aug 2024 14:28:21 +1000 Subject: [PATCH 4/5] docs: add how to log template provisioner output (#82) --- docs/resources/template.md | 7 +++++-- internal/provider/template_resource.go | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/resources/template.md b/docs/resources/template.md index 4077d55..03af184 100644 --- a/docs/resources/template.md +++ b/docs/resources/template.md @@ -3,12 +3,15 @@ page_title: "coderd_template Resource - terraform-provider-coderd" subcategory: "" description: |- - A Coder template + A Coder template. + Logs from building template versions are streamed from the provisioner when the TF_LOG environment variable is INFO or higher. --- # coderd_template (Resource) -A Coder template +A Coder template. + +Logs from building template versions are streamed from the provisioner when the `TF_LOG` environment variable is `INFO` or higher. ## Example Usage diff --git a/internal/provider/template_resource.go b/internal/provider/template_resource.go index 3bdde6d..57c60a9 100644 --- a/internal/provider/template_resource.go +++ b/internal/provider/template_resource.go @@ -229,7 +229,8 @@ func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRe func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "A Coder template", + MarkdownDescription: "A Coder template.\n\nLogs from building template versions are streamed from the provisioner " + + "when the `TF_LOG` environment variable is `INFO` or higher.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ From 7645de8b74b3508ba04b637658a1eb8693843cd8 Mon Sep 17 00:00:00 2001 From: Ethan <39577870+ethanndickson@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:09:26 +1000 Subject: [PATCH 5/5] chore: support importing resources by fully qualified names (#84) Previously, user, group and template resources could only be imported via their UUIDs. The `coder/coder` frontend doesn't expose these UUIDs to the user, so we should provide an alternative, more user-friendly way to import resources. User resources: ID or username, since usernames must be unique, even across multiple orgs. Template resources: ID or `/` Group resources: ID or `` --- docs/resources/group.md | 3 ++ docs/resources/template.md | 3 ++ docs/resources/user.md | 3 ++ internal/provider/group_resource.go | 33 +++++++++++++++++---- internal/provider/group_resource_test.go | 11 +++++-- internal/provider/template_resource.go | 27 +++++++++++++++-- internal/provider/template_resource_test.go | 10 ++++++- internal/provider/user_resource.go | 18 +++++++++-- internal/provider/user_resource_test.go | 11 ++++++- 9 files changed, 106 insertions(+), 13 deletions(-) diff --git a/docs/resources/group.md b/docs/resources/group.md index 5579753..2d265d9 100644 --- a/docs/resources/group.md +++ b/docs/resources/group.md @@ -5,6 +5,7 @@ subcategory: "" description: |- A group on the Coder deployment. Creating groups requires an Enterprise license. + When importing, the ID supplied can be either a group UUID retrieved via the API or /. --- # coderd_group (Resource) @@ -13,6 +14,8 @@ A group on the Coder deployment. Creating groups requires an Enterprise license. +When importing, the ID supplied can be either a group UUID retrieved via the API or `/`. + ## Example Usage ```terraform diff --git a/docs/resources/template.md b/docs/resources/template.md index 03af184..92d9fc7 100644 --- a/docs/resources/template.md +++ b/docs/resources/template.md @@ -5,6 +5,7 @@ subcategory: "" description: |- A Coder template. Logs from building template versions are streamed from the provisioner when the TF_LOG environment variable is INFO or higher. + When importing, the ID supplied can be either a template UUID retrieved via the API or /. --- # coderd_template (Resource) @@ -13,6 +14,8 @@ A Coder template. Logs from building template versions are streamed from the provisioner when the `TF_LOG` environment variable is `INFO` or higher. +When importing, the ID supplied can be either a template UUID retrieved via the API or `/`. + ## Example Usage ```terraform diff --git a/docs/resources/user.md b/docs/resources/user.md index e1c6b68..1671fa6 100644 --- a/docs/resources/user.md +++ b/docs/resources/user.md @@ -4,12 +4,15 @@ page_title: "coderd_user Resource - terraform-provider-coderd" subcategory: "" description: |- A user on the Coder deployment. + When importing, the ID supplied can be either a user UUID or a username. --- # coderd_user (Resource) A user on the Coder deployment. +When importing, the ID supplied can be either a user UUID or a username. + ## Example Usage ```terraform diff --git a/internal/provider/group_resource.go b/internal/provider/group_resource.go index 2599f9f..c7f11bd 100644 --- a/internal/provider/group_resource.go +++ b/internal/provider/group_resource.go @@ -3,6 +3,7 @@ package provider import ( "context" "fmt" + "strings" "github.com/coder/coder/v2/codersdk" "github.com/google/uuid" @@ -60,7 +61,9 @@ func (r *GroupResource) Metadata(ctx context.Context, req resource.MetadataReque func (r *GroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "A group on the Coder deployment.\n\nCreating groups requires an Enterprise license.", + MarkdownDescription: "A group on the Coder deployment.\n\n" + + "Creating groups requires an Enterprise license.\n\n" + + "When importing, the ID supplied can be either a group UUID retrieved via the API or `/`.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -324,10 +327,30 @@ func (r *GroupResource) Delete(ctx context.Context, req resource.DeleteRequest, } func (r *GroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + var groupID uuid.UUID client := r.data.Client - groupID, err := uuid.Parse(req.ID) - if err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse import group ID as UUID, got error: %s", err)) + idParts := strings.Split(req.ID, "/") + if len(idParts) == 1 { + var err error + groupID, err = uuid.Parse(req.ID) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse import group ID as UUID, got error: %s", err)) + return + } + } else if len(idParts) == 2 { + org, err := client.OrganizationByName(ctx, idParts[0]) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to get organization with name %s: %s", idParts[0], err)) + return + } + group, err := client.GroupByOrgAndName(ctx, org.ID, idParts[1]) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to get group with name %s: %s", idParts[1], err)) + return + } + groupID = group.ID + } else { + resp.Diagnostics.AddError("Client Error", "Invalid import ID format, expected a single UUID or `/`") return } group, err := client.Group(ctx, groupID) @@ -339,5 +362,5 @@ func (r *GroupResource) ImportState(ctx context.Context, req resource.ImportStat resp.Diagnostics.AddError("Client Error", "Cannot import groups created via OIDC") return } - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), groupID.String())...) } diff --git a/internal/provider/group_resource_test.go b/internal/provider/group_resource_test.go index fbf751e..159856f 100644 --- a/internal/provider/group_resource_test.go +++ b/internal/provider/group_resource_test.go @@ -78,14 +78,21 @@ func TestAccGroupResource(t *testing.T) { resource.TestCheckResourceAttr("coderd_group.test", "members.0", user1.ID.String()), ), }, - // Import + // Import by ID { - Config: cfg1.String(t), ResourceName: "coderd_group.test", ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"members"}, }, + // Import by org name and group name + { + ResourceName: "coderd_group.test", + ImportState: true, + ImportStateId: "default/example-group", + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"members"}, + }, // Update and Read { Config: cfg2.String(t), diff --git a/internal/provider/template_resource.go b/internal/provider/template_resource.go index 57c60a9..9255f2f 100644 --- a/internal/provider/template_resource.go +++ b/internal/provider/template_resource.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "strings" "cdr.dev/slog" "github.com/coder/coder/v2/codersdk" @@ -230,7 +231,8 @@ func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRe func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ MarkdownDescription: "A Coder template.\n\nLogs from building template versions are streamed from the provisioner " + - "when the `TF_LOG` environment variable is `INFO` or higher.", + "when the `TF_LOG` environment variable is `INFO` or higher.\n\n" + + "When importing, the ID supplied can be either a template UUID retrieved via the API or `/`.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -771,7 +773,28 @@ func (r *TemplateResource) Delete(ctx context.Context, req resource.DeleteReques } func (r *TemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + idParts := strings.Split(req.ID, "/") + if len(idParts) == 1 { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + return + } else if len(idParts) == 2 { + client := r.data.Client + org, err := client.OrganizationByName(ctx, idParts[0]) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to get organization with name %s: %s", idParts[0], err)) + return + } + template, err := client.TemplateByName(ctx, org.ID, idParts[1]) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to get template with name %s: %s", idParts[1], err)) + return + } + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), template.ID.String())...) + return + } else { + resp.Diagnostics.AddError("Client Error", "Invalid import ID format, expected a single UUID or `/`") + return + } } // ConfigValidators implements resource.ResourceWithConfigValidators. diff --git a/internal/provider/template_resource_test.go b/internal/provider/template_resource_test.go index bea6b4b..b8b7f19 100644 --- a/internal/provider/template_resource_test.go +++ b/internal/provider/template_resource_test.go @@ -145,7 +145,7 @@ func TestAccTemplateResource(t *testing.T) { }, Check: testAccCheckNumTemplateVersions(ctx, client, 3), }, - // Import + // Import by ID { Config: cfg1.String(t), ResourceName: "coderd_template.test", @@ -155,6 +155,14 @@ func TestAccTemplateResource(t *testing.T) { // We can't import ACL as we can't currently differentiate between managed and unmanaged ACL ImportStateVerifyIgnore: []string{"versions", "acl"}, }, + // Import by org name and template name + { + ResourceName: "coderd_template.test", + ImportState: true, + ImportStateVerify: true, + ImportStateId: "default/example-template", + ImportStateVerifyIgnore: []string{"versions", "acl"}, + }, // Change existing version directory & name, update template metadata. Creates a fourth version. { Config: cfg2.String(t), diff --git a/internal/provider/user_resource.go b/internal/provider/user_resource.go index 3eee654..4e8de49 100644 --- a/internal/provider/user_resource.go +++ b/internal/provider/user_resource.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -55,7 +56,8 @@ func (r *UserResource) Metadata(ctx context.Context, req resource.MetadataReques func (r *UserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "A user on the Coder deployment.", + MarkdownDescription: "A user on the Coder deployment.\n\n" + + "When importing, the ID supplied can be either a user UUID or a username.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -371,6 +373,18 @@ func (r *UserResource) Delete(ctx context.Context, req resource.DeleteRequest, r tflog.Info(ctx, "successfully deleted user") } +// Req.ID can be either a UUID or a username. func (r *UserResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + _, err := uuid.Parse(req.ID) + if err == nil { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + return + } + client := r.data.Client + user, err := client.User(ctx, req.ID) + if err != nil { + resp.Diagnostics.AddError("Client Error", "Invalid import ID format, expected a single UUID or a valid username") + return + } + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), user.ID.String())...) } diff --git a/internal/provider/user_resource_test.go b/internal/provider/user_resource_test.go index 0c3a233..a7bb470 100644 --- a/internal/provider/user_resource_test.go +++ b/internal/provider/user_resource_test.go @@ -60,7 +60,7 @@ func TestAccUserResource(t *testing.T) { resource.TestCheckResourceAttr("coderd_user.test", "suspended", "false"), ), }, - // ImportState testing + // Import by ID { ResourceName: "coderd_user.test", ImportState: true, @@ -68,6 +68,15 @@ func TestAccUserResource(t *testing.T) { // We can't pull the password from the API. ImportStateVerifyIgnore: []string{"password"}, }, + // ImportState by username + { + ResourceName: "coderd_user.test", + ImportState: true, + ImportStateVerify: true, + ImportStateId: "example", + // We can't pull the password from the API. + ImportStateVerifyIgnore: []string{"password"}, + }, // Update and Read testing { Config: cfg2.String(t),