From 992610e737c74f392e1e320529d978415ab89630 Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Mon, 11 Mar 2024 10:14:31 -0400 Subject: [PATCH] Cloud: Make full use of ID + import generation framework Split from https://github.com/grafana/terraform-provider-grafana/pull/1391 Convert the `stack` and `stack_service_account` resources to the new ID system that validates the ID format --- docs/resources/cloud_stack.md | 3 +- docs/resources/cloud_stack_service_account.md | 8 +++ .../resources/grafana_cloud_stack/import.sh | 3 +- .../import.sh | 1 + .../resources/cloud/resource_cloud_stack.go | 32 ++++++++--- .../resource_cloud_stack_service_account.go | 53 +++++++++++-------- ...ource_cloud_stack_service_account_token.go | 9 ++-- 7 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 examples/resources/grafana_cloud_stack_service_account/import.sh diff --git a/docs/resources/cloud_stack.md b/docs/resources/cloud_stack.md index 6a2b414bb..49e092439 100644 --- a/docs/resources/cloud_stack.md +++ b/docs/resources/cloud_stack.md @@ -86,6 +86,5 @@ resource "grafana_cloud_stack" "test" { Import is supported using the following syntax: ```shell -terraform import grafana_cloud_stack.stack_name {{stack_id}} // import by numerical ID -terraform import grafana_cloud_stack.stack_name {{stack_slug}} // or import by slug +terraform import grafana_cloud_stack.name "{{ stackSlugOrID }}" ``` diff --git a/docs/resources/cloud_stack_service_account.md b/docs/resources/cloud_stack_service_account.md index a91a04661..a2f4b455a 100644 --- a/docs/resources/cloud_stack_service_account.md +++ b/docs/resources/cloud_stack_service_account.md @@ -51,3 +51,11 @@ resource "grafana_cloud_stack_service_account" "cloud_sa" { ### Read-Only - `id` (String) The ID of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import grafana_cloud_stack_service_account.name "{{ stackSlug }}:{{ serviceAccountID }}" +``` diff --git a/examples/resources/grafana_cloud_stack/import.sh b/examples/resources/grafana_cloud_stack/import.sh index f5f7c55a1..9bcb10685 100644 --- a/examples/resources/grafana_cloud_stack/import.sh +++ b/examples/resources/grafana_cloud_stack/import.sh @@ -1,2 +1 @@ -terraform import grafana_cloud_stack.stack_name {{stack_id}} // import by numerical ID -terraform import grafana_cloud_stack.stack_name {{stack_slug}} // or import by slug +terraform import grafana_cloud_stack.name "{{ stackSlugOrID }}" diff --git a/examples/resources/grafana_cloud_stack_service_account/import.sh b/examples/resources/grafana_cloud_stack_service_account/import.sh new file mode 100644 index 000000000..3e23832c8 --- /dev/null +++ b/examples/resources/grafana_cloud_stack_service_account/import.sh @@ -0,0 +1 @@ +terraform import grafana_cloud_stack_service_account.name "{{ stackSlug }}:{{ serviceAccountID }}" diff --git a/internal/resources/cloud/resource_cloud_stack.go b/internal/resources/cloud/resource_cloud_stack.go index c6e2e8167..798fdddb1 100644 --- a/internal/resources/cloud/resource_cloud_stack.go +++ b/internal/resources/cloud/resource_cloud_stack.go @@ -26,6 +26,7 @@ const defaultReadinessTimeout = time.Minute * 5 var ( stackLabelRegex = regexp.MustCompile(`^[a-zA-Z0-9/\-.]+$`) stackSlugRegex = regexp.MustCompile(`^[a-z][a-z0-9]+$`) + resourceStackID = common.NewResourceID("grafana_cloud_stack", common.StringIDField("stackSlugOrID")) ) func resourceStack() *schema.Resource { @@ -248,6 +249,11 @@ func createStack(ctx context.Context, d *schema.ResourceData, client *gcom.APICl } func updateStack(ctx context.Context, d *schema.ResourceData, client *gcom.APIClient) diag.Diagnostics { + id, err := resourceStackID.Single(d.Id()) + if err != nil { + return diag.FromErr(err) + } + // Default to the slug if the URL is not set url := d.Get("url").(string) if url == "" { @@ -261,8 +267,8 @@ func updateStack(ctx context.Context, d *schema.ResourceData, client *gcom.APICl Url: &url, Labels: common.Ref(common.UnpackMap[string](d.Get("labels"))), } - req := client.InstancesAPI.PostInstance(ctx, d.Id()).PostInstanceRequest(stack).XRequestId(ClientRequestID()) - _, _, err := req.Execute() + req := client.InstancesAPI.PostInstance(ctx, id.(string)).PostInstanceRequest(stack).XRequestId(ClientRequestID()) + _, _, err = req.Execute() if err != nil { return apiError(err) } @@ -275,13 +281,23 @@ func updateStack(ctx context.Context, d *schema.ResourceData, client *gcom.APICl } func deleteStack(ctx context.Context, d *schema.ResourceData, client *gcom.APIClient) diag.Diagnostics { - req := client.InstancesAPI.DeleteInstance(ctx, d.Id()).XRequestId(ClientRequestID()) - _, _, err := req.Execute() + id, err := resourceStackID.Single(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + req := client.InstancesAPI.DeleteInstance(ctx, id.(string)).XRequestId(ClientRequestID()) + _, _, err = req.Execute() return apiError(err) } func readStack(ctx context.Context, d *schema.ResourceData, client *gcom.APIClient) diag.Diagnostics { - req := client.InstancesAPI.GetInstance(ctx, d.Id()) + id, err := resourceStackID.Single(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + req := client.InstancesAPI.GetInstance(ctx, id.(string)) stack, _, err := req.Execute() if err, shouldReturn := common.CheckReadError("stack", d, err); shouldReturn { return err @@ -293,13 +309,13 @@ func readStack(ctx context.Context, d *schema.ResourceData, client *gcom.APIClie return nil } - connectionsReq := client.InstancesAPI.GetConnections(ctx, d.Id()) + connectionsReq := client.InstancesAPI.GetConnections(ctx, id.(string)) connections, _, err := connectionsReq.Execute() if err != nil { return apiError(err) } - if err := FlattenStack(d, stack, connections); err != nil { + if err := flattenStack(d, stack, connections); err != nil { return diag.FromErr(err) } // Always set the wait attribute to true after creation @@ -312,7 +328,7 @@ func readStack(ctx context.Context, d *schema.ResourceData, client *gcom.APIClie return nil } -func FlattenStack(d *schema.ResourceData, stack *gcom.FormattedApiInstance, connections *gcom.FormattedApiInstanceConnections) error { +func flattenStack(d *schema.ResourceData, stack *gcom.FormattedApiInstance, connections *gcom.FormattedApiInstanceConnections) error { id := strconv.FormatInt(int64(stack.Id), 10) d.SetId(id) d.Set("name", stack.Name) diff --git a/internal/resources/cloud/resource_cloud_stack_service_account.go b/internal/resources/cloud/resource_cloud_stack_service_account.go index bef42532f..5442dd3a2 100644 --- a/internal/resources/cloud/resource_cloud_stack_service_account.go +++ b/internal/resources/cloud/resource_cloud_stack_service_account.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net/url" - "strconv" "time" "github.com/grafana/grafana-com-public-clients/go/gcom" @@ -17,6 +16,14 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) +var ( + resourceStackServiceAccountID = common.NewResourceID( + "grafana_cloud_stack_service_account", + common.StringIDField("stackSlug"), + common.IntIDField("serviceAccountID"), + ) +) + func resourceStackServiceAccount() *schema.Resource { return &schema.Resource{ @@ -70,7 +77,8 @@ Required access policy scopes: } func createStackServiceAccount(ctx context.Context, d *schema.ResourceData, cloudClient *gcom.APIClient) diag.Diagnostics { - client, cleanup, err := CreateTemporaryStackGrafanaClient(ctx, cloudClient, d.Get("stack_slug").(string), "terraform-temp-") + stackSlug := d.Get("stack_slug").(string) + client, cleanup, err := CreateTemporaryStackGrafanaClient(ctx, cloudClient, stackSlug, "terraform-temp-") if err != nil { return diag.FromErr(err) } @@ -87,27 +95,28 @@ func createStackServiceAccount(ctx context.Context, d *schema.ResourceData, clou } sa := resp.Payload - d.SetId(strconv.FormatInt(sa.ID, 10)) - return readStackServiceAccountWithClient(client, d) + d.SetId(resourceStackServiceAccountID.Make(stackSlug, sa.ID)) + return readStackServiceAccountWithClient(client, d, sa.ID) } func readStackServiceAccount(ctx context.Context, d *schema.ResourceData, cloudClient *gcom.APIClient) diag.Diagnostics { - client, cleanup, err := CreateTemporaryStackGrafanaClient(ctx, cloudClient, d.Get("stack_slug").(string), "terraform-temp-") + split, err := resourceStackServiceAccountID.Split(d.Id()) if err != nil { return diag.FromErr(err) } - defer cleanup() + stackSlug, serviceAccountID := split[0].(string), split[1].(int64) - return readStackServiceAccountWithClient(client, d) -} - -func readStackServiceAccountWithClient(client *goapi.GrafanaHTTPAPI, d *schema.ResourceData) diag.Diagnostics { - id, err := strconv.ParseInt(d.Id(), 10, 64) + client, cleanup, err := CreateTemporaryStackGrafanaClient(ctx, cloudClient, stackSlug, "terraform-temp-") if err != nil { return diag.FromErr(err) } + defer cleanup() - resp, err := client.ServiceAccounts.RetrieveServiceAccount(id) + return readStackServiceAccountWithClient(client, d, serviceAccountID) +} + +func readStackServiceAccountWithClient(client *goapi.GrafanaHTTPAPI, d *schema.ResourceData, serviceAccountID int64) diag.Diagnostics { + resp, err := client.ServiceAccounts.RetrieveServiceAccount(serviceAccountID) if err != nil { return diag.FromErr(err) } @@ -130,16 +139,17 @@ func readStackServiceAccountWithClient(client *goapi.GrafanaHTTPAPI, d *schema.R } func updateStackServiceAccount(ctx context.Context, d *schema.ResourceData, cloudClient *gcom.APIClient) diag.Diagnostics { - client, cleanup, err := CreateTemporaryStackGrafanaClient(ctx, cloudClient, d.Get("stack_slug").(string), "terraform-temp-") + split, err := resourceStackServiceAccountID.Split(d.Id()) if err != nil { return diag.FromErr(err) } - defer cleanup() + stackSlug, serviceAccountID := split[0].(string), split[1].(int64) - id, err := strconv.ParseInt(d.Id(), 10, 64) + client, cleanup, err := CreateTemporaryStackGrafanaClient(ctx, cloudClient, stackSlug, "terraform-temp-") if err != nil { return diag.FromErr(err) } + defer cleanup() updateRequest := service_accounts.NewUpdateServiceAccountParams(). WithBody(&models.UpdateServiceAccountForm{ @@ -147,28 +157,29 @@ func updateStackServiceAccount(ctx context.Context, d *schema.ResourceData, clou Role: d.Get("role").(string), IsDisabled: d.Get("is_disabled").(bool), }). - WithServiceAccountID(id) + WithServiceAccountID(serviceAccountID) if _, err := client.ServiceAccounts.UpdateServiceAccount(updateRequest); err != nil { return diag.FromErr(err) } - return readStackServiceAccountWithClient(client, d) + return readStackServiceAccountWithClient(client, d, serviceAccountID) } func deleteStackServiceAccount(ctx context.Context, d *schema.ResourceData, cloudClient *gcom.APIClient) diag.Diagnostics { - client, cleanup, err := CreateTemporaryStackGrafanaClient(ctx, cloudClient, d.Get("stack_slug").(string), "terraform-temp-") + split, err := resourceStackServiceAccountID.Split(d.Id()) if err != nil { return diag.FromErr(err) } - defer cleanup() + stackSlug, serviceAccountID := split[0].(string), split[1].(int64) - id, err := strconv.ParseInt(d.Id(), 10, 64) + client, cleanup, err := CreateTemporaryStackGrafanaClient(ctx, cloudClient, stackSlug, "terraform-temp-") if err != nil { return diag.FromErr(err) } + defer cleanup() - _, err = client.ServiceAccounts.DeleteServiceAccount(id) + _, err = client.ServiceAccounts.DeleteServiceAccount(serviceAccountID) return diag.FromErr(err) } diff --git a/internal/resources/cloud/resource_cloud_stack_service_account_token.go b/internal/resources/cloud/resource_cloud_stack_service_account_token.go index f713826ac..67a82cfd8 100644 --- a/internal/resources/cloud/resource_cloud_stack_service_account_token.go +++ b/internal/resources/cloud/resource_cloud_stack_service_account_token.go @@ -76,10 +76,11 @@ func stackServiceAccountTokenCreate(ctx context.Context, d *schema.ResourceData, } defer cleanup() - serviceAccountID, err := strconv.ParseInt(d.Get("service_account_id").(string), 10, 64) + split, err := resourceStackServiceAccountID.Split(d.Get("service_account_id").(string)) if err != nil { return diag.FromErr(err) } + serviceAccountID := split[1].(int64) name := d.Get("name").(string) ttl := d.Get("seconds_to_live").(int) @@ -115,10 +116,11 @@ func stackServiceAccountTokenRead(ctx context.Context, d *schema.ResourceData, c } func stackServiceAccountTokenReadWithClient(c *goapi.GrafanaHTTPAPI, d *schema.ResourceData) diag.Diagnostics { - serviceAccountID, err := strconv.ParseInt(d.Get("service_account_id").(string), 10, 64) + split, err := resourceStackServiceAccountID.Split(d.Get("service_account_id").(string)) if err != nil { return diag.FromErr(err) } + serviceAccountID := split[1].(int64) response, err := c.ServiceAccounts.ListTokens(serviceAccountID) if err != nil { @@ -161,10 +163,11 @@ func stackServiceAccountTokenDelete(ctx context.Context, d *schema.ResourceData, } defer cleanup() - serviceAccountID, err := strconv.ParseInt(d.Get("service_account_id").(string), 10, 64) + split, err := resourceStackServiceAccountID.Split(d.Get("service_account_id").(string)) if err != nil { return diag.FromErr(err) } + serviceAccountID := split[1].(int64) id, err := strconv.ParseInt(d.Id(), 10, 32) if err != nil {