From 4f1931e16450bec5e554a50ea0c19c04ea711569 Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Sun, 5 Jan 2025 09:25:40 -0600 Subject: [PATCH 01/10] Nexus Endpoint resource --- docs/resources/nexus_endpoint.md | 126 ++++++ .../temporalcloud_nexus_endpoint/import.sh | 6 + .../temporalcloud_nexus_endpoint/resource.tf | 62 +++ internal/provider/nexus_endpoint_resource.go | 402 ++++++++++++++++++ internal/provider/provider.go | 1 + 5 files changed, 597 insertions(+) create mode 100644 docs/resources/nexus_endpoint.md create mode 100644 examples/resources/temporalcloud_nexus_endpoint/import.sh create mode 100644 examples/resources/temporalcloud_nexus_endpoint/resource.tf create mode 100644 internal/provider/nexus_endpoint_resource.go diff --git a/docs/resources/nexus_endpoint.md b/docs/resources/nexus_endpoint.md new file mode 100644 index 0000000..e1bea0c --- /dev/null +++ b/docs/resources/nexus_endpoint.md @@ -0,0 +1,126 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "temporalcloud_nexus_endpoint Resource - terraform-provider-temporalcloud" +subcategory: "" +description: |- + Provisions a Temporal Cloud Nexus endpoint. +--- + +# temporalcloud_nexus_endpoint (Resource) + +Provisions a Temporal Cloud Nexus endpoint. + +## Example Usage + +```terraform +terraform { + required_providers { + temporalcloud = { + source = "temporalio/temporalcloud" + } + } +} + +resource "temporalcloud_namespace" "target_namespace" { + name = "terraform-target-namespace" + regions = ["aws-us-west-2"] + api_key_auth = true + retention_days = 14 + timeouts { + create = "10m" + delete = "10m" + } +} + +resource "temporalcloud_namespace" "caller_namespace" { + name = "terraform-caller-namespace" + regions = ["aws-us-east-1"] + api_key_auth = true + retention_days = 14 + timeouts { + create = "10m" + delete = "10m" + } +} + +resource "temporalcloud_namespace" "caller_namespace_2" { + name = "terraform-caller-namespace-2" + regions = ["gcp-us-central1"] + api_key_auth = true + retention_days = 14 + timeouts { + create = "10m" + delete = "10m" + } +} + +resource "temporalcloud_nexus_endpoint" "nexus_endpoint" { + name = "terraform-nexus-endpoint" + description = <<-EOT + Service Name: + my-hello-service + Operation Names: + echo + say-hello + + Input / Output arguments are in the following repository: + https://github.com/temporalio/samples-go/blob/main/nexus/service/api.go + EOT + worker_target_spec = { + namespace_id = temporalcloud_namespace.target_namespace.id + task_queue = "terraform-task-queue" + } + allowed_caller_namespaces = [ + temporalcloud_namespace.caller_namespace.id, + temporalcloud_namespace.caller_namespace_2.id, + ] +} +``` + + +## Schema + +### Required + +- `allowed_caller_namespaces` (Set of String) Namespace(s) that are allowed to call this Endpoint. +- `name` (String) The name of the endpoint. Must be unique within an account and match `^[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9]$` +- `worker_target_spec` (Attributes) A target spec for routing nexus requests to a specific cloud namespace worker. (see [below for nested schema](#nestedatt--worker_target_spec)) + +### Optional + +- `description` (String, Sensitive) The description for the Nexus endpoint. +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `id` (String) The unique identifier of the Nexus endpoint. + + +### Nested Schema for `worker_target_spec` + +Required: + +- `namespace_id` (String) The target cloud namespace to route requests to. Namespace must be in same account as the endpoint. +- `task_queue` (String) The task queue on the cloud namespace to route requests to. + + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). +- `delete` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Setting a timeout for a Delete operation is only applicable if changes are saved into state before the destroy operation occurs. + +## Import + +Import is supported using the following syntax: + +```shell +# Nexus Endpoints can be imported to incorporate existing Nexus Endpoints into your Terraform pipeline. +# To import a Nexus Endpoint, you need +# - a resource configuration in your Terraform configuration file/module to accept the imported Nexus Endpoint. In the example below, the placeholder is "temporalcloud_nexus_endpoint" "nexus_endpoint" +# - the Nexus Endpoint's ID, which is found using the Temporal Cloud CLI tcld nexus endpoint list. In the example below, this is 405f7da4224a43d99c211904ed9b3819 + +terraform import temporalcloud_nexus_endpoint.nexus_endpoint 405f7da4224a43d99c211904ed9b3819 +``` diff --git a/examples/resources/temporalcloud_nexus_endpoint/import.sh b/examples/resources/temporalcloud_nexus_endpoint/import.sh new file mode 100644 index 0000000..1b2b73c --- /dev/null +++ b/examples/resources/temporalcloud_nexus_endpoint/import.sh @@ -0,0 +1,6 @@ +# Nexus Endpoints can be imported to incorporate existing Nexus Endpoints into your Terraform pipeline. +# To import a Nexus Endpoint, you need +# - a resource configuration in your Terraform configuration file/module to accept the imported Nexus Endpoint. In the example below, the placeholder is "temporalcloud_nexus_endpoint" "nexus_endpoint" +# - the Nexus Endpoint's ID, which is found using the Temporal Cloud CLI tcld nexus endpoint list. In the example below, this is 405f7da4224a43d99c211904ed9b3819 + +terraform import temporalcloud_nexus_endpoint.nexus_endpoint 405f7da4224a43d99c211904ed9b3819 \ No newline at end of file diff --git a/examples/resources/temporalcloud_nexus_endpoint/resource.tf b/examples/resources/temporalcloud_nexus_endpoint/resource.tf new file mode 100644 index 0000000..fcfb73a --- /dev/null +++ b/examples/resources/temporalcloud_nexus_endpoint/resource.tf @@ -0,0 +1,62 @@ +terraform { + required_providers { + temporalcloud = { + source = "temporalio/temporalcloud" + } + } +} + +resource "temporalcloud_namespace" "target_namespace" { + name = "terraform-target-namespace" + regions = ["aws-us-west-2"] + api_key_auth = true + retention_days = 14 + timeouts { + create = "10m" + delete = "10m" + } +} + +resource "temporalcloud_namespace" "caller_namespace" { + name = "terraform-caller-namespace" + regions = ["aws-us-east-1"] + api_key_auth = true + retention_days = 14 + timeouts { + create = "10m" + delete = "10m" + } +} + +resource "temporalcloud_namespace" "caller_namespace_2" { + name = "terraform-caller-namespace-2" + regions = ["gcp-us-central1"] + api_key_auth = true + retention_days = 14 + timeouts { + create = "10m" + delete = "10m" + } +} + +resource "temporalcloud_nexus_endpoint" "nexus_endpoint" { + name = "terraform-nexus-endpoint" + description = <<-EOT + Service Name: + my-hello-service + Operation Names: + echo + say-hello + + Input / Output arguments are in the following repository: + https://github.com/temporalio/samples-go/blob/main/nexus/service/api.go + EOT + worker_target_spec = { + namespace_id = temporalcloud_namespace.target_namespace.id + task_queue = "terraform-task-queue" + } + allowed_caller_namespaces = [ + temporalcloud_namespace.caller_namespace.id, + temporalcloud_namespace.caller_namespace_2.id, + ] +} diff --git a/internal/provider/nexus_endpoint_resource.go b/internal/provider/nexus_endpoint_resource.go new file mode 100644 index 0000000..a264075 --- /dev/null +++ b/internal/provider/nexus_endpoint_resource.go @@ -0,0 +1,402 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "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/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/temporalio/terraform-provider-temporalcloud/internal/client" + cloudservicev1 "go.temporal.io/api/cloud/cloudservice/v1" + nexusv1 "go.temporal.io/api/cloud/nexus/v1" +) + +type ( + nexusEndpointResource struct { + client *client.Client + } + + nexusEndpointResourceModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + WorkerTargetSpec types.Object `tfsdk:"worker_target_spec"` + AllowedCallerNamespaces types.Set `tfsdk:"allowed_caller_namespaces"` + + Timeouts timeouts.Value `tfsdk:"timeouts"` + } + + nexusEndpointWorkerTargetModel struct { + NamespaceID types.String `tfsdk:"namespace_id"` + TaskQueue types.String `tfsdk:"task_queue"` + } +) + +var ( + _ resource.Resource = (*nexusEndpointResource)(nil) + _ resource.ResourceWithConfigure = (*nexusEndpointResource)(nil) + _ resource.ResourceWithImportState = (*nexusEndpointResource)(nil) + + workerTargetSpecAttrs = map[string]attr.Type{ + "namespace_id": types.StringType, + "task_queue": types.StringType, + } +) + +func NewNexusEndpointResource() resource.Resource { + return &nexusEndpointResource{} +} + +func (r *nexusEndpointResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = client +} + +func (r *nexusEndpointResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nexus_endpoint" +} + +func (r *nexusEndpointResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Provisions a Temporal Cloud Nexus endpoint.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "The unique identifier of the Nexus endpoint.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + Description: "The name of the endpoint. Must be unique within an account and match `^[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9]$`", + Required: true, + }, + "description": schema.StringAttribute{ + Description: "The description for the Nexus endpoint.", + Optional: true, + Sensitive: true, + }, + "worker_target_spec": schema.SingleNestedAttribute{ + Description: "A target spec for routing nexus requests to a specific cloud namespace worker.", + Attributes: map[string]schema.Attribute{ + "namespace_id": schema.StringAttribute{ + Description: "The target cloud namespace to route requests to. Namespace must be in same account as the endpoint.", + Required: true, + }, + "task_queue": schema.StringAttribute{ + Description: "The task queue on the cloud namespace to route requests to.", + Required: true, + }, + }, + Required: true, + }, + "allowed_caller_namespaces": schema.SetAttribute{ + Description: "Namespace(s) that are allowed to call this Endpoint.", + ElementType: types.StringType, + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "timeouts": timeouts.Block(ctx, timeouts.Opts{ + Create: true, + Delete: true, + }), + }, + } +} + +func (r *nexusEndpointResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan nexusEndpointResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + createTimeout, diags := plan.Timeouts.Create(ctx, defaultCreateTimeout) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + ctx, cancel := context.WithTimeout(ctx, createTimeout) + defer cancel() + + description := "" + if !plan.Description.IsNull() { + description = plan.Description.ValueString() + } + + targetSpec, diags := getTargetSpecFromModel(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + policySpecs, diags := getPolicySpecsFromModel(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + svcResp, err := r.client.CloudService().CreateNexusEndpoint(ctx, &cloudservicev1.CreateNexusEndpointRequest{ + Spec: &nexusv1.EndpointSpec{ + Name: plan.Name.ValueString(), + Description: description, + TargetSpec: targetSpec, + PolicySpecs: policySpecs, + }, + }) + + if err != nil { + resp.Diagnostics.AddError("Failed to create Nexus endpoint", err.Error()) + return + } + if err := client.AwaitAsyncOperation(ctx, r.client, svcResp.AsyncOperation); err != nil { + resp.Diagnostics.AddError("Failed to create Nexus endpoint", err.Error()) + return + } + + nexusEndpoint, err := r.client.CloudService().GetNexusEndpoint(ctx, &cloudservicev1.GetNexusEndpointRequest{ + EndpointId: svcResp.GetEndpointId(), + }) + if err != nil { + resp.Diagnostics.AddError("Failed to get Nexus endpoint after creation", err.Error()) + return + } + + resp.Diagnostics.Append(updateNexusEndpointModelFromSpec(ctx, &plan, nexusEndpoint.Endpoint)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *nexusEndpointResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state nexusEndpointResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + nexusEndpoint, err := r.client.CloudService().GetNexusEndpoint(ctx, &cloudservicev1.GetNexusEndpointRequest{ + EndpointId: state.ID.ValueString(), + }) + if err != nil { + resp.Diagnostics.AddError("Failed to get Nexus endpoint", err.Error()) + return + } + + resp.Diagnostics.Append(updateNexusEndpointModelFromSpec(ctx, &state, nexusEndpoint.Endpoint)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *nexusEndpointResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan nexusEndpointResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + nexusEndpoint, err := r.client.CloudService().GetNexusEndpoint(ctx, &cloudservicev1.GetNexusEndpointRequest{ + EndpointId: plan.ID.ValueString(), + }) + if err != nil { + resp.Diagnostics.AddError("Failed to get current Nexus endpoint status", err.Error()) + return + } + + description := "" + if !plan.Description.IsNull() { + description = plan.Description.ValueString() + } + + targetSpec, diags := getTargetSpecFromModel(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + policySpecs, diags := getPolicySpecsFromModel(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + svcResp, err := r.client.CloudService().UpdateNexusEndpoint(ctx, &cloudservicev1.UpdateNexusEndpointRequest{ + EndpointId: plan.ID.ValueString(), + Spec: &nexusv1.EndpointSpec{ + Name: plan.Name.ValueString(), + Description: description, + TargetSpec: targetSpec, + PolicySpecs: policySpecs, + }, + ResourceVersion: nexusEndpoint.GetEndpoint().GetResourceVersion(), + }) + if err != nil { + resp.Diagnostics.AddError("Failed to update Nexus endpoint", err.Error()) + return + } + + if err := client.AwaitAsyncOperation(ctx, r.client, svcResp.GetAsyncOperation()); err != nil { + resp.Diagnostics.AddError("Failed to update Nexus endpoint", err.Error()) + return + } + + nexusEndpoint, err = r.client.CloudService().GetNexusEndpoint(ctx, &cloudservicev1.GetNexusEndpointRequest{ + EndpointId: plan.ID.ValueString(), + }) + if err != nil { + resp.Diagnostics.AddError("Failed to get Nexus endpoint after update", err.Error()) + return + } + + resp.Diagnostics.Append(updateNexusEndpointModelFromSpec(ctx, &plan, nexusEndpoint.Endpoint)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *nexusEndpointResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state nexusEndpointResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + deleteTimeout, diags := state.Timeouts.Delete(ctx, defaultDeleteTimeout) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + nexusEndpoint, err := r.client.CloudService().GetNexusEndpoint(ctx, &cloudservicev1.GetNexusEndpointRequest{ + EndpointId: state.ID.ValueString(), + }) + if err != nil { + resp.Diagnostics.AddError("Failed to get current Nexus endpoint status", err.Error()) + return + } + + ctx, cancel := context.WithTimeout(ctx, deleteTimeout) + defer cancel() + + svcResp, err := r.client.CloudService().DeleteNexusEndpoint(ctx, &cloudservicev1.DeleteNexusEndpointRequest{ + EndpointId: state.ID.ValueString(), + ResourceVersion: nexusEndpoint.GetEndpoint().GetResourceVersion(), + }) + if err != nil { + resp.Diagnostics.AddError("Failed to delete Nexus endpoint", err.Error()) + return + } + + if err := client.AwaitAsyncOperation(ctx, r.client, svcResp.AsyncOperation); err != nil { + resp.Diagnostics.AddError("Failed to delete Nexus endpoint", err.Error()) + } +} + +func (r *nexusEndpointResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func updateNexusEndpointModelFromSpec(ctx context.Context, model *nexusEndpointResourceModel, nexusEndpoint *nexusv1.Endpoint) diag.Diagnostics { + var diags diag.Diagnostics + + model.ID = types.StringValue(nexusEndpoint.GetId()) + + model.Name = types.StringValue(nexusEndpoint.GetSpec().GetName()) + + if nexusEndpoint.GetSpec().GetDescription() != "" { + model.Description = types.StringValue(nexusEndpoint.GetSpec().GetDescription()) + } + + nexusEndpointTargetSpec := nexusEndpoint.GetSpec().GetTargetSpec() + if workerSpec := nexusEndpointTargetSpec.GetWorkerTargetSpec(); workerSpec != nil { + workerTargetSpec := &nexusEndpointWorkerTargetModel{ + NamespaceID: types.StringValue(workerSpec.GetNamespaceId()), + TaskQueue: types.StringValue(workerSpec.GetTaskQueue()), + } + model.WorkerTargetSpec, diags = types.ObjectValueFrom(ctx, workerTargetSpecAttrs, workerTargetSpec) + if diags.HasError() { + return diags + } + } + + allowedNamespaces := make([]types.String, 0) + nexusEndpointPolicySpecs := nexusEndpoint.GetSpec().GetPolicySpecs() + for _, policySpec := range nexusEndpointPolicySpecs { + if policySpec.GetAllowedCloudNamespacePolicySpec() != nil { + allowedNamespaces = append(allowedNamespaces, types.StringValue(policySpec.GetAllowedCloudNamespacePolicySpec().GetNamespaceId())) + } + } + model.AllowedCallerNamespaces, diags = types.SetValueFrom(ctx, types.StringType, allowedNamespaces) + if diags.HasError() { + return diags + } + + return diags +} + +func getTargetSpecFromModel(ctx context.Context, model *nexusEndpointResourceModel) (*nexusv1.EndpointTargetSpec, diag.Diagnostics) { + var diags diag.Diagnostics + + var workerTargetSpecModel nexusEndpointWorkerTargetModel + diags.Append(model.WorkerTargetSpec.As(ctx, &workerTargetSpecModel, basetypes.ObjectAsOptions{})...) + if diags.HasError() { + return nil, diags + } + workerTargetSpec := &nexusv1.WorkerTargetSpec{ + NamespaceId: workerTargetSpecModel.NamespaceID.ValueString(), + TaskQueue: workerTargetSpecModel.TaskQueue.ValueString(), + } + + return &nexusv1.EndpointTargetSpec{ + Variant: &nexusv1.EndpointTargetSpec_WorkerTargetSpec{ + WorkerTargetSpec: workerTargetSpec, + }, + }, diags +} + +func getPolicySpecsFromModel(_ context.Context, model *nexusEndpointResourceModel) ([]*nexusv1.EndpointPolicySpec, diag.Diagnostics) { + var diags diag.Diagnostics + + policySpecs := make([]*nexusv1.EndpointPolicySpec, 0, len(model.AllowedCallerNamespaces.Elements())) + for _, namespace := range model.AllowedCallerNamespaces.Elements() { + ns := namespace.(types.String).ValueString() + policySpecs = append(policySpecs, &nexusv1.EndpointPolicySpec{ + Variant: &nexusv1.EndpointPolicySpec_AllowedCloudNamespacePolicySpec{ + AllowedCloudNamespacePolicySpec: &nexusv1.AllowedCloudNamespacePolicySpec{ + NamespaceId: ns, + }, + }, + }) + } + + return policySpecs, diags +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 9ce8fd8..94058a4 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -174,6 +174,7 @@ func (p *TerraformCloudProvider) Resources(ctx context.Context) []func() resourc NewServiceAccountResource, NewApiKeyResource, NewMetricsEndpointResource, + NewNexusEndpointResource, } } From fa5d35724ee0d73b34e0568021d9b59d04d95bdb Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Sun, 5 Jan 2025 09:34:52 -0600 Subject: [PATCH 02/10] fix lint errors --- internal/provider/nexus_endpoint_resource.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/provider/nexus_endpoint_resource.go b/internal/provider/nexus_endpoint_resource.go index a264075..0309ad3 100644 --- a/internal/provider/nexus_endpoint_resource.go +++ b/internal/provider/nexus_endpoint_resource.go @@ -384,19 +384,20 @@ func getTargetSpecFromModel(ctx context.Context, model *nexusEndpointResourceMod } func getPolicySpecsFromModel(_ context.Context, model *nexusEndpointResourceModel) ([]*nexusv1.EndpointPolicySpec, diag.Diagnostics) { - var diags diag.Diagnostics - policySpecs := make([]*nexusv1.EndpointPolicySpec, 0, len(model.AllowedCallerNamespaces.Elements())) for _, namespace := range model.AllowedCallerNamespaces.Elements() { - ns := namespace.(types.String).ValueString() + ns, ok := namespace.(types.String) + if !ok { + return nil, diag.Diagnostics{diag.NewErrorDiagnostic("Invalid namespace type", "Expected types.String")} + } policySpecs = append(policySpecs, &nexusv1.EndpointPolicySpec{ Variant: &nexusv1.EndpointPolicySpec_AllowedCloudNamespacePolicySpec{ AllowedCloudNamespacePolicySpec: &nexusv1.AllowedCloudNamespacePolicySpec{ - NamespaceId: ns, + NamespaceId: ns.ValueString(), }, }, }) } - return policySpecs, diags + return policySpecs, nil } From 7a55d86e807303d5a20e09701cbea125084ece67 Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Sun, 5 Jan 2025 12:35:25 -0600 Subject: [PATCH 03/10] Add acc tests --- .../provider/nexus_endpoint_resource_test.go | 108 ++++++++++++++++++ internal/provider/utils_test.go | 16 +++ 2 files changed, 124 insertions(+) create mode 100644 internal/provider/nexus_endpoint_resource_test.go create mode 100644 internal/provider/utils_test.go diff --git a/internal/provider/nexus_endpoint_resource_test.go b/internal/provider/nexus_endpoint_resource_test.go new file mode 100644 index 0000000..9b67192 --- /dev/null +++ b/internal/provider/nexus_endpoint_resource_test.go @@ -0,0 +1,108 @@ +package provider + +import ( + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccNexusEndpointResource(t *testing.T) { + // Using a counter to ensure unique names across test steps + endpointName := fmt.Sprintf("tf-nexus-endpoint-%s-%s", time.Now().Format("2006-01-02-15-04-05"), randomStringWithLength(3)) + description := "test description" + targetNamespaceName := fmt.Sprintf("tf-nexus-target-%s-%s", time.Now().Format("060102150405"), randomStringWithLength(4)) + taskQueue := "task-queue-1" + callerNamespaceName := fmt.Sprintf("tf-nexus-caller-%s-%s", time.Now().Format("060102150405"), randomStringWithLength(4)) + callerNamespace2Name := fmt.Sprintf("tf-nexus-caller2-%s-%s", time.Now().Format("060102150405"), randomStringWithLength(3)) + + updatedDescription := "updated description" + updatedTaskQueue := "task-queue-2" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: testAccNexusEndpointResourceConfig(endpointName, description, targetNamespaceName, taskQueue, []string{callerNamespaceName, callerNamespace2Name}), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "name", endpointName), + resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "description", description), + resource.TestCheckResourceAttrSet("temporalcloud_nexus_endpoint.test", "worker_target_spec.namespace_id"), + // resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "worker_target_spec.namespace_id", targetNamespaceName + "." + accountID), + resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "worker_target_spec.task_queue", taskQueue), + resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "allowed_caller_namespaces.#", "2"), + resource.TestCheckResourceAttrSet("temporalcloud_nexus_endpoint.test", "id"), + ), + }, + // ImportState testing + { + ResourceName: "temporalcloud_nexus_endpoint.test", + ImportState: true, + ImportStateVerify: true, + }, + // Update and Read testing + { + Config: testAccNexusEndpointResourceConfig(endpointName, updatedDescription, targetNamespaceName, updatedTaskQueue, []string{callerNamespaceName}), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "description", updatedDescription), + resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "worker_target_spec.task_queue", updatedTaskQueue), + resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "allowed_caller_namespaces.#", "1"), + ), + }, + // Delete testing automatically occurs in TestCase + }, + }) +} + +func testAccNamespaceResourceConfig(resourceName, name, region string, retentionDays int) string { + return fmt.Sprintf(` +resource "temporalcloud_namespace" %[1]q { + name = %[2]q + regions = [%[3]q] + api_key_auth = true + retention_days = %[4]d + timeouts { + create = "15m" + delete = "15m" + } +} +`, resourceName, name, region, retentionDays) +} + +func testAccNexusEndpointResourceConfig(name, description, targetNamespaceName, taskQueue string, allowedNamespaces []string) string { + region := "aws-us-west-2" + retentionDays := 1 + allowedNamespaceIDs := []string{} + namespacesConfig := testAccNamespaceResourceConfig("target_namespace", targetNamespaceName, region, retentionDays) + for i, allowedNamespace := range allowedNamespaces { + namespacesConfig += testAccNamespaceResourceConfig("allowed_namespace_"+strconv.Itoa(i), allowedNamespace, region, retentionDays) + allowedNamespaceIDs = append(allowedNamespaceIDs, "temporalcloud_namespace.allowed_namespace_"+strconv.Itoa(i)+".id") + } + allowedNamespaceIDsStr := fmt.Sprintf("[%s]", strings.Join(allowedNamespaceIDs, ", ")) + + return fmt.Sprintf(` +%[1]s + +resource "temporalcloud_nexus_endpoint" "test" { + name = %[2]q + description = %[3]q + + worker_target_spec = { + namespace_id = temporalcloud_namespace.target_namespace.id + task_queue = %[4]q + } + + allowed_caller_namespaces = %[5]s + + timeouts { + create = "4m" + delete = "4m" + } +} +`, namespacesConfig, name, description, taskQueue, allowedNamespaceIDsStr) +} diff --git a/internal/provider/utils_test.go b/internal/provider/utils_test.go new file mode 100644 index 0000000..f1cef06 --- /dev/null +++ b/internal/provider/utils_test.go @@ -0,0 +1,16 @@ +package provider + +import ( + "math/rand" + "time" +) + +func randomStringWithLength(length int) string { + r := rand.New(rand.NewSource(time.Now().UTC().UnixNano())) + const charset = "abcdefghijklmnopqrstuvwxyz" + b := make([]byte, length) + for i := range b { + b[i] = charset[r.Intn(len(charset))] + } + return string(b) +} From e5076b1c9504d9e879eb62ff24c938c821347b89 Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Mon, 6 Jan 2025 08:43:43 -0600 Subject: [PATCH 04/10] Refactor time --- internal/provider/nexus_endpoint_resource_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/provider/nexus_endpoint_resource_test.go b/internal/provider/nexus_endpoint_resource_test.go index 9b67192..2606bf8 100644 --- a/internal/provider/nexus_endpoint_resource_test.go +++ b/internal/provider/nexus_endpoint_resource_test.go @@ -11,13 +11,13 @@ import ( ) func TestAccNexusEndpointResource(t *testing.T) { - // Using a counter to ensure unique names across test steps - endpointName := fmt.Sprintf("tf-nexus-endpoint-%s-%s", time.Now().Format("2006-01-02-15-04-05"), randomStringWithLength(3)) + timeSuffix := time.Now().Format("060102150405") + endpointName := fmt.Sprintf("tf-nexus-endpoint-%s-%s", timeSuffix, randomStringWithLength(3)) description := "test description" - targetNamespaceName := fmt.Sprintf("tf-nexus-target-%s-%s", time.Now().Format("060102150405"), randomStringWithLength(4)) + targetNamespaceName := fmt.Sprintf("tf-nexus-target-%s-%s", timeSuffix, randomStringWithLength(4)) taskQueue := "task-queue-1" - callerNamespaceName := fmt.Sprintf("tf-nexus-caller-%s-%s", time.Now().Format("060102150405"), randomStringWithLength(4)) - callerNamespace2Name := fmt.Sprintf("tf-nexus-caller2-%s-%s", time.Now().Format("060102150405"), randomStringWithLength(3)) + callerNamespaceName := fmt.Sprintf("tf-nexus-caller-%s-%s", timeSuffix, randomStringWithLength(4)) + callerNamespace2Name := fmt.Sprintf("tf-nexus-caller2-%s-%s", timeSuffix, randomStringWithLength(3)) updatedDescription := "updated description" updatedTaskQueue := "task-queue-2" From dba8a51d69aa60e1341f1b8c0f01afd90f49140d Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Thu, 16 Jan 2025 19:21:04 -0600 Subject: [PATCH 05/10] Fix Description --- internal/provider/nexus_endpoint_resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/nexus_endpoint_resource.go b/internal/provider/nexus_endpoint_resource.go index 0309ad3..47ed97b 100644 --- a/internal/provider/nexus_endpoint_resource.go +++ b/internal/provider/nexus_endpoint_resource.go @@ -113,7 +113,7 @@ func (r *nexusEndpointResource) Schema(ctx context.Context, _ resource.SchemaReq Required: true, }, "allowed_caller_namespaces": schema.SetAttribute{ - Description: "Namespace(s) that are allowed to call this Endpoint.", + Description: "Namespace Id(s) that are allowed to call this Endpoint.", ElementType: types.StringType, Required: true, }, From 21dc48fe5abf2cd62c6b8357360b6a41fdd6923d Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Thu, 16 Jan 2025 19:11:23 -0600 Subject: [PATCH 06/10] Rename `worker_target_spec` to `worker_target` update docs for worker_target change --- docs/resources/nexus_endpoint.md | 10 ++++---- .../temporalcloud_nexus_endpoint/resource.tf | 2 +- internal/provider/nexus_endpoint_resource.go | 24 +++++++++---------- .../provider/nexus_endpoint_resource_test.go | 10 ++++---- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/resources/nexus_endpoint.md b/docs/resources/nexus_endpoint.md index e1bea0c..bd1b8c5 100644 --- a/docs/resources/nexus_endpoint.md +++ b/docs/resources/nexus_endpoint.md @@ -66,7 +66,7 @@ resource "temporalcloud_nexus_endpoint" "nexus_endpoint" { Input / Output arguments are in the following repository: https://github.com/temporalio/samples-go/blob/main/nexus/service/api.go EOT - worker_target_spec = { + worker_target = { namespace_id = temporalcloud_namespace.target_namespace.id task_queue = "terraform-task-queue" } @@ -82,9 +82,9 @@ resource "temporalcloud_nexus_endpoint" "nexus_endpoint" { ### Required -- `allowed_caller_namespaces` (Set of String) Namespace(s) that are allowed to call this Endpoint. +- `allowed_caller_namespaces` (Set of String) Namespace Id(s) that are allowed to call this Endpoint. - `name` (String) The name of the endpoint. Must be unique within an account and match `^[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9]$` -- `worker_target_spec` (Attributes) A target spec for routing nexus requests to a specific cloud namespace worker. (see [below for nested schema](#nestedatt--worker_target_spec)) +- `worker_target` (Attributes) A target spec for routing nexus requests to a specific cloud namespace worker. (see [below for nested schema](#nestedatt--worker_target)) ### Optional @@ -95,8 +95,8 @@ resource "temporalcloud_nexus_endpoint" "nexus_endpoint" { - `id` (String) The unique identifier of the Nexus endpoint. - -### Nested Schema for `worker_target_spec` + +### Nested Schema for `worker_target` Required: diff --git a/examples/resources/temporalcloud_nexus_endpoint/resource.tf b/examples/resources/temporalcloud_nexus_endpoint/resource.tf index fcfb73a..bf0dccb 100644 --- a/examples/resources/temporalcloud_nexus_endpoint/resource.tf +++ b/examples/resources/temporalcloud_nexus_endpoint/resource.tf @@ -51,7 +51,7 @@ resource "temporalcloud_nexus_endpoint" "nexus_endpoint" { Input / Output arguments are in the following repository: https://github.com/temporalio/samples-go/blob/main/nexus/service/api.go EOT - worker_target_spec = { + worker_target = { namespace_id = temporalcloud_namespace.target_namespace.id task_queue = "terraform-task-queue" } diff --git a/internal/provider/nexus_endpoint_resource.go b/internal/provider/nexus_endpoint_resource.go index 47ed97b..ce66217 100644 --- a/internal/provider/nexus_endpoint_resource.go +++ b/internal/provider/nexus_endpoint_resource.go @@ -29,7 +29,7 @@ type ( ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` - WorkerTargetSpec types.Object `tfsdk:"worker_target_spec"` + WorkerTarget types.Object `tfsdk:"worker_target"` AllowedCallerNamespaces types.Set `tfsdk:"allowed_caller_namespaces"` Timeouts timeouts.Value `tfsdk:"timeouts"` @@ -46,7 +46,7 @@ var ( _ resource.ResourceWithConfigure = (*nexusEndpointResource)(nil) _ resource.ResourceWithImportState = (*nexusEndpointResource)(nil) - workerTargetSpecAttrs = map[string]attr.Type{ + workerTargetAttrs = map[string]attr.Type{ "namespace_id": types.StringType, "task_queue": types.StringType, } @@ -98,7 +98,7 @@ func (r *nexusEndpointResource) Schema(ctx context.Context, _ resource.SchemaReq Optional: true, Sensitive: true, }, - "worker_target_spec": schema.SingleNestedAttribute{ + "worker_target": schema.SingleNestedAttribute{ Description: "A target spec for routing nexus requests to a specific cloud namespace worker.", Attributes: map[string]schema.Attribute{ "namespace_id": schema.StringAttribute{ @@ -337,12 +337,12 @@ func updateNexusEndpointModelFromSpec(ctx context.Context, model *nexusEndpointR } nexusEndpointTargetSpec := nexusEndpoint.GetSpec().GetTargetSpec() - if workerSpec := nexusEndpointTargetSpec.GetWorkerTargetSpec(); workerSpec != nil { - workerTargetSpec := &nexusEndpointWorkerTargetModel{ - NamespaceID: types.StringValue(workerSpec.GetNamespaceId()), - TaskQueue: types.StringValue(workerSpec.GetTaskQueue()), + if workerTargetSpec := nexusEndpointTargetSpec.GetWorkerTargetSpec(); workerTargetSpec != nil { + workerTarget := &nexusEndpointWorkerTargetModel{ + NamespaceID: types.StringValue(workerTargetSpec.GetNamespaceId()), + TaskQueue: types.StringValue(workerTargetSpec.GetTaskQueue()), } - model.WorkerTargetSpec, diags = types.ObjectValueFrom(ctx, workerTargetSpecAttrs, workerTargetSpec) + model.WorkerTarget, diags = types.ObjectValueFrom(ctx, workerTargetAttrs, workerTarget) if diags.HasError() { return diags } @@ -366,14 +366,14 @@ func updateNexusEndpointModelFromSpec(ctx context.Context, model *nexusEndpointR func getTargetSpecFromModel(ctx context.Context, model *nexusEndpointResourceModel) (*nexusv1.EndpointTargetSpec, diag.Diagnostics) { var diags diag.Diagnostics - var workerTargetSpecModel nexusEndpointWorkerTargetModel - diags.Append(model.WorkerTargetSpec.As(ctx, &workerTargetSpecModel, basetypes.ObjectAsOptions{})...) + var workerTargetModel nexusEndpointWorkerTargetModel + diags.Append(model.WorkerTarget.As(ctx, &workerTargetModel, basetypes.ObjectAsOptions{})...) if diags.HasError() { return nil, diags } workerTargetSpec := &nexusv1.WorkerTargetSpec{ - NamespaceId: workerTargetSpecModel.NamespaceID.ValueString(), - TaskQueue: workerTargetSpecModel.TaskQueue.ValueString(), + NamespaceId: workerTargetModel.NamespaceID.ValueString(), + TaskQueue: workerTargetModel.TaskQueue.ValueString(), } return &nexusv1.EndpointTargetSpec{ diff --git a/internal/provider/nexus_endpoint_resource_test.go b/internal/provider/nexus_endpoint_resource_test.go index 2606bf8..b3bfdf0 100644 --- a/internal/provider/nexus_endpoint_resource_test.go +++ b/internal/provider/nexus_endpoint_resource_test.go @@ -32,9 +32,9 @@ func TestAccNexusEndpointResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "name", endpointName), resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "description", description), - resource.TestCheckResourceAttrSet("temporalcloud_nexus_endpoint.test", "worker_target_spec.namespace_id"), - // resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "worker_target_spec.namespace_id", targetNamespaceName + "." + accountID), - resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "worker_target_spec.task_queue", taskQueue), + resource.TestCheckResourceAttrSet("temporalcloud_nexus_endpoint.test", "worker_target.namespace_id"), + // resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "worker_target.namespace_id", targetNamespaceName + "." + accountID), + resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "worker_target.task_queue", taskQueue), resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "allowed_caller_namespaces.#", "2"), resource.TestCheckResourceAttrSet("temporalcloud_nexus_endpoint.test", "id"), ), @@ -50,7 +50,7 @@ func TestAccNexusEndpointResource(t *testing.T) { Config: testAccNexusEndpointResourceConfig(endpointName, updatedDescription, targetNamespaceName, updatedTaskQueue, []string{callerNamespaceName}), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "description", updatedDescription), - resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "worker_target_spec.task_queue", updatedTaskQueue), + resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "worker_target.task_queue", updatedTaskQueue), resource.TestCheckResourceAttr("temporalcloud_nexus_endpoint.test", "allowed_caller_namespaces.#", "1"), ), }, @@ -92,7 +92,7 @@ resource "temporalcloud_nexus_endpoint" "test" { name = %[2]q description = %[3]q - worker_target_spec = { + worker_target = { namespace_id = temporalcloud_namespace.target_namespace.id task_queue = %[4]q } From c3af8200210fc35d7863c21930d6f6d8130a1309 Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Thu, 16 Jan 2025 20:35:19 -0600 Subject: [PATCH 07/10] Add a test case to assert allowed namespace order does not change plan --- internal/provider/nexus_endpoint_resource_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/provider/nexus_endpoint_resource_test.go b/internal/provider/nexus_endpoint_resource_test.go index b3bfdf0..d51cd0e 100644 --- a/internal/provider/nexus_endpoint_resource_test.go +++ b/internal/provider/nexus_endpoint_resource_test.go @@ -2,7 +2,6 @@ package provider import ( "fmt" - "strconv" "strings" "testing" "time" @@ -39,6 +38,11 @@ func TestAccNexusEndpointResource(t *testing.T) { resource.TestCheckResourceAttrSet("temporalcloud_nexus_endpoint.test", "id"), ), }, + { + Config: testAccNexusEndpointResourceConfig(endpointName, description, targetNamespaceName, taskQueue, []string{callerNamespace2Name, callerNamespaceName}), + PlanOnly: true, + ExpectNonEmptyPlan: false, + }, // ImportState testing { ResourceName: "temporalcloud_nexus_endpoint.test", @@ -79,9 +83,9 @@ func testAccNexusEndpointResourceConfig(name, description, targetNamespaceName, retentionDays := 1 allowedNamespaceIDs := []string{} namespacesConfig := testAccNamespaceResourceConfig("target_namespace", targetNamespaceName, region, retentionDays) - for i, allowedNamespace := range allowedNamespaces { - namespacesConfig += testAccNamespaceResourceConfig("allowed_namespace_"+strconv.Itoa(i), allowedNamespace, region, retentionDays) - allowedNamespaceIDs = append(allowedNamespaceIDs, "temporalcloud_namespace.allowed_namespace_"+strconv.Itoa(i)+".id") + for _, allowedNamespace := range allowedNamespaces { + namespacesConfig += testAccNamespaceResourceConfig("allowed_namespace_"+allowedNamespace, allowedNamespace, region, retentionDays) + allowedNamespaceIDs = append(allowedNamespaceIDs, "temporalcloud_namespace.allowed_namespace_"+allowedNamespace+".id") } allowedNamespaceIDsStr := fmt.Sprintf("[%s]", strings.Join(allowedNamespaceIDs, ", ")) From d031e693fa669bb9cabbfe1d6b15382f0a137cf8 Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Thu, 16 Jan 2025 21:52:03 -0600 Subject: [PATCH 08/10] Update acc test namespace region --- internal/provider/nexus_endpoint_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/nexus_endpoint_resource_test.go b/internal/provider/nexus_endpoint_resource_test.go index d51cd0e..f3bced3 100644 --- a/internal/provider/nexus_endpoint_resource_test.go +++ b/internal/provider/nexus_endpoint_resource_test.go @@ -79,7 +79,7 @@ resource "temporalcloud_namespace" %[1]q { } func testAccNexusEndpointResourceConfig(name, description, targetNamespaceName, taskQueue string, allowedNamespaces []string) string { - region := "aws-us-west-2" + region := "aws-us-east-1" retentionDays := 1 allowedNamespaceIDs := []string{} namespacesConfig := testAccNamespaceResourceConfig("target_namespace", targetNamespaceName, region, retentionDays) From cc9d67e5d500a1a8fd7742fb212edc046289953c Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Sat, 18 Jan 2025 09:49:27 -0600 Subject: [PATCH 09/10] Handle description payload --- internal/client/client.go | 2 +- internal/provider/nexus_endpoint_resource.go | 25 ++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/internal/client/client.go b/internal/client/client.go index 5847905..d391d4b 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -37,7 +37,7 @@ import ( "go.temporal.io/sdk/client" ) -var TemporalCloudAPIVersion = "2024-10-01-00" +var TemporalCloudAPIVersion = "2025-01-01-00" // Client is a client for the Temporal Cloud API. type Client struct { diff --git a/internal/provider/nexus_endpoint_resource.go b/internal/provider/nexus_endpoint_resource.go index ce66217..b479620 100644 --- a/internal/provider/nexus_endpoint_resource.go +++ b/internal/provider/nexus_endpoint_resource.go @@ -18,6 +18,7 @@ import ( "github.com/temporalio/terraform-provider-temporalcloud/internal/client" cloudservicev1 "go.temporal.io/api/cloud/cloudservice/v1" nexusv1 "go.temporal.io/api/cloud/nexus/v1" + "go.temporal.io/sdk/converter" ) type ( @@ -147,6 +148,11 @@ func (r *nexusEndpointResource) Create(ctx context.Context, req resource.CreateR if !plan.Description.IsNull() { description = plan.Description.ValueString() } + descriptionPayload, err := converter.GetDefaultDataConverter().ToPayload(description) + if err != nil { + resp.Diagnostics.AddError("Failed to convert Nexus endpoint description", err.Error()) + return + } targetSpec, diags := getTargetSpecFromModel(ctx, &plan) resp.Diagnostics.Append(diags...) @@ -163,7 +169,7 @@ func (r *nexusEndpointResource) Create(ctx context.Context, req resource.CreateR svcResp, err := r.client.CloudService().CreateNexusEndpoint(ctx, &cloudservicev1.CreateNexusEndpointRequest{ Spec: &nexusv1.EndpointSpec{ Name: plan.Name.ValueString(), - Description: description, + Description: descriptionPayload, TargetSpec: targetSpec, PolicySpecs: policySpecs, }, @@ -235,6 +241,11 @@ func (r *nexusEndpointResource) Update(ctx context.Context, req resource.UpdateR if !plan.Description.IsNull() { description = plan.Description.ValueString() } + descriptionPayload, err := converter.GetDefaultDataConverter().ToPayload(description) + if err != nil { + resp.Diagnostics.AddError("Failed to convert Nexus endpoint description", err.Error()) + return + } targetSpec, diags := getTargetSpecFromModel(ctx, &plan) resp.Diagnostics.Append(diags...) @@ -252,7 +263,7 @@ func (r *nexusEndpointResource) Update(ctx context.Context, req resource.UpdateR EndpointId: plan.ID.ValueString(), Spec: &nexusv1.EndpointSpec{ Name: plan.Name.ValueString(), - Description: description, + Description: descriptionPayload, TargetSpec: targetSpec, PolicySpecs: policySpecs, }, @@ -332,8 +343,14 @@ func updateNexusEndpointModelFromSpec(ctx context.Context, model *nexusEndpointR model.Name = types.StringValue(nexusEndpoint.GetSpec().GetName()) - if nexusEndpoint.GetSpec().GetDescription() != "" { - model.Description = types.StringValue(nexusEndpoint.GetSpec().GetDescription()) + if nexusEndpoint.GetSpec().GetDescription() != nil { + var description string + err := converter.GetDefaultDataConverter().FromPayload(nexusEndpoint.GetSpec().GetDescription(), &description) + if err != nil { + diags.AddError("Failed to convert Nexus endpoint description", err.Error()) + return diags + } + model.Description = types.StringValue(description) } nexusEndpointTargetSpec := nexusEndpoint.GetSpec().GetTargetSpec() From 1168e86ebd18a11fe58acf1778b80bf1eaf213cb Mon Sep 17 00:00:00 2001 From: Nikki Dag Date: Sat, 18 Jan 2025 10:48:18 -0600 Subject: [PATCH 10/10] Increase test timeout --- .github/workflows/test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd04f24..9a177be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,6 @@ jobs: name: Terraform Provider Acceptance Tests needs: build runs-on: ubuntu-latest - timeout-minutes: 15 strategy: fail-fast: false matrix: @@ -87,5 +86,4 @@ jobs: - env: TF_ACC: "1" TEMPORAL_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLOUD_API_KEY }} - run: go test -timeout 15m -parallel 7 -v -cover ./internal/provider/ - timeout-minutes: 15 + run: go test -timeout 20m -parallel 7 -v -cover ./internal/provider/