From d22cc67e22654c9095056d82f1df5701638ff061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Wed, 4 Dec 2024 14:02:20 +0100 Subject: [PATCH 01/15] Fixup Fixup Fixup --- go.mod | 2 +- go.sum | 2 + pkg/provider/provider.go | 1 + pkg/resources/database/resource_user.go | 331 ++++++++++++++++++ pkg/resources/database/resource_user_kafka.go | 250 +++++++++++++ pkg/resources/database/resource_user_mysql.go | 250 +++++++++++++ .../database/resource_user_opensearch.go | 225 ++++++++++++ pkg/resources/database/resource_user_pg.go | 249 +++++++++++++ .../datasource/schema/attribute.go | 3 + .../datasource/schema/map_attribute.go | 4 +- .../datasource/schema/map_nested_attribute.go | 6 +- .../datasource/schema/set_nested_attribute.go | 2 +- .../schema/single_nested_attribute.go | 2 +- .../ephemeral/close.go | 30 ++ .../ephemeral/config_validator.go | 28 ++ .../ephemeral/configure.go | 33 ++ .../ephemeral/deferred.go | 50 +++ .../ephemeral/doc.go | 26 ++ .../ephemeral/ephemeral_resource.go | 108 ++++++ .../ephemeral/metadata.go | 23 ++ .../ephemeral/open.go | 80 +++++ .../ephemeral/renew.go | 51 +++ .../ephemeral/schema.go | 26 ++ .../ephemeral/schema/attribute.go | 39 +++ .../ephemeral/schema/block.go | 30 ++ .../ephemeral/schema/bool_attribute.go | 185 ++++++++++ .../ephemeral/schema/doc.go | 11 + .../ephemeral/schema/dynamic_attribute.go | 186 ++++++++++ .../ephemeral/schema/float32_attribute.go | 189 ++++++++++ .../ephemeral/schema/float64_attribute.go | 188 ++++++++++ .../ephemeral/schema/int32_attribute.go | 189 ++++++++++ .../ephemeral/schema/int64_attribute.go | 188 ++++++++++ .../ephemeral/schema/list_attribute.go | 220 ++++++++++++ .../ephemeral/schema/list_nested_attribute.go | 244 +++++++++++++ .../ephemeral/schema/list_nested_block.go | 205 +++++++++++ .../ephemeral/schema/map_attribute.go | 223 ++++++++++++ .../ephemeral/schema/map_nested_attribute.go | 245 +++++++++++++ .../ephemeral/schema/nested_attribute.go | 14 + .../schema/nested_attribute_object.go | 82 +++++ .../ephemeral/schema/nested_block_object.go | 94 +++++ .../ephemeral/schema/number_attribute.go | 189 ++++++++++ .../ephemeral/schema/object_attribute.go | 222 ++++++++++++ .../ephemeral/schema/schema.go | 187 ++++++++++ .../ephemeral/schema/set_attribute.go | 218 ++++++++++++ .../ephemeral/schema/set_nested_attribute.go | 240 +++++++++++++ .../ephemeral/schema/set_nested_block.go | 205 +++++++++++ .../schema/single_nested_attribute.go | 244 +++++++++++++ .../ephemeral/schema/single_nested_block.go | 213 +++++++++++ .../ephemeral/schema/string_attribute.go | 185 ++++++++++ .../ephemeral/validate_config.go | 32 ++ .../fromproto5/client_capabilities.go | 14 + .../fromproto5/closeephemeralresource.go | 52 +++ .../fromproto5/ephemeral_result_data.go | 53 +++ .../fromproto5/openephemeralresource.go | 52 +++ .../fromproto5/renewephemeralresource.go | 52 +++ .../validateephemeralresourceconfig.go | 31 ++ .../fromproto6/client_capabilities.go | 14 + .../fromproto6/closeephemeralresource.go | 52 +++ .../fromproto6/ephemeral_result_data.go | 53 +++ .../fromproto6/openephemeralresource.go | 52 +++ .../fromproto6/renewephemeralresource.go | 52 +++ .../validateephemeralresourceconfig.go | 31 ++ .../internal/fwschemadata/data_description.go | 6 + .../internal/fwserver/server.go | 29 ++ .../fwserver/server_closeephemeralresource.go | 76 ++++ .../fwserver/server_configureprovider.go | 1 + .../fwserver/server_ephemeralresources.go | 198 +++++++++++ .../internal/fwserver/server_getmetadata.go | 14 + .../fwserver/server_getproviderschema.go | 25 +- .../fwserver/server_openephemeralresource.go | 113 ++++++ .../fwserver/server_renewephemeralresource.go | 98 ++++++ .../server_validateephemeralresourceconfig.go | 109 ++++++ .../internal/logging/keys.go | 3 + .../internal/privatestate/data.go | 4 +- .../server_closeephemeralresource.go | 50 +++ .../server_openephemeralresource.go | 50 +++ .../server_renewephemeralresource.go | 50 +++ .../server_validateephemeralresourceconfig.go | 50 +++ .../server_closeephemeralresource.go | 50 +++ .../server_openephemeralresource.go | 50 +++ .../server_renewephemeralresource.go | 50 +++ .../server_validateephemeralresourceconfig.go | 50 +++ .../toproto5/closeephemeralresource.go | 25 ++ .../internal/toproto5/deferred.go | 10 + .../toproto5/ephemeral_result_data.go | 28 ++ .../toproto5/ephemeralresourcemetadata.go | 19 + .../internal/toproto5/getmetadata.go | 15 +- .../internal/toproto5/getproviderschema.go | 25 +- .../toproto5/openephemeralresource.go | 37 ++ .../toproto5/renewephemeralresource.go | 31 ++ .../internal/toproto5/server_capabilities.go | 1 + .../validateephemeralresourceconfig.go | 25 ++ .../toproto6/closeephemeralresource.go | 25 ++ .../internal/toproto6/deferred.go | 10 + .../toproto6/ephemeral_result_data.go | 28 ++ .../toproto6/ephemeralresourcemetadata.go | 19 + .../internal/toproto6/getmetadata.go | 5 + .../internal/toproto6/getproviderschema.go | 25 +- .../toproto6/openephemeralresource.go | 37 ++ .../toproto6/renewephemeralresource.go | 31 ++ .../internal/toproto6/server_capabilities.go | 1 + .../validateephemeralresourceconfig.go | 25 ++ .../provider/configure.go | 5 + .../provider/metaschema/map_attribute.go | 4 +- .../metaschema/map_nested_attribute.go | 6 +- .../metaschema/set_nested_attribute.go | 2 +- .../metaschema/single_nested_attribute.go | 2 +- .../provider/provider.go | 19 + .../provider/schema/attribute.go | 3 + .../provider/schema/map_attribute.go | 4 +- .../provider/schema/map_nested_attribute.go | 6 +- .../provider/schema/set_nested_attribute.go | 2 +- .../schema/single_nested_attribute.go | 2 +- .../resource/schema/attribute.go | 3 + .../resource/schema/boolplanmodifier/doc.go | 5 + .../boolplanmodifier/requires_replace.go | 30 ++ .../boolplanmodifier/requires_replace_if.go | 73 ++++ .../requires_replace_if_configured.go | 34 ++ .../requires_replace_if_func.go | 25 ++ .../boolplanmodifier/use_state_for_unknown.go | 55 +++ .../resource/schema/map_attribute.go | 4 +- .../resource/schema/map_nested_attribute.go | 6 +- .../resource/schema/set_nested_attribute.go | 2 +- .../schema/single_nested_attribute.go | 2 +- .../tfsdk/ephemeral_result_data.go | 94 +++++ vendor/modules.txt | 7 +- 126 files changed, 8595 insertions(+), 55 deletions(-) create mode 100644 pkg/resources/database/resource_user.go create mode 100644 pkg/resources/database/resource_user_kafka.go create mode 100644 pkg/resources/database/resource_user_mysql.go create mode 100644 pkg/resources/database/resource_user_opensearch.go create mode 100644 pkg/resources/database/resource_user_pg.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/close.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/config_validator.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/configure.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/deferred.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/doc.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/ephemeral_resource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/metadata.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/open.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/renew.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/block.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/bool_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/doc.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/dynamic_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/float32_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/float64_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/int32_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/int64_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_nested_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_nested_block.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/map_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/map_nested_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_attribute_object.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_block_object.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/number_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/object_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/schema.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_nested_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_nested_block.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/single_nested_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/single_nested_block.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/string_attribute.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/validate_config.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/closeephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/ephemeral_result_data.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/openephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/renewephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/validateephemeralresourceconfig.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/closeephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/ephemeral_result_data.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/openephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/renewephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/validateephemeralresourceconfig.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_closeephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_ephemeralresources.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_openephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_renewephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_validateephemeralresourceconfig.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_closeephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_openephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_renewephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_validateephemeralresourceconfig.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_closeephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_openephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_renewephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_validateephemeralresourceconfig.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/closeephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/ephemeral_result_data.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/ephemeralresourcemetadata.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/openephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/renewephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/validateephemeralresourceconfig.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/closeephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/ephemeral_result_data.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/ephemeralresourcemetadata.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/openephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/renewephemeralresource.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/validateephemeralresourceconfig.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/doc.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if_configured.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if_func.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/use_state_for_unknown.go create mode 100644 vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/ephemeral_result_data.go diff --git a/go.mod b/go.mod index d38589b47..44943a58a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/terraform-plugin-docs v0.16.0 - github.com/hashicorp/terraform-plugin-framework v1.11.0 + github.com/hashicorp/terraform-plugin-framework v1.13.0 github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0 github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.0 github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 diff --git a/go.sum b/go.sum index af248bb11..571153344 100644 --- a/go.sum +++ b/go.sum @@ -177,6 +177,8 @@ github.com/hashicorp/terraform-plugin-docs v0.16.0 h1:UmxFr3AScl6Wged84jndJIfFcc github.com/hashicorp/terraform-plugin-docs v0.16.0/go.mod h1:M3ZrlKBJAbPMtNOPwHicGi1c+hZUh7/g0ifT/z7TVfA= github.com/hashicorp/terraform-plugin-framework v1.11.0 h1:M7+9zBArexHFXDx/pKTxjE6n/2UCXY6b8FIq9ZYhwfE= github.com/hashicorp/terraform-plugin-framework v1.11.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM= +github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ532Bh2BobF4H+yBiYJ/scw= +github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU= github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0 h1:SJXL5FfJJm17554Kpt9jFXngdM6fXbnUnZ6iT2IeiYA= github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0/go.mod h1:p0phD0IYhsu9bR4+6OetVvvH59I6LwjXGnTVEr8ox6E= github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.0 h1:9buCmO0ciBITSCuw5ag6RdOwSsnBMl7OxOKOyXvRiZM= diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index d001e0c0c..ca9809606 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -220,6 +220,7 @@ func (p *ExoscaleProvider) DataSources(ctx context.Context) []func() datasource. func (p *ExoscaleProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ database.NewResource, + database.NewMysqlUserResource, iam.NewResourceOrgPolicy, iam.NewResourceRole, iam.NewResourceAPIKey, diff --git a/pkg/resources/database/resource_user.go b/pkg/resources/database/resource_user.go new file mode 100644 index 000000000..0bb9ae0da --- /dev/null +++ b/pkg/resources/database/resource_user.go @@ -0,0 +1,331 @@ +package database + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "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/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-log/tflog" + + exoscale "github.com/exoscale/egoscale/v3" + "github.com/exoscale/terraform-provider-exoscale/pkg/config" + "github.com/exoscale/terraform-provider-exoscale/pkg/utils" +) + +// UserResource defines the resource implementation. +type UserResource struct { + client *exoscale.Client +} + +// UserResourceModel describes the resource data model. +type UserResourceModel struct { + Id types.String `tfsdk:"id"` + Service types.String `tfsdk:"service"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` + Zone types.String `tfsdk:"zone"` + + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +var commonAttributes = map[string]schema.Attribute{ + // Attributes referencing the service + "service": schema.StringAttribute{ + Required: true, + MarkdownDescription: "❗ The name of the database service.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "zone": schema.StringAttribute{ + MarkdownDescription: "❗ The Exoscale [Zone](https://www.exoscale.com/datacenters/) name.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.OneOf(config.Zones...), + }, + }, + + // Variables + "username": schema.StringAttribute{ + Required: true, + MarkdownDescription: "❗ The name of the user for this service.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + + // Computed attributes + "id": schema.StringAttribute{ + MarkdownDescription: "The ID of this resource, as SERVICENAME/USERNAME", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "password": schema.StringAttribute{ + Description: "The password of the service user.", + Computed: true, + Sensitive: true, + }, +} + +func buildUserAttributes(newAttributes map[string]schema.Attribute) map[string]schema.Attribute { + + newSchemas := map[string]schema.Attribute{} + for k, v := range commonAttributes { + newSchemas[k] = v + } + for k, v := range newAttributes { + newSchemas[k] = v + } + + return newSchemas + +} + +type DatabaseServiceUserModel interface { + Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) + Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) + Delete(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) + Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) + GetTimeouts() timeouts.Value + SetTimeouts(timeouts.Value) + GenerateID() + GetID() basetypes.StringValue + GetZone() basetypes.StringValue +} + +func UserRead[T DatabaseServiceUserModel](ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, data T, client *exoscale.Client) { + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + // Set timeout + t, diags := data.GetTimeouts().Read(ctx, config.DefaultTimeout) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + ctx, cancel := context.WithTimeout(ctx, t) + defer cancel() + + data.GenerateID() + + client, err := utils.SwitchClientZone( + ctx, + client, + exoscale.ZoneName(data.GetZone().ValueString()), + ) + if err != nil { + resp.Diagnostics.AddError( + "unable to change exoscale client zone", + err.Error(), + ) + return + } + + data.Read(ctx, client, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + + tflog.Trace(ctx, "resource read done", map[string]interface{}{ + "id": data.GetID(), + }) + +} + +func UserReadForImport[T DatabaseServiceUserModel](ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, data T, client *exoscale.Client) { + + // Set timeout + t, diags := data.GetTimeouts().Read(ctx, config.DefaultTimeout) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + ctx, cancel := context.WithTimeout(ctx, t) + defer cancel() + + data.GenerateID() + + client, err := utils.SwitchClientZone( + ctx, + client, + exoscale.ZoneName(data.GetZone().ValueString()), + ) + if err != nil { + resp.Diagnostics.AddError( + "unable to change exoscale client zone", + err.Error(), + ) + return + } + + data.Read(ctx, client, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + + tflog.Trace(ctx, "resource read done", map[string]interface{}{ + "id": data.GetID(), + }) + +} + +func UserCreate[T DatabaseServiceUserModel](ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, data T, client *exoscale.Client) { + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + // Set timeout + t, diags := data.GetTimeouts().Create(ctx, config.DefaultTimeout) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + ctx, cancel := context.WithTimeout(ctx, t) + defer cancel() + + data.GenerateID() + + client, err := utils.SwitchClientZone( + ctx, + client, + exoscale.ZoneName(data.GetZone().ValueString()), + ) + if err != nil { + resp.Diagnostics.AddError( + "unable to change exoscale client zone", + err.Error(), + ) + return + } + + data.Create(ctx, client, &diags) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + + tflog.Trace(ctx, "resource created", map[string]interface{}{ + "id": data.GetID(), + }) + +} + +func UserUpdate[T DatabaseServiceUserModel](ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, stateData, planData T, client *exoscale.Client) { + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + // Read Terraform state data (for comparison) into the model + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + if resp.Diagnostics.HasError() { + return + } + + // Set timeout + t, diags := stateData.GetTimeouts().Update(ctx, config.DefaultTimeout) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + ctx, cancel := context.WithTimeout(ctx, t) + defer cancel() + + client, err := utils.SwitchClientZone( + ctx, + client, + exoscale.ZoneName(planData.GetZone().ValueString()), + ) + if err != nil { + resp.Diagnostics.AddError( + "unable to change exoscale client zone", + err.Error(), + ) + return + } + + planData.Update(ctx, client, &diags) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &planData)...) + + tflog.Trace(ctx, "resource updated", map[string]interface{}{ + "id": planData.GetID(), + }) +} + +func UserDelete[T DatabaseServiceUserModel](ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, data T, client *exoscale.Client) { + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Set timeout + t, diags := data.GetTimeouts().Delete(ctx, config.DefaultTimeout) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + ctx, cancel := context.WithTimeout(ctx, t) + defer cancel() + + data.GenerateID() + + client, err := utils.SwitchClientZone( + ctx, + client, + exoscale.ZoneName(data.GetZone().ValueString()), + ) + if err != nil { + resp.Diagnostics.AddError( + "unable to change exoscale client zone", + err.Error(), + ) + return + } + + data.Delete(ctx, client, &diags) + + if resp.Diagnostics.HasError() { + return + } + + tflog.Trace(ctx, "resource deleted", map[string]interface{}{ + "id": data.GetID(), + }) + +} diff --git a/pkg/resources/database/resource_user_kafka.go b/pkg/resources/database/resource_user_kafka.go new file mode 100644 index 000000000..fcc8ea3c7 --- /dev/null +++ b/pkg/resources/database/resource_user_kafka.go @@ -0,0 +1,250 @@ +package database + +import ( + "context" + "fmt" + "strings" + + exoscale "github.com/exoscale/egoscale/v3" + providerConfig "github.com/exoscale/terraform-provider-exoscale/pkg/provider/config" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/diag" + "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/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var _ resource.Resource = &KafkaUserResource{} +var _ resource.ResourceWithImportState = &KafkaUserResource{} + +func NewKafkaUserResource() resource.Resource { + return &KafkaUserResource{} +} + +type KafkaUserResource struct { + UserResource +} + +type KafkaUserResourceModel struct { + UserResourceModel + AccessKey types.String `tfsdk:"access_key"` + AccessCert types.String `tfsdk:"access_cert"` + AccessCertExpiry types.String `tfsdk:"access_cert_expiry"` +} + +func (r *KafkaUserResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + r.client = req.ProviderData.(*providerConfig.ExoscaleProviderConfig).ClientV3 +} + +func (r *KafkaUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_database_kafka_user" +} + +func (r *KafkaUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Manage service users for a Kafka Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/).", + Attributes: buildUserAttributes(map[string]schema.Attribute{ + "access_key": schema.StringAttribute{ + Description: "Access certificate key for the user.", + Computed: true, + Sensitive: true, + }, + "access_cert": schema.StringAttribute{ + Description: "Access certificate for the user.", + Computed: true, + Sensitive: true, + }, + "access_cert_expiry": schema.StringAttribute{ + Description: "Access certificate expiry date.", + Computed: true, + }, + }), + Blocks: map[string]schema.Block{ + "timeouts": timeouts.BlockAll(ctx), + }, + } +} + +func (r *KafkaUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data KafkaUserResourceModel + UserRead(ctx, req, resp, &data, r.client) +} + +func (r *KafkaUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + + var data KafkaUserResourceModel + UserCreate(ctx, req, resp, &data, r.client) +} + +func (r *KafkaUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var stateData, planData KafkaUserResourceModel + UserUpdate(ctx, req, resp, &stateData, &planData, r.client) +} + +func (r *KafkaUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data KafkaUserResourceModel + UserDelete(ctx, req, resp, &data, r.client) +} + +func (r *KafkaUserResource) 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) != 2 || idParts[0] == "" || idParts[1] == "" { + + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: service/username@zone. Got: %q", req.ID), + ) + + return + } + + userID := idParts[0] + zone := idParts[1] + + id := strings.Split(userID, "/") + + if len(id) != 2 || id[0] == "" || id[1] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: service/username@zone. Got: %q", req.ID), + ) + } + + serviceName := id[0] + username := id[1] + + var data KafkaUserResourceModel + + // Set timeouts (quirk https://github.com/hashicorp/terraform-plugin-framework-timeouts/issues/46) + var timeouts timeouts.Value + resp.Diagnostics.Append(resp.State.GetAttribute(ctx, path.Root("timeouts"), &timeouts)...) + if resp.Diagnostics.HasError() { + return + } + data.Timeouts = timeouts + + data.Id = types.StringValue(userID) + data.Username = types.StringValue(username) + data.Service = types.StringValue(serviceName) + data.Zone = types.StringValue(zone) + + UserReadForImport(ctx, req, resp, &data, r.client) + +} + +func (data *KafkaUserResourceModel) Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + createRequest := exoscale.CreateDBAASKafkaUserRequest{ + Username: exoscale.DBAASUserUsername(data.Username.ValueString()), + } + + op, err := client.CreateDBAASKafkaUser(ctx, data.Service.ValueString(), createRequest) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to create service user, got error %s", err.Error()), + ) + return + } + + _, err = client.Wait(ctx, op, exoscale.OperationStateSuccess) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to create service user, got error %s", err.Error()), + ) + return + } + + svc, err := client.GetDBAASServiceKafka(ctx, data.Service.ValueString()) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read database service kafka, got error: %s", err)) + return + } + + for _, user := range svc.Users { + if user.Username == data.Username.ValueString() { + data.Password = basetypes.NewStringValue(user.Password) + data.AccessCert = basetypes.NewStringValue(user.AccessCert) + data.AccessKey = basetypes.NewStringValue(user.AccessKey) + data.AccessCertExpiry = basetypes.NewStringValue(user.AccessCertExpiry.String()) + return + } + } + diagnostics.AddError("Client Error", "Unable to find newly created user for the service") +} + +func (data *KafkaUserResourceModel) Delete(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + op, err := client.DeleteDBAASKafkaUser(ctx, data.Service.ValueString(), data.Username.ValueString()) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to delete service user, got error %s", err.Error()), + ) + return + } + + _, err = client.Wait(ctx, op, exoscale.OperationStateSuccess) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to delete service user, got error %s", err.Error()), + ) + return + } + +} + +func (data *KafkaUserResourceModel) Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + svc, err := client.GetDBAASServiceKafka(ctx, data.Service.ValueString()) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read service kafka user, got error: %s", err)) + return + } + + for _, user := range svc.Users { + if user.Username == data.Username.ValueString() { + data.Password = basetypes.NewStringValue(user.Password) + data.AccessCert = basetypes.NewStringValue(user.AccessCert) + data.AccessKey = basetypes.NewStringValue(user.AccessKey) + data.AccessCertExpiry = basetypes.NewStringValue(user.AccessCertExpiry.String()) + return + } + } + diagnostics.AddError("Client Error", "Unable to read user for the service") +} + +func (data *KafkaUserResourceModel) Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + // Nothing to do here as all fields of this resource are immutable; replaces will be required + // automatically +} + +func (data *KafkaUserResourceModel) GetTimeouts() timeouts.Value { + return data.Timeouts +} + +func (data *KafkaUserResourceModel) SetTimeouts(t timeouts.Value) { + data.Timeouts = t +} + +func (data *KafkaUserResourceModel) GetID() basetypes.StringValue { + return data.Id +} +func (data *KafkaUserResourceModel) GetZone() basetypes.StringValue { + return data.Zone +} + +func (data *KafkaUserResourceModel) GenerateID() { + data.Id = basetypes.NewStringValue(fmt.Sprintf("%s/%s", data.Service.ValueString(), data.Username.ValueString())) +} diff --git a/pkg/resources/database/resource_user_mysql.go b/pkg/resources/database/resource_user_mysql.go new file mode 100644 index 000000000..059b883db --- /dev/null +++ b/pkg/resources/database/resource_user_mysql.go @@ -0,0 +1,250 @@ +package database + +import ( + "context" + "fmt" + "strings" + + exoscale "github.com/exoscale/egoscale/v3" + providerConfig "github.com/exoscale/terraform-provider-exoscale/pkg/provider/config" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "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/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var _ resource.Resource = &MysqlUserResource{} +var _ resource.ResourceWithImportState = &MysqlUserResource{} + +func NewMysqlUserResource() resource.Resource { + return &MysqlUserResource{} +} + +type MysqlUserResource struct { + UserResource +} + +type MysqlUserResourceModel struct { + UserResourceModel + Authentication types.String `tfsdk:"authentication"` +} + +func (r *MysqlUserResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + r.client = req.ProviderData.(*providerConfig.ExoscaleProviderConfig).ClientV3 +} + +func (r *MysqlUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_database_mysql_user" +} + +func (r *MysqlUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Manage service users for MySQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/).", + Attributes: buildUserAttributes(map[string]schema.Attribute{ + "authentication": schema.StringAttribute{ + MarkdownDescription: "Authentication details. The possible values are `null`, `caching_sha2_password` and `mysql_native_password`.", + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.OneOf("caching_sha2_password", "mysql_native_password"), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + stringplanmodifier.UseStateForUnknown(), + }, + }, + }), + Blocks: map[string]schema.Block{ + "timeouts": timeouts.BlockAll(ctx), + }, + } +} + +func (r *MysqlUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data MysqlUserResourceModel + UserRead(ctx, req, resp, &data, r.client) +} + +func (r *MysqlUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data MysqlUserResourceModel + UserCreate(ctx, req, resp, &data, r.client) +} + +func (r *MysqlUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var stateData, planData MysqlUserResourceModel + UserUpdate(ctx, req, resp, &stateData, &planData, r.client) +} + +func (r *MysqlUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data MysqlUserResourceModel + UserDelete(ctx, req, resp, &data, r.client) +} + +func (r *MysqlUserResource) 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) != 2 || idParts[0] == "" || idParts[1] == "" { + + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: service/username@zone. Got: %q", req.ID), + ) + + return + } + + userID := idParts[0] + zone := idParts[1] + + id := strings.Split(userID, "/") + + if len(id) != 2 || id[0] == "" || id[1] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: service/username@zone. Got: %q", req.ID), + ) + } + + serviceName := id[0] + username := id[1] + + var data MysqlUserResourceModel + + // Set timeouts (quirk https://github.com/hashicorp/terraform-plugin-framework-timeouts/issues/46) + var timeouts timeouts.Value + resp.Diagnostics.Append(resp.State.GetAttribute(ctx, path.Root("timeouts"), &timeouts)...) + if resp.Diagnostics.HasError() { + return + } + data.Timeouts = timeouts + + data.Id = types.StringValue(userID) + data.Username = types.StringValue(username) + data.Service = types.StringValue(serviceName) + data.Zone = types.StringValue(zone) + + UserReadForImport(ctx, req, resp, &data, r.client) + +} + +func (data *MysqlUserResourceModel) Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + createRequest := exoscale.CreateDBAASMysqlUserRequest{ + Username: exoscale.DBAASUserUsername(data.Username.ValueString()), + } + + if !data.Authentication.IsNull() { + createRequest.Authentication = exoscale.EnumMysqlAuthenticationPlugin(data.Authentication.ValueString()) + + } + + op, err := client.CreateDBAASMysqlUser(ctx, data.Service.ValueString(), createRequest) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to create service user, got error %s", err.Error()), + ) + return + } + + _, err = client.Wait(ctx, op, exoscale.OperationStateSuccess) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to create service user, got error %s", err.Error()), + ) + return + } + + svc, err := client.GetDBAASServiceMysql(ctx, data.Service.ValueString()) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read database service mysql, got error: %s", err)) + return + } + + for _, user := range svc.Users { + if user.Username == data.Username.ValueString() { + data.Password = basetypes.NewStringValue(user.Password) + data.Authentication = basetypes.NewStringValue(user.Authentication) + return + } + } + diagnostics.AddError("Client Error", "Unable to find newly created user for the service") +} + +func (data *MysqlUserResourceModel) Delete(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + op, err := client.DeleteDBAASMysqlUser(ctx, data.Service.ValueString(), data.Username.ValueString()) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to delete service user, got error %s", err.Error()), + ) + return + } + + _, err = client.Wait(ctx, op, exoscale.OperationStateSuccess) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to delete service user, got error %s", err.Error()), + ) + return + } + +} + +func (data *MysqlUserResourceModel) Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + svc, err := client.GetDBAASServiceMysql(ctx, data.Service.ValueString()) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read service mysql user, got error: %s", err)) + return + } + + for _, user := range svc.Users { + if user.Username == data.Username.ValueString() { + data.Password = basetypes.NewStringValue(user.Password) + data.Authentication = basetypes.NewStringValue(user.Authentication) + return + } + } + diagnostics.AddError("Client Error", "Unable to read user for the service") +} + +func (data *MysqlUserResourceModel) Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + // Nothing to do here as all fields of this resource are immutable; replaces will be required + // automatically +} + +func (data *MysqlUserResourceModel) GetTimeouts() timeouts.Value { + return data.Timeouts +} + +func (data *MysqlUserResourceModel) SetTimeouts(t timeouts.Value) { + data.Timeouts = t +} + +func (data *MysqlUserResourceModel) GetID() basetypes.StringValue { + return data.Id +} +func (data *MysqlUserResourceModel) GetZone() basetypes.StringValue { + return data.Zone +} + +func (data *MysqlUserResourceModel) GenerateID() { + data.Id = basetypes.NewStringValue(fmt.Sprintf("%s/%s", data.Service.ValueString(), data.Username.ValueString())) +} diff --git a/pkg/resources/database/resource_user_opensearch.go b/pkg/resources/database/resource_user_opensearch.go new file mode 100644 index 000000000..3b82f28c9 --- /dev/null +++ b/pkg/resources/database/resource_user_opensearch.go @@ -0,0 +1,225 @@ +package database + +import ( + "context" + "fmt" + "strings" + + exoscale "github.com/exoscale/egoscale/v3" + providerConfig "github.com/exoscale/terraform-provider-exoscale/pkg/provider/config" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/diag" + "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/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var _ resource.Resource = &OpensearchUserResource{} +var _ resource.ResourceWithImportState = &OpensearchUserResource{} + +func NewOpensearchUserResource() resource.Resource { + return &OpensearchUserResource{} +} + +type OpensearchUserResource struct { + UserResource +} + +type OpensearchUserResourceModel struct { + UserResourceModel +} + +func (r *OpensearchUserResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + r.client = req.ProviderData.(*providerConfig.ExoscaleProviderConfig).ClientV3 +} + +func (r *OpensearchUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_database_opensearch_user" +} + +func (r *OpensearchUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Manage service users for an Opensearch Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/).", + Attributes: commonAttributes, + Blocks: map[string]schema.Block{ + "timeouts": timeouts.BlockAll(ctx), + }, + } +} + +func (r *OpensearchUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data OpensearchUserResourceModel + UserRead(ctx, req, resp, &data, r.client) +} + +func (r *OpensearchUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data OpensearchUserResourceModel + UserCreate(ctx, req, resp, &data, r.client) +} + +func (r *OpensearchUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var stateData, planData OpensearchUserResourceModel + UserUpdate(ctx, req, resp, &stateData, &planData, r.client) +} + +func (r *OpensearchUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data OpensearchUserResourceModel + UserDelete(ctx, req, resp, &data, r.client) +} + +func (r *OpensearchUserResource) 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) != 2 || idParts[0] == "" || idParts[1] == "" { + + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: service/username@zone. Got: %q", req.ID), + ) + + return + } + + userID := idParts[0] + zone := idParts[1] + + id := strings.Split(userID, "/") + + if len(id) != 2 || id[0] == "" || id[1] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: service/username@zone. Got: %q", req.ID), + ) + } + + serviceName := id[0] + username := id[1] + + var data OpensearchUserResourceModel + + // Set timeouts (quirk https://github.com/hashicorp/terraform-plugin-framework-timeouts/issues/46) + var timeouts timeouts.Value + resp.Diagnostics.Append(resp.State.GetAttribute(ctx, path.Root("timeouts"), &timeouts)...) + if resp.Diagnostics.HasError() { + return + } + data.Timeouts = timeouts + + data.Id = types.StringValue(userID) + data.Username = types.StringValue(username) + data.Service = types.StringValue(serviceName) + data.Zone = types.StringValue(zone) + + UserReadForImport(ctx, req, resp, &data, r.client) + +} + +func (data *OpensearchUserResourceModel) Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + createRequest := exoscale.CreateDBAASOpensearchUserRequest{ + Username: exoscale.DBAASUserUsername(data.Username.ValueString()), + } + + op, err := client.CreateDBAASOpensearchUser(ctx, data.Service.ValueString(), createRequest) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to create service user, got error %s", err.Error()), + ) + return + } + + _, err = client.Wait(ctx, op, exoscale.OperationStateSuccess) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to create service user, got error %s", err.Error()), + ) + return + } + + svc, err := client.GetDBAASServiceOpensearch(ctx, data.Service.ValueString()) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read database service opensearch, got error: %s", err)) + return + } + + for _, user := range svc.Users { + if user.Username == data.Username.ValueString() { + data.Password = basetypes.NewStringValue(user.Password) + return + } + } + diagnostics.AddError("Client Error", "Unable to find newly created user for the service") +} + +func (data *OpensearchUserResourceModel) Delete(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + op, err := client.DeleteDBAASOpensearchUser(ctx, data.Service.ValueString(), data.Username.ValueString()) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to delete service user, got error %s", err.Error()), + ) + return + } + + _, err = client.Wait(ctx, op, exoscale.OperationStateSuccess) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to delete service user, got error %s", err.Error()), + ) + return + } + +} + +func (data *OpensearchUserResourceModel) Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + svc, err := client.GetDBAASServiceOpensearch(ctx, data.Service.ValueString()) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read service opensearch user, got error: %s", err)) + return + } + + for _, user := range svc.Users { + if user.Username == data.Username.ValueString() { + data.Password = basetypes.NewStringValue(user.Password) + return + } + } + diagnostics.AddError("Client Error", "Unable to read user for the service") +} + +func (data *OpensearchUserResourceModel) Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + // Nothing to do here as all fields of this resource are immutable; replaces will be required + // automatically +} + +func (data *OpensearchUserResourceModel) GetTimeouts() timeouts.Value { + return data.Timeouts +} + +func (data *OpensearchUserResourceModel) SetTimeouts(t timeouts.Value) { + data.Timeouts = t +} + +func (data *OpensearchUserResourceModel) GetID() basetypes.StringValue { + return data.Id +} +func (data *OpensearchUserResourceModel) GetZone() basetypes.StringValue { + return data.Zone +} + +func (data *OpensearchUserResourceModel) GenerateID() { + data.Id = basetypes.NewStringValue(fmt.Sprintf("%s/%s", data.Service.ValueString(), data.Username.ValueString())) +} diff --git a/pkg/resources/database/resource_user_pg.go b/pkg/resources/database/resource_user_pg.go new file mode 100644 index 000000000..2ec13adee --- /dev/null +++ b/pkg/resources/database/resource_user_pg.go @@ -0,0 +1,249 @@ +package database + +import ( + "context" + "fmt" + "strings" + + exoscale "github.com/exoscale/egoscale/v3" + providerConfig "github.com/exoscale/terraform-provider-exoscale/pkg/provider/config" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/diag" + "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/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var _ resource.Resource = &PGUserResource{} +var _ resource.ResourceWithImportState = &PGUserResource{} + +func NewPGUserResource() resource.Resource { + return &PGUserResource{} +} + +type PGUserResource struct { + UserResource +} + +type PGUserResourceModel struct { + UserResourceModel + AllowReplication types.Bool `tfsdk:"replication"` +} + +func (r *PGUserResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + r.client = req.ProviderData.(*providerConfig.ExoscaleProviderConfig).ClientV3 +} + +func (r *PGUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_database_pg_user" +} + +func (r *PGUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Manage service users for a PostgreSQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/).", + Attributes: buildUserAttributes(map[string]schema.Attribute{ + "allow_replication": schema.BoolAttribute{ + MarkdownDescription: "Allows replication", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + }, + }), + Blocks: map[string]schema.Block{ + "timeouts": timeouts.BlockAll(ctx), + }, + } +} + +func (r *PGUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data PGUserResourceModel + UserRead(ctx, req, resp, &data, r.client) +} + +func (r *PGUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + + var data PGUserResourceModel + UserCreate(ctx, req, resp, &data, r.client) +} + +func (r *PGUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var stateData, planData PGUserResourceModel + UserUpdate(ctx, req, resp, &stateData, &planData, r.client) +} + +func (r *PGUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data PGUserResourceModel + UserDelete(ctx, req, resp, &data, r.client) +} + +func (r *PGUserResource) 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) != 2 || idParts[0] == "" || idParts[1] == "" { + + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: service/username@zone. Got: %q", req.ID), + ) + + return + } + + userID := idParts[0] + zone := idParts[1] + + id := strings.Split(userID, "/") + + if len(id) != 2 || id[0] == "" || id[1] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: service/username@zone. Got: %q", req.ID), + ) + } + + serviceName := id[0] + username := id[1] + + var data PGUserResourceModel + + // Set timeouts (quirk https://github.com/hashicorp/terraform-plugin-framework-timeouts/issues/46) + var timeouts timeouts.Value + resp.Diagnostics.Append(resp.State.GetAttribute(ctx, path.Root("timeouts"), &timeouts)...) + if resp.Diagnostics.HasError() { + return + } + data.Timeouts = timeouts + + data.Id = types.StringValue(userID) + data.Username = types.StringValue(username) + data.Service = types.StringValue(serviceName) + data.Zone = types.StringValue(zone) + data.Zone = types.StringValue(zone) + + UserReadForImport(ctx, req, resp, &data, r.client) + +} + +func (data *PGUserResourceModel) Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + createRequest := exoscale.CreateDBAASPostgresUserRequest{ + Username: exoscale.DBAASUserUsername(data.Username.ValueString()), + } + + if !data.AllowReplication.IsNull() { + createRequest.AllowReplication = data.AllowReplication.ValueBoolPointer() + } + + op, err := client.CreateDBAASPostgresUser(ctx, data.Service.ValueString(), createRequest) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to create service user, got error %s", err.Error()), + ) + return + } + + _, err = client.Wait(ctx, op, exoscale.OperationStateSuccess) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to create service user, got error %s", err.Error()), + ) + return + } + + svc, err := client.GetDBAASServicePG(ctx, data.Service.ValueString()) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read database service pg, got error: %s", err)) + return + } + + for _, user := range svc.Users { + if user.Username == data.Username.ValueString() { + data.Password = basetypes.NewStringValue(user.Password) + if user.AllowReplication != nil { + data.AllowReplication = basetypes.NewBoolValue(*user.AllowReplication) + } + return + } + } + diagnostics.AddError("Client Error", "Unable to find newly created user for the service") +} + +func (data *PGUserResourceModel) Delete(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + op, err := client.DeleteDBAASPostgresUser(ctx, data.Service.ValueString(), data.Username.ValueString()) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to delete service user, got error %s", err.Error()), + ) + return + } + + _, err = client.Wait(ctx, op, exoscale.OperationStateSuccess) + if err != nil { + diagnostics.AddError( + "Client Error", + fmt.Sprintf("Unable to delete service user, got error %s", err.Error()), + ) + return + } + +} + +func (data *PGUserResourceModel) Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + + svc, err := client.GetDBAASServicePG(ctx, data.Service.ValueString()) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read service pg user, got error: %s", err)) + return + } + + for _, user := range svc.Users { + if user.Username == data.Username.ValueString() { + data.Password = basetypes.NewStringValue(user.Password) + if user.AllowReplication != nil { + data.AllowReplication = basetypes.NewBoolValue(*user.AllowReplication) + } + return + } + } + diagnostics.AddError("Client Error", "Unable to read user for the service") +} + +func (data *PGUserResourceModel) Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + // Nothing to do here as all fields of this resource are immutable; replaces will be required + // automatically +} + +func (data *PGUserResourceModel) GetTimeouts() timeouts.Value { + return data.Timeouts +} + +func (data *PGUserResourceModel) SetTimeouts(t timeouts.Value) { + data.Timeouts = t +} + +func (data *PGUserResourceModel) GetID() basetypes.StringValue { + return data.Id +} +func (data *PGUserResourceModel) GetZone() basetypes.StringValue { + return data.Zone +} + +func (data *PGUserResourceModel) GenerateID() { + data.Id = basetypes.NewStringValue(fmt.Sprintf("%s/%s", data.Service.ValueString(), data.Username.ValueString())) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/attribute.go index 4a0feceec..67294bfc6 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/attribute.go @@ -10,7 +10,10 @@ import ( // Attribute define a value field inside the Schema. Implementations in this // package include: // - BoolAttribute +// - DynamicAttribute +// - Float32Attribute // - Float64Attribute +// - Int32Attribute // - Int64Attribute // - ListAttribute // - MapAttribute diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/map_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/map_attribute.go index d9b701f73..516576a4e 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/map_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/map_attribute.go @@ -23,7 +23,7 @@ var ( _ fwxschema.AttributeWithMapValidators = MapAttribute{} ) -// MapAttribute represents a schema attribute that is a list with a single +// MapAttribute represents a schema attribute that is a map with a single // element type. When retrieving the value for this attribute, use types.Map // as the value type unless the CustomType field is set. The ElementType field // must be set. @@ -32,7 +32,7 @@ var ( // require definition beyond type information. // // Terraform configurations configure this attribute using expressions that -// return a list or directly via curly brace syntax. +// return a map or directly via curly brace syntax. // // # map of strings // example_attribute = { diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/map_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/map_nested_attribute.go index 2f3a60ec5..9729efdc3 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/map_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/map_nested_attribute.go @@ -25,7 +25,7 @@ var ( _ fwxschema.AttributeWithMapValidators = MapNestedAttribute{} ) -// MapNestedAttribute represents an attribute that is a set of objects where +// MapNestedAttribute represents an attribute that is a map of objects where // the object attributes can be fully defined, including further nested // attributes. When retrieving the value for this attribute, use types.Map // as the value type unless the CustomType field is set. The NestedObject field @@ -35,7 +35,7 @@ var ( // not require definition beyond type information. // // Terraform configurations configure this attribute using expressions that -// return a set of objects or directly via curly brace syntax. +// return a map of objects or directly via curly brace syntax. // // # map of objects // example_attribute = { @@ -195,7 +195,7 @@ func (a MapNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { return a.NestedObject } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeMap. func (a MapNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeMap } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/set_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/set_nested_attribute.go index 860ab4c96..1b17b8743 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/set_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/set_nested_attribute.go @@ -190,7 +190,7 @@ func (a SetNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { return a.NestedObject } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeSet. func (a SetNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeSet } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/single_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/single_nested_attribute.go index 21c9f3231..811e76de4 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/single_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/datasource/schema/single_nested_attribute.go @@ -198,7 +198,7 @@ func (a SingleNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject } } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeSingle. func (a SingleNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeSingle } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/close.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/close.go new file mode 100644 index 000000000..3d1745853 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/close.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package ephemeral + +import ( + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" +) + +// CloseRequest represents a request for the provider to close an ephemeral +// resource. An instance of this request struct is supplied as an argument to +// the ephemeral resource's Close function. +type CloseRequest struct { + // Private is provider-defined ephemeral resource private state data + // which was previously provided by the latest Open or Renew operation. + // + // Use the GetKey method to read data. + Private *privatestate.ProviderData +} + +// CloseResponse represents a response to a CloseRequest. An +// instance of this response struct is supplied as an argument +// to the ephemeral resource's Close function, in which the provider +// should set values on the CloseResponse as appropriate. +type CloseResponse struct { + // Diagnostics report errors or warnings related to closing the + // resource. An empty slice indicates a successful operation with no + // warnings or errors generated. + Diagnostics diag.Diagnostics +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/config_validator.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/config_validator.go new file mode 100644 index 000000000..782718d8d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/config_validator.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package ephemeral + +import "context" + +// ConfigValidator describes reusable EphemeralResource configuration validation functionality. +type ConfigValidator interface { + // Description describes the validation in plain text formatting. + // + // This information may be automatically added to ephemeral resource plain text + // descriptions by external tooling. + Description(context.Context) string + + // MarkdownDescription describes the validation in Markdown formatting. + // + // This information may be automatically added to ephemeral resource Markdown + // descriptions by external tooling. + MarkdownDescription(context.Context) string + + // ValidateEphemeralResource performs the validation. + // + // This method name is separate from the datasource.ConfigValidator + // interface ValidateDataSource method name, provider.ConfigValidator + // interface ValidateProvider method name, and resource.ConfigValidator + // interface ValidateResource method name to allow generic validators. + ValidateEphemeralResource(context.Context, ValidateConfigRequest, *ValidateConfigResponse) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/configure.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/configure.go new file mode 100644 index 000000000..bcf6dd908 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/configure.go @@ -0,0 +1,33 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package ephemeral + +import ( + "github.com/hashicorp/terraform-plugin-framework/diag" +) + +// ConfigureRequest represents a request for the provider to configure an +// ephemeral resource, i.e., set provider-level data or clients. An instance of +// this request struct is supplied as an argument to the EphemeralResource type +// Configure method. +type ConfigureRequest struct { + // ProviderData is the data set in the + // [provider.ConfigureResponse.EphemeralResourceData] field. This data is + // provider-specifc and therefore can contain any necessary remote system + // clients, custom provider data, or anything else pertinent to the + // functionality of the EphemeralResource. + // + // This data is only set after the ConfigureProvider RPC has been called + // by Terraform. + ProviderData any +} + +// ConfigureResponse represents a response to a ConfigureRequest. An +// instance of this response struct is supplied as an argument to the +// EphemeralResource type Configure method. +type ConfigureResponse struct { + // Diagnostics report errors or warnings related to configuring of the + // EphemeralResource. An empty slice indicates a successful operation with no + // warnings or errors generated. + Diagnostics diag.Diagnostics +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/deferred.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/deferred.go new file mode 100644 index 000000000..d4773a10a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/deferred.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ephemeral + +const ( + // DeferredReasonUnknown is used to indicate an invalid `DeferredReason`. + // Provider developers should not use it. + DeferredReasonUnknown DeferredReason = 0 + + // DeferredReasonEphemeralResourceConfigUnknown is used to indicate that the resource configuration + // is partially unknown and the real values need to be known before the change can be planned. + DeferredReasonEphemeralResourceConfigUnknown DeferredReason = 1 + + // DeferredReasonProviderConfigUnknown is used to indicate that the provider configuration + // is partially unknown and the real values need to be known before the change can be planned. + DeferredReasonProviderConfigUnknown DeferredReason = 2 + + // DeferredReasonAbsentPrereq is used to indicate that a hard dependency has not been satisfied. + DeferredReasonAbsentPrereq DeferredReason = 3 +) + +// Deferred is used to indicate to Terraform that a change needs to be deferred for a reason. +// +// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject +// to change or break without warning. It is not protected by version compatibility guarantees. +type Deferred struct { + // Reason is the reason for deferring the change. + Reason DeferredReason +} + +// DeferredReason represents different reasons for deferring a change. +// +// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject +// to change or break without warning. It is not protected by version compatibility guarantees. +type DeferredReason int32 + +func (d DeferredReason) String() string { + switch d { + case 0: + return "Unknown" + case 1: + return "Ephemeral Resource Config Unknown" + case 2: + return "Provider Config Unknown" + case 3: + return "Absent Prerequisite" + } + return "Unknown" +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/doc.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/doc.go new file mode 100644 index 000000000..2537d610c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/doc.go @@ -0,0 +1,26 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package ephemeral contains all interfaces, request types, and response +// types for an ephemeral resource implementation. +// +// In Terraform, an ephemeral resource is a concept which enables provider +// developers to offer practitioners ephemeral values, which will not be stored +// in any artifact produced by Terraform (plan/state). Ephemeral resources can +// optionally implement renewal logic via the (EphemeralResource).Renew method +// and cleanup logic via the (EphemeralResource).Close method. +// +// Ephemeral resources are not saved into the Terraform plan or state and can +// only be referenced in other ephemeral values, such as provider configuration +// attributes. Ephemeral resources are defined by a type/name, such as "examplecloud_thing", +// a schema representing the structure and data types of configuration, and lifecycle logic. +// +// The main starting point for implementations in this package is the +// EphemeralResource type which represents an instance of an ephemeral resource +// that has its own configuration and lifecycle logic. The [ephemeral.EphemeralResource] +// implementations are referenced by the [provider.ProviderWithEphemeralResources] type +// EphemeralResources method, which enables the ephemeral resource practitioner usage. +// +// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until +// these notices are removed. +package ephemeral diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/ephemeral_resource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/ephemeral_resource.go new file mode 100644 index 000000000..1261de52d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/ephemeral_resource.go @@ -0,0 +1,108 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package ephemeral + +import ( + "context" +) + +// EphemeralResource represents an instance of an ephemeral resource type. This is the core +// interface that all ephemeral resources must implement. +// +// Ephemeral resources can optionally implement these additional concepts: +// +// - Configure: Include provider-level data or clients via EphemeralResourceWithConfigure +// +// - Validation: Schema-based or entire configuration via EphemeralResourceWithConfigValidators +// or EphemeralResourceWithValidateConfig. +// +// - Renew: Handle renewal of an expired remote object via EphemeralResourceWithRenew. +// Ephemeral resources can indicate to Terraform when a renewal must occur via the RenewAt +// response field of the Open/Renew methods. Renew cannot return new result data for the +// ephemeral resource instance, so this logic is only appropriate for remote objects like +// HashiCorp Vault leases, which can be renewed without changing their data. +// +// - Close: Allows providers to clean up the ephemeral resource via EphemeralResourceWithClose. +// +// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until +// these notices are removed. +type EphemeralResource interface { + // Metadata should return the full name of the ephemeral resource, such as + // examplecloud_thing. + Metadata(context.Context, MetadataRequest, *MetadataResponse) + + // Schema should return the schema for this ephemeral resource. + Schema(context.Context, SchemaRequest, *SchemaResponse) + + // Open is called when the provider must generate a new ephemeral resource. Config values + // should be read from the OpenRequest and new response values set on the OpenResponse. + Open(context.Context, OpenRequest, *OpenResponse) +} + +// EphemeralResourceWithRenew is an interface type that extends EphemeralResource to +// include a method which the framework will call when Terraform detects that the +// provider-defined returned RenewAt time for an ephemeral resource has passed. This RenewAt +// response field can be set in the OpenResponse and RenewResponse. +type EphemeralResourceWithRenew interface { + EphemeralResource + + // Renew is called when the provider must renew the ephemeral resource based on + // the provided RenewAt time. This RenewAt response field can be set in the OpenResponse and RenewResponse. + // + // Renew cannot return new result data for the ephemeral resource instance, so this logic is only appropriate + // for remote objects like HashiCorp Vault leases, which can be renewed without changing their data. + Renew(context.Context, RenewRequest, *RenewResponse) +} + +// EphemeralResourceWithClose is an interface type that extends +// EphemeralResource to include a method which the framework will call when +// Terraform determines that the ephemeral resource can be safely cleaned up. +type EphemeralResourceWithClose interface { + EphemeralResource + + // Close is called when the provider can clean up the ephemeral resource. + // Config values may be read from the CloseRequest. + Close(context.Context, CloseRequest, *CloseResponse) +} + +// EphemeralResourceWithConfigure is an interface type that extends EphemeralResource to +// include a method which the framework will automatically call so provider +// developers have the opportunity to setup any necessary provider-level data +// or clients in the EphemeralResource type. +type EphemeralResourceWithConfigure interface { + EphemeralResource + + // Configure enables provider-level data or clients to be set in the + // provider-defined EphemeralResource type. + Configure(context.Context, ConfigureRequest, *ConfigureResponse) +} + +// EphemeralResourceWithConfigValidators is an interface type that extends EphemeralResource to include declarative validations. +// +// Declaring validation using this methodology simplifies implementation of +// reusable functionality. These also include descriptions, which can be used +// for automating documentation. +// +// Validation will include ConfigValidators and ValidateConfig, if both are +// implemented, in addition to any Attribute or Type validation. +type EphemeralResourceWithConfigValidators interface { + EphemeralResource + + // ConfigValidators returns a list of functions which will all be performed during validation. + ConfigValidators(context.Context) []ConfigValidator +} + +// EphemeralResourceWithValidateConfig is an interface type that extends EphemeralResource to include imperative validation. +// +// Declaring validation using this methodology simplifies one-off +// functionality that typically applies to a single ephemeral resource. Any documentation +// of this functionality must be manually added into schema descriptions. +// +// Validation will include ConfigValidators and ValidateConfig, if both are +// implemented, in addition to any Attribute or Type validation. +type EphemeralResourceWithValidateConfig interface { + EphemeralResource + + // ValidateConfig performs the validation. + ValidateConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/metadata.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/metadata.go new file mode 100644 index 000000000..ed97522b8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/metadata.go @@ -0,0 +1,23 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package ephemeral + +// MetadataRequest represents a request for the EphemeralResource to return metadata, +// such as its type name. An instance of this request struct is supplied as +// an argument to the EphemeralResource type Metadata method. +type MetadataRequest struct { + // ProviderTypeName is the string returned from + // [provider.MetadataResponse.TypeName], if the Provider type implements + // the Metadata method. This string should prefix the EphemeralResource type name + // with an underscore in the response. + ProviderTypeName string +} + +// MetadataResponse represents a response to a MetadataRequest. An +// instance of this response struct is supplied as an argument to the +// EphemeralResource type Metadata method. +type MetadataResponse struct { + // TypeName should be the full ephemeral resource type, including the provider + // type prefix and an underscore. For example, examplecloud_thing. + TypeName string +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/open.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/open.go new file mode 100644 index 000000000..5da47ee1c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/open.go @@ -0,0 +1,80 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package ephemeral + +import ( + "time" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +// OpenClientCapabilities allows Terraform to publish information +// regarding optionally supported protocol features for the OpenEphemeralResource RPC, +// such as forward-compatible Terraform behavior changes. +type OpenClientCapabilities struct { + // DeferralAllowed indicates whether the Terraform client initiating + // the request allows a deferral response. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. + DeferralAllowed bool +} + +// OpenRequest represents a request for the provider to open an ephemeral +// resource. An instance of this request struct is supplied as an argument to +// the ephemeral resource's Open function. +type OpenRequest struct { + // Config is the configuration the user supplied for the ephemeral + // resource. + Config tfsdk.Config + + // ClientCapabilities defines optionally supported protocol features for the + // OpenEphemeralResource RPC, such as forward-compatible Terraform behavior changes. + ClientCapabilities OpenClientCapabilities +} + +// OpenResponse represents a response to a OpenRequest. An +// instance of this response struct is supplied as an argument +// to the ephemeral resource's Open function, in which the provider +// should set values on the OpenResponse as appropriate. +type OpenResponse struct { + // Result is the object representing the values of the ephemeral + // resource following the Open operation. This field is pre-populated + // from OpenRequest.Config and should be set during the resource's Open + // operation. + Result tfsdk.EphemeralResultData + + // Private is the private state ephemeral resource data following the + // Open operation. This field is not pre-populated as there is no + // pre-existing private state data during the ephemeral resource's + // Open operation. + // + // This private data will be passed to any Renew or Close operations. + Private *privatestate.ProviderData + + // RenewAt is an optional date/time field that indicates to Terraform + // when this ephemeral resource must be renewed at. Terraform will call + // the (EphemeralResource).Renew method when the current date/time is on + // or after RenewAt during a Terraform operation. + // + // It is recommended to add extra time (usually no more than a few minutes) + // before an ephemeral resource expires to account for latency. + RenewAt time.Time + + // Diagnostics report errors or warnings related to opening the ephemeral + // resource. An empty slice indicates a successful operation with no + // warnings or errors generated. + Diagnostics diag.Diagnostics + + // Deferred indicates that Terraform should defer opening this + // ephemeral resource until a followup apply operation. + // + // This field can only be set if + // `(ephemeral.OpenRequest).ClientCapabilities.DeferralAllowed` is true. + // + // NOTE: This functionality is related to deferred action support, which is currently experimental and is subject + // to change or break without warning. It is not protected by version compatibility guarantees. + Deferred *Deferred +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/renew.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/renew.go new file mode 100644 index 000000000..dd7f0c8e3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/renew.go @@ -0,0 +1,51 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package ephemeral + +import ( + "time" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" +) + +// RenewRequest represents a request for the provider to renew an ephemeral +// resource. An instance of this request struct is supplied as an argument to +// the ephemeral resource's Renew function. +type RenewRequest struct { + // Private is provider-defined ephemeral resource private state data + // which was previously provided by the latest Open or Renew operation. + // Any existing data is copied to RenewResponse.Private to prevent + // accidental private state data loss. + // + // Use the GetKey method to read data. Use the SetKey method on + // RenewResponse.Private to update or remove a value. + Private *privatestate.ProviderData +} + +// RenewResponse represents a response to a RenewRequest. An +// instance of this response struct is supplied as an argument +// to the ephemeral resource's Renew function, in which the provider +// should set values on the RenewResponse as appropriate. +type RenewResponse struct { + // RenewAt is an optional date/time field that indicates to Terraform + // when this ephemeral resource must be renewed at. Terraform will call + // the (EphemeralResource).Renew method when the current date/time is on + // or after RenewAt during a Terraform operation. + // + // It is recommended to add extra time (usually no more than a few minutes) + // before an ephemeral resource expires to account for latency. + RenewAt time.Time + + // Private is the private state ephemeral resource data following the + // Renew operation. This field is pre-populated from RenewRequest.Private + // and can be modified during the ephemeral resource's Renew operation. + // + // This private data will be passed to any Renew or Close operations. + Private *privatestate.ProviderData + + // Diagnostics report errors or warnings related to renewing the ephemeral + // resource. An empty slice indicates a successful operation with no + // warnings or errors generated. + Diagnostics diag.Diagnostics +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema.go new file mode 100644 index 000000000..c49067975 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema.go @@ -0,0 +1,26 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package ephemeral + +import ( + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral/schema" +) + +// SchemaRequest represents a request for the EphemeralResource to return its schema. +// An instance of this request struct is supplied as an argument to the +// EphemeralResource type Schema method. +type SchemaRequest struct{} + +// SchemaResponse represents a response to a SchemaRequest. An instance of this +// response struct is supplied as an argument to the EphemeralResource type Schema +// method. +type SchemaResponse struct { + // Schema is the schema of the ephemeral resource. + Schema schema.Schema + + // Diagnostics report errors or warnings related to retrieving the ephemeral + // resource schema. An empty slice indicates success, with no warnings + // or errors generated. + Diagnostics diag.Diagnostics +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/attribute.go new file mode 100644 index 000000000..8b6ebe60b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/attribute.go @@ -0,0 +1,39 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" +) + +// Attribute defines a value field inside the Schema. Implementations in this +// package include: +// - BoolAttribute +// - DynamicAttribute +// - Float32Attribute +// - Float64Attribute +// - Int32Attribute +// - Int64Attribute +// - ListAttribute +// - MapAttribute +// - NumberAttribute +// - ObjectAttribute +// - SetAttribute +// - StringAttribute +// +// Additionally, the NestedAttribute interface extends Attribute with nested +// attributes. Only supported in protocol version 6. Implementations in this +// package include: +// - ListNestedAttribute +// - MapNestedAttribute +// - SetNestedAttribute +// - SingleNestedAttribute +// +// In practitioner configurations, an equals sign (=) is required to set +// the value. [Configuration Reference] +// +// [Configuration Reference]: https://developer.hashicorp.com/terraform/language/syntax/configuration +type Attribute interface { + fwschema.Attribute +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/block.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/block.go new file mode 100644 index 000000000..f741d8f8e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/block.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" +) + +// Block defines a structural field inside a Schema. Implementations in this +// package include: +// - ListNestedBlock +// - SetNestedBlock +// - SingleNestedBlock +// +// In practitioner configurations, an equals sign (=) cannot be used to set the +// value. Blocks are instead repeated as necessary, or require the use of +// [Dynamic Block Expressions]. +// +// Prefer NestedAttribute over Block. Blocks should typically be used for +// configuration compatibility with previously existing schemas from an older +// Terraform Plugin SDK. Efforts should be made to convert from Block to +// NestedAttribute as a breaking change for practitioners. +// +// [Dynamic Block Expressions]: https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks +// +// [Configuration Reference]: https://developer.hashicorp.com/terraform/language/syntax/configuration +type Block interface { + fwschema.Block +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/bool_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/bool_attribute.go new file mode 100644 index 000000000..47e248aae --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/bool_attribute.go @@ -0,0 +1,185 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = BoolAttribute{} + _ fwxschema.AttributeWithBoolValidators = BoolAttribute{} +) + +// BoolAttribute represents a schema attribute that is a boolean. When +// retrieving the value for this attribute, use types.Bool as the value type +// unless the CustomType field is set. +// +// Terraform configurations configure this attribute using expressions that +// return a boolean or directly via the true/false keywords. +// +// example_attribute = true +// +// Terraform configurations reference this attribute using the attribute name. +// +// .example_attribute +type BoolAttribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.BoolType. When retrieving data, the basetypes.BoolValuable + // associated with this custom type must be used in place of types.Bool. + CustomType basetypes.BoolTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Bool +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a BoolAttribute. +func (a BoolAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// BoolValidators returns the Validators field value. +func (a BoolAttribute) BoolValidators() []validator.Bool { + return a.Validators +} + +// Equal returns true if the given Attribute is a BoolAttribute +// and all fields are equal. +func (a BoolAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(BoolAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a BoolAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a BoolAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a BoolAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.StringType or the CustomType field value if defined. +func (a BoolAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.BoolType +} + +// IsComputed returns the Computed field value. +func (a BoolAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a BoolAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a BoolAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a BoolAttribute) IsSensitive() bool { + return a.Sensitive +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/doc.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/doc.go new file mode 100644 index 000000000..93c0835e2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/doc.go @@ -0,0 +1,11 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package schema contains all available schema functionality for ephemeral resources. +// Ephemeral resource schemas define the structure and value types for configuration +// and result data. Schemas are implemented via the ephemeral.EphemeralResource type +// Schema method. +// +// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until +// these notices are removed. +package schema diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/dynamic_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/dynamic_attribute.go new file mode 100644 index 000000000..5088fef63 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/dynamic_attribute.go @@ -0,0 +1,186 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = DynamicAttribute{} + _ fwxschema.AttributeWithDynamicValidators = DynamicAttribute{} +) + +// DynamicAttribute represents a schema attribute that is a dynamic, rather +// than a single static type. Static types are always preferable over dynamic +// types in Terraform as practitioners will receive less helpful configuration +// assistance from validation error diagnostics and editor integrations. When +// retrieving the value for this attribute, use types.Dynamic as the value type +// unless the CustomType field is set. +// +// The concrete value type for a dynamic is determined at runtime in this order: +// 1. By Terraform, if defined in the configuration (if Required or Optional). +// 2. By the provider (if Computed). +// +// Once the concrete value type has been determined, it must remain consistent between +// plan and apply or Terraform will return an error. +type DynamicAttribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.DynamicType. When retrieving data, the basetypes.DynamicValuable + // associated with this custom type must be used in place of types.Dynamic. + CustomType basetypes.DynamicTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Dynamic +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a DynamicAttribute. +func (a DynamicAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a DynamicAttribute +// and all fields are equal. +func (a DynamicAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(DynamicAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a DynamicAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a DynamicAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a DynamicAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.DynamicType or the CustomType field value if defined. +func (a DynamicAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.DynamicType +} + +// IsComputed returns the Computed field value. +func (a DynamicAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a DynamicAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a DynamicAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a DynamicAttribute) IsSensitive() bool { + return a.Sensitive +} + +// DynamicValidators returns the Validators field value. +func (a DynamicAttribute) DynamicValidators() []validator.Dynamic { + return a.Validators +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/float32_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/float32_attribute.go new file mode 100644 index 000000000..93ce2ac7d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/float32_attribute.go @@ -0,0 +1,189 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = Float32Attribute{} + _ fwxschema.AttributeWithFloat32Validators = Float32Attribute{} +) + +// Float32Attribute represents a schema attribute that is a 32-bit floating +// point number. When retrieving the value for this attribute, use +// types.Float32 as the value type unless the CustomType field is set. +// +// Use Int32Attribute for 32-bit integer attributes or NumberAttribute for +// 512-bit generic number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via a floating point value. +// +// example_attribute = 123.45 +// +// Terraform configurations reference this attribute using the attribute name. +// +// .example_attribute +type Float32Attribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.Float32Type. When retrieving data, the basetypes.Float32Valuable + // associated with this custom type must be used in place of types.Float32. + CustomType basetypes.Float32Typable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Float32 +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a Float32Attribute. +func (a Float32Attribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a Float32Attribute +// and all fields are equal. +func (a Float32Attribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(Float32Attribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// Float32Validators returns the Validators field value. +func (a Float32Attribute) Float32Validators() []validator.Float32 { + return a.Validators +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a Float32Attribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a Float32Attribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a Float32Attribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.Float32Type or the CustomType field value if defined. +func (a Float32Attribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.Float32Type +} + +// IsComputed returns the Computed field value. +func (a Float32Attribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a Float32Attribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a Float32Attribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a Float32Attribute) IsSensitive() bool { + return a.Sensitive +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/float64_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/float64_attribute.go new file mode 100644 index 000000000..ff1912d1e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/float64_attribute.go @@ -0,0 +1,188 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = Float64Attribute{} + _ fwxschema.AttributeWithFloat64Validators = Float64Attribute{} +) + +// Float64Attribute represents a schema attribute that is a 64-bit floating +// point number. When retrieving the value for this attribute, use +// types.Float64 as the value type unless the CustomType field is set. +// +// Use Int64Attribute for 64-bit integer attributes or NumberAttribute for +// 512-bit generic number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via a floating point value. +// +// example_attribute = 123.45 +// +// Terraform configurations reference this attribute using the attribute name. +// +// .example_attribute +type Float64Attribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.Float64Type. When retrieving data, the basetypes.Float64Valuable + // associated with this custom type must be used in place of types.Float64. + CustomType basetypes.Float64Typable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Float64 +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a Float64Attribute. +func (a Float64Attribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a Float64Attribute +// and all fields are equal. +func (a Float64Attribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(Float64Attribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// Float64Validators returns the Validators field value. +func (a Float64Attribute) Float64Validators() []validator.Float64 { + return a.Validators +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a Float64Attribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a Float64Attribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a Float64Attribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.Float64Type or the CustomType field value if defined. +func (a Float64Attribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.Float64Type +} + +// IsComputed returns the Computed field value. +func (a Float64Attribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a Float64Attribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a Float64Attribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a Float64Attribute) IsSensitive() bool { + return a.Sensitive +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/int32_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/int32_attribute.go new file mode 100644 index 000000000..f98cbe3b0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/int32_attribute.go @@ -0,0 +1,189 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = Int32Attribute{} + _ fwxschema.AttributeWithInt32Validators = Int32Attribute{} +) + +// Int32Attribute represents a schema attribute that is a 32-bit integer. +// When retrieving the value for this attribute, use types.Int32 as the value +// type unless the CustomType field is set. +// +// Use Float32Attribute for 32-bit floating point number attributes or +// NumberAttribute for 512-bit generic number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via an integer value. +// +// example_attribute = 123 +// +// Terraform configurations reference this attribute using the attribute name. +// +// .example_attribute +type Int32Attribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.Int32Type. When retrieving data, the basetypes.Int32Valuable + // associated with this custom type must be used in place of types.Int32. + CustomType basetypes.Int32Typable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Int32 +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a Int32Attribute. +func (a Int32Attribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a Int32Attribute +// and all fields are equal. +func (a Int32Attribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(Int32Attribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a Int32Attribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a Int32Attribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a Int32Attribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.Int32Type or the CustomType field value if defined. +func (a Int32Attribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.Int32Type +} + +// Int32Validators returns the Validators field value. +func (a Int32Attribute) Int32Validators() []validator.Int32 { + return a.Validators +} + +// IsComputed returns the Computed field value. +func (a Int32Attribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a Int32Attribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a Int32Attribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a Int32Attribute) IsSensitive() bool { + return a.Sensitive +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/int64_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/int64_attribute.go new file mode 100644 index 000000000..52b1b7ffd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/int64_attribute.go @@ -0,0 +1,188 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = Int64Attribute{} + _ fwxschema.AttributeWithInt64Validators = Int64Attribute{} +) + +// Int64Attribute represents a schema attribute that is a 64-bit integer. +// When retrieving the value for this attribute, use types.Int64 as the value +// type unless the CustomType field is set. +// +// Use Float64Attribute for 64-bit floating point number attributes or +// NumberAttribute for 512-bit generic number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via an integer value. +// +// example_attribute = 123 +// +// Terraform configurations reference this attribute using the attribute name. +// +// .example_attribute +type Int64Attribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.Int64Type. When retrieving data, the basetypes.Int64Valuable + // associated with this custom type must be used in place of types.Int64. + CustomType basetypes.Int64Typable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Int64 +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a Int64Attribute. +func (a Int64Attribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a Int64Attribute +// and all fields are equal. +func (a Int64Attribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(Int64Attribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a Int64Attribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a Int64Attribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a Int64Attribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.Int64Type or the CustomType field value if defined. +func (a Int64Attribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.Int64Type +} + +// Int64Validators returns the Validators field value. +func (a Int64Attribute) Int64Validators() []validator.Int64 { + return a.Validators +} + +// IsComputed returns the Computed field value. +func (a Int64Attribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a Int64Attribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a Int64Attribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a Int64Attribute) IsSensitive() bool { + return a.Sensitive +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_attribute.go new file mode 100644 index 000000000..3846316b6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_attribute.go @@ -0,0 +1,220 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = ListAttribute{} + _ fwschema.AttributeWithValidateImplementation = ListAttribute{} + _ fwxschema.AttributeWithListValidators = ListAttribute{} +) + +// ListAttribute represents a schema attribute that is a list with a single +// element type. When retrieving the value for this attribute, use types.List +// as the value type unless the CustomType field is set. The ElementType field +// must be set. +// +// Use ListNestedAttribute if the underlying elements should be objects and +// require definition beyond type information. +// +// Terraform configurations configure this attribute using expressions that +// return a list or directly via square brace syntax. +// +// # list of strings +// example_attribute = ["first", "second"] +// +// Terraform configurations reference this attribute using expressions that +// accept a list or an element directly via square brace 0-based index syntax: +// +// # first known element +// .example_attribute[0] +type ListAttribute struct { + // ElementType is the type for all elements of the list. This field must be + // set. + // + // Element types that contain a dynamic type (i.e. types.Dynamic) are not supported. + // If underlying dynamic values are required, replace this attribute definition with + // DynamicAttribute instead. + ElementType attr.Type + + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.ListType. When retrieving data, the basetypes.ListValuable + // associated with this custom type must be used in place of types.List. + CustomType basetypes.ListTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.List +} + +// ApplyTerraform5AttributePathStep returns the result of stepping into a list +// index or an error. +func (a ListAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a ListAttribute +// and all fields are equal. +func (a ListAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(ListAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a ListAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a ListAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a ListAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.ListType or the CustomType field value if defined. +func (a ListAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.ListType{ + ElemType: a.ElementType, + } +} + +// IsComputed returns the Computed field value. +func (a ListAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a ListAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a ListAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a ListAttribute) IsSensitive() bool { + return a.Sensitive +} + +// ListValidators returns the Validators field value. +func (a ListAttribute) ListValidators() []validator.List { + return a.Validators +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the attribute to prevent unexpected +// errors or panics. This logic runs during the GetProviderSchema RPC +// and should never include false positives. +func (a ListAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if a.CustomType == nil && a.ElementType == nil { + resp.Diagnostics.Append(fwschema.AttributeMissingElementTypeDiag(req.Path)) + } + + if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) { + resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_nested_attribute.go new file mode 100644 index 000000000..4a91f2742 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_nested_attribute.go @@ -0,0 +1,244 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ NestedAttribute = ListNestedAttribute{} + _ fwschema.AttributeWithValidateImplementation = ListNestedAttribute{} + _ fwxschema.AttributeWithListValidators = ListNestedAttribute{} +) + +// ListNestedAttribute represents an attribute that is a list of objects where +// the object attributes can be fully defined, including further nested +// attributes. When retrieving the value for this attribute, use types.List +// as the value type unless the CustomType field is set. The NestedObject field +// must be set. Nested attributes are only compatible with protocol version 6. +// +// Use ListAttribute if the underlying elements are of a single type and do +// not require definition beyond type information. +// +// Terraform configurations configure this attribute using expressions that +// return a list of objects or directly via square and curly brace syntax. +// +// # list of objects +// example_attribute = [ +// { +// nested_attribute = #... +// }, +// ] +// +// Terraform configurations reference this attribute using expressions that +// accept a list of objects or an element directly via square brace 0-based +// index syntax: +// +// # first known object +// .example_attribute[0] +// # first known object nested_attribute value +// .example_attribute[0].nested_attribute +type ListNestedAttribute struct { + // NestedObject is the underlying object that contains nested attributes. + // This field must be set. + // + // Nested attributes that contain a dynamic type (i.e. DynamicAttribute) are not supported. + // If underlying dynamic values are required, replace this attribute definition with + // DynamicAttribute instead. + NestedObject NestedAttributeObject + + // CustomType enables the use of a custom attribute type in place of the + // default types.ListType of types.ObjectType. When retrieving data, the + // basetypes.ListValuable associated with this custom type must be used in + // place of types.List. + CustomType basetypes.ListTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.List +} + +// ApplyTerraform5AttributePathStep returns the Attributes field value if step +// is ElementKeyInt, otherwise returns an error. +func (a ListNestedAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + _, ok := step.(tftypes.ElementKeyInt) + + if !ok { + return nil, fmt.Errorf("cannot apply step %T to ListNestedAttribute", step) + } + + return a.NestedObject, nil +} + +// Equal returns true if the given Attribute is a ListNestedAttribute +// and all fields are equal. +func (a ListNestedAttribute) Equal(o fwschema.Attribute) bool { + other, ok := o.(ListNestedAttribute) + + if !ok { + return false + } + + return fwschema.NestedAttributesEqual(a, other) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a ListNestedAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a ListNestedAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a ListNestedAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetNestedObject returns the NestedObject field value. +func (a ListNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { + return a.NestedObject +} + +// GetNestingMode always returns NestingModeList. +func (a ListNestedAttribute) GetNestingMode() fwschema.NestingMode { + return fwschema.NestingModeList +} + +// GetType returns ListType of ObjectType or CustomType. +func (a ListNestedAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.ListType{ + ElemType: a.NestedObject.Type(), + } +} + +// IsComputed returns the Computed field value. +func (a ListNestedAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a ListNestedAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a ListNestedAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a ListNestedAttribute) IsSensitive() bool { + return a.Sensitive +} + +// ListValidators returns the Validators field value. +func (a ListNestedAttribute) ListValidators() []validator.List { + return a.Validators +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the attribute to prevent unexpected +// errors or panics. This logic runs during the GetProviderSchema RPC and +// should never include false positives. +func (a ListNestedAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) { + resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_nested_block.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_nested_block.go new file mode 100644 index 000000000..4d098bc2d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/list_nested_block.go @@ -0,0 +1,205 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Block = ListNestedBlock{} + _ fwschema.BlockWithValidateImplementation = ListNestedBlock{} + _ fwxschema.BlockWithListValidators = ListNestedBlock{} +) + +// ListNestedBlock represents a block that is a list of objects where +// the object attributes can be fully defined, including further attributes +// or blocks. When retrieving the value for this block, use types.List +// as the value type unless the CustomType field is set. The NestedObject field +// must be set. +// +// Prefer ListNestedAttribute over ListNestedBlock if the provider is +// using protocol version 6. Nested attributes allow practitioners to configure +// values directly with expressions. +// +// Terraform configurations configure this block repeatedly using curly brace +// syntax without an equals (=) sign or [Dynamic Block Expressions]. +// +// # list of blocks with two elements +// example_block { +// nested_attribute = #... +// } +// example_block { +// nested_attribute = #... +// } +// +// Terraform configurations reference this block using expressions that +// accept a list of objects or an element directly via square brace 0-based +// index syntax: +// +// # first known object +// .example_block[0] +// # first known object nested_attribute value +// .example_block[0].nested_attribute +// +// [Dynamic Block Expressions]: https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks +type ListNestedBlock struct { + // NestedObject is the underlying object that contains nested attributes or + // blocks. This field must be set. + // + // Nested attributes that contain a dynamic type (i.e. DynamicAttribute) are not supported. + // If underlying dynamic values are required, replace this block definition with + // a DynamicAttribute. + NestedObject NestedBlockObject + + // CustomType enables the use of a custom attribute type in place of the + // default types.ListType of types.ObjectType. When retrieving data, the + // basetypes.ListValuable associated with this custom type must be used in + // place of types.List. + CustomType basetypes.ListTypable + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.List +} + +// ApplyTerraform5AttributePathStep returns the NestedObject field value if step +// is ElementKeyInt, otherwise returns an error. +func (b ListNestedBlock) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + _, ok := step.(tftypes.ElementKeyInt) + + if !ok { + return nil, fmt.Errorf("cannot apply step %T to ListNestedBlock", step) + } + + return b.NestedObject, nil +} + +// Equal returns true if the given Block is ListNestedBlock +// and all fields are equal. +func (b ListNestedBlock) Equal(o fwschema.Block) bool { + if _, ok := o.(ListNestedBlock); !ok { + return false + } + + return fwschema.BlocksEqual(b, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (b ListNestedBlock) GetDeprecationMessage() string { + return b.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (b ListNestedBlock) GetDescription() string { + return b.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (b ListNestedBlock) GetMarkdownDescription() string { + return b.MarkdownDescription +} + +// GetNestedObject returns the NestedObject field value. +func (b ListNestedBlock) GetNestedObject() fwschema.NestedBlockObject { + return b.NestedObject +} + +// GetNestingMode always returns BlockNestingModeList. +func (b ListNestedBlock) GetNestingMode() fwschema.BlockNestingMode { + return fwschema.BlockNestingModeList +} + +// ListValidators returns the Validators field value. +func (b ListNestedBlock) ListValidators() []validator.List { + return b.Validators +} + +// Type returns ListType of ObjectType or CustomType. +func (b ListNestedBlock) Type() attr.Type { + if b.CustomType != nil { + return b.CustomType + } + + return types.ListType{ + ElemType: b.NestedObject.Type(), + } +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the block to prevent unexpected +// errors or panics. This logic runs during the GetProviderSchema RPC and +// should never include false positives. +func (b ListNestedBlock) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if b.CustomType == nil && fwtype.ContainsCollectionWithDynamic(b.Type()) { + resp.Diagnostics.Append(fwtype.BlockCollectionWithDynamicTypeDiag(req.Path)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/map_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/map_attribute.go new file mode 100644 index 000000000..366619ed5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/map_attribute.go @@ -0,0 +1,223 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = MapAttribute{} + _ fwschema.AttributeWithValidateImplementation = MapAttribute{} + _ fwxschema.AttributeWithMapValidators = MapAttribute{} +) + +// MapAttribute represents a schema attribute that is a map with a single +// element type. When retrieving the value for this attribute, use types.Map +// as the value type unless the CustomType field is set. The ElementType field +// must be set. +// +// Use MapNestedAttribute if the underlying elements should be objects and +// require definition beyond type information. +// +// Terraform configurations configure this attribute using expressions that +// return a map or directly via curly brace syntax. +// +// # map of strings +// example_attribute = { +// key1 = "first", +// key2 = "second", +// } +// +// Terraform configurations reference this attribute using expressions that +// accept a map or an element directly via square brace string syntax: +// +// # key1 known element +// .example_attribute["key1"] +type MapAttribute struct { + // ElementType is the type for all elements of the map. This field must be + // set. + // + // Element types that contain a dynamic type (i.e. types.Dynamic) are not supported. + // If underlying dynamic values are required, replace this attribute definition with + // DynamicAttribute instead. + ElementType attr.Type + + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.MapType. When retrieving data, the basetypes.MapValuable + // associated with this custom type must be used in place of types.Map. + CustomType basetypes.MapTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Map +} + +// ApplyTerraform5AttributePathStep returns the result of stepping into a map +// index or an error. +func (a MapAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a MapAttribute +// and all fields are equal. +func (a MapAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(MapAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a MapAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a MapAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a MapAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.MapType or the CustomType field value if defined. +func (a MapAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.MapType{ + ElemType: a.ElementType, + } +} + +// IsComputed returns the Computed field value. +func (a MapAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a MapAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a MapAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a MapAttribute) IsSensitive() bool { + return a.Sensitive +} + +// MapValidators returns the Validators field value. +func (a MapAttribute) MapValidators() []validator.Map { + return a.Validators +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the attribute to prevent unexpected +// errors or panics. This logic runs during the GetProviderSchema RPC +// and should never include false positives. +func (a MapAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if a.CustomType == nil && a.ElementType == nil { + resp.Diagnostics.Append(fwschema.AttributeMissingElementTypeDiag(req.Path)) + } + + if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) { + resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/map_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/map_nested_attribute.go new file mode 100644 index 000000000..11d701cc9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/map_nested_attribute.go @@ -0,0 +1,245 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ NestedAttribute = MapNestedAttribute{} + _ fwschema.AttributeWithValidateImplementation = MapNestedAttribute{} + _ fwxschema.AttributeWithMapValidators = MapNestedAttribute{} +) + +// MapNestedAttribute represents an attribute that is a map of objects where +// the object attributes can be fully defined, including further nested +// attributes. When retrieving the value for this attribute, use types.Map +// as the value type unless the CustomType field is set. The NestedObject field +// must be set. Nested attributes are only compatible with protocol version 6. +// +// Use MapAttribute if the underlying elements are of a single type and do +// not require definition beyond type information. +// +// Terraform configurations configure this attribute using expressions that +// return a map of objects or directly via curly brace syntax. +// +// # map of objects +// example_attribute = { +// key = { +// nested_attribute = #... +// }, +// ] +// +// Terraform configurations reference this attribute using expressions that +// accept a map of objects or an element directly via square brace string +// syntax: +// +// # known object at key +// .example_attribute["key"] +// # known object nested_attribute value at key +// .example_attribute["key"].nested_attribute +type MapNestedAttribute struct { + // NestedObject is the underlying object that contains nested attributes. + // This field must be set. + // + // Nested attributes that contain a dynamic type (i.e. DynamicAttribute) are not supported. + // If underlying dynamic values are required, replace this attribute definition with + // DynamicAttribute instead. + NestedObject NestedAttributeObject + + // CustomType enables the use of a custom attribute type in place of the + // default types.MapType of types.ObjectType. When retrieving data, the + // basetypes.MapValuable associated with this custom type must be used in + // place of types.Map. + CustomType basetypes.MapTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Map +} + +// ApplyTerraform5AttributePathStep returns the Attributes field value if step +// is ElementKeyString, otherwise returns an error. +func (a MapNestedAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + _, ok := step.(tftypes.ElementKeyString) + + if !ok { + return nil, fmt.Errorf("cannot apply step %T to MapNestedAttribute", step) + } + + return a.NestedObject, nil +} + +// Equal returns true if the given Attribute is a MapNestedAttribute +// and all fields are equal. +func (a MapNestedAttribute) Equal(o fwschema.Attribute) bool { + other, ok := o.(MapNestedAttribute) + + if !ok { + return false + } + + return fwschema.NestedAttributesEqual(a, other) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a MapNestedAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a MapNestedAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a MapNestedAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetNestedObject returns the NestedObject field value. +func (a MapNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { + return a.NestedObject +} + +// GetNestingMode always returns NestingModeMap. +func (a MapNestedAttribute) GetNestingMode() fwschema.NestingMode { + return fwschema.NestingModeMap +} + +// GetType returns MapType of ObjectType or CustomType. +func (a MapNestedAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.MapType{ + ElemType: a.NestedObject.Type(), + } +} + +// IsComputed returns the Computed field value. +func (a MapNestedAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a MapNestedAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a MapNestedAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a MapNestedAttribute) IsSensitive() bool { + return a.Sensitive +} + +// MapValidators returns the Validators field value. +func (a MapNestedAttribute) MapValidators() []validator.Map { + return a.Validators +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the attribute to prevent unexpected +// errors or panics. This logic runs during the GetProviderSchema RPC and +// should never include false positives. +func (a MapNestedAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) { + resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_attribute.go new file mode 100644 index 000000000..31d2ee158 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_attribute.go @@ -0,0 +1,14 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" +) + +// Nested attributes are only compatible with protocol version 6. +type NestedAttribute interface { + Attribute + fwschema.NestedAttribute +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_attribute_object.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_attribute_object.go new file mode 100644 index 000000000..3719a2398 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_attribute_object.go @@ -0,0 +1,82 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var _ fwxschema.NestedAttributeObjectWithValidators = NestedAttributeObject{} + +// NestedAttributeObject is the object containing the underlying attributes +// for a ListNestedAttribute, MapNestedAttribute, SetNestedAttribute, or +// SingleNestedAttribute (automatically generated). When retrieving the value +// for this attribute, use types.Object as the value type unless the CustomType +// field is set. The Attributes field must be set. Nested attributes are only +// compatible with protocol version 6. +// +// This object enables customizing and simplifying details within its parent +// NestedAttribute, therefore it cannot have Terraform schema fields such as +// Required, Description, etc. +type NestedAttributeObject struct { + // Attributes is the mapping of underlying attribute names to attribute + // definitions. This field must be set. + Attributes map[string]Attribute + + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.ObjectType. When retrieving data, the basetypes.ObjectValuable + // associated with this custom type must be used in place of types.Object. + CustomType basetypes.ObjectTypable + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Object +} + +// ApplyTerraform5AttributePathStep performs an AttributeName step on the +// underlying attributes or returns an error. +func (o NestedAttributeObject) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (any, error) { + return fwschema.NestedAttributeObjectApplyTerraform5AttributePathStep(o, step) +} + +// Equal returns true if the given NestedAttributeObject is equivalent. +func (o NestedAttributeObject) Equal(other fwschema.NestedAttributeObject) bool { + if _, ok := other.(NestedAttributeObject); !ok { + return false + } + + return fwschema.NestedAttributeObjectEqual(o, other) +} + +// GetAttributes returns the Attributes field value. +func (o NestedAttributeObject) GetAttributes() fwschema.UnderlyingAttributes { + return schemaAttributes(o.Attributes) +} + +// ObjectValidators returns the Validators field value. +func (o NestedAttributeObject) ObjectValidators() []validator.Object { + return o.Validators +} + +// Type returns the framework type of the NestedAttributeObject. +func (o NestedAttributeObject) Type() basetypes.ObjectTypable { + if o.CustomType != nil { + return o.CustomType + } + + return fwschema.NestedAttributeObjectType(o) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_block_object.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_block_object.go new file mode 100644 index 000000000..2b560b606 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/nested_block_object.go @@ -0,0 +1,94 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var _ fwxschema.NestedBlockObjectWithValidators = NestedBlockObject{} + +// NestedBlockObject is the object containing the underlying attributes and +// blocks for a ListNestedBlock or SetNestedBlock. When retrieving the value +// for this attribute, use types.Object as the value type unless the CustomType +// field is set. +// +// This object enables customizing and simplifying details within its parent +// Block, therefore it cannot have Terraform schema fields such as Description, +// etc. +type NestedBlockObject struct { + // Attributes is the mapping of underlying attribute names to attribute + // definitions. + // + // Names must only contain lowercase letters, numbers, and underscores. + // Names must not collide with any Blocks names. + Attributes map[string]Attribute + + // Blocks is the mapping of underlying block names to block definitions. + // + // Names must only contain lowercase letters, numbers, and underscores. + // Names must not collide with any Attributes names. + Blocks map[string]Block + + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.ObjectType. When retrieving data, the basetypes.ObjectValuable + // associated with this custom type must be used in place of types.Object. + CustomType basetypes.ObjectTypable + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Object +} + +// ApplyTerraform5AttributePathStep performs an AttributeName step on the +// underlying attributes or returns an error. +func (o NestedBlockObject) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (any, error) { + return fwschema.NestedBlockObjectApplyTerraform5AttributePathStep(o, step) +} + +// Equal returns true if the given NestedBlockObject is equivalent. +func (o NestedBlockObject) Equal(other fwschema.NestedBlockObject) bool { + if _, ok := other.(NestedBlockObject); !ok { + return false + } + + return fwschema.NestedBlockObjectEqual(o, other) +} + +// GetAttributes returns the Attributes field value. +func (o NestedBlockObject) GetAttributes() fwschema.UnderlyingAttributes { + return schemaAttributes(o.Attributes) +} + +// GetAttributes returns the Blocks field value. +func (o NestedBlockObject) GetBlocks() map[string]fwschema.Block { + return schemaBlocks(o.Blocks) +} + +// ObjectValidators returns the Validators field value. +func (o NestedBlockObject) ObjectValidators() []validator.Object { + return o.Validators +} + +// Type returns the framework type of the NestedBlockObject. +func (o NestedBlockObject) Type() basetypes.ObjectTypable { + if o.CustomType != nil { + return o.CustomType + } + + return fwschema.NestedBlockObjectType(o) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/number_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/number_attribute.go new file mode 100644 index 000000000..547396409 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/number_attribute.go @@ -0,0 +1,189 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = NumberAttribute{} + _ fwxschema.AttributeWithNumberValidators = NumberAttribute{} +) + +// NumberAttribute represents a schema attribute that is a generic number with +// up to 512 bits of floating point or integer precision. When retrieving the +// value for this attribute, use types.Number as the value type unless the +// CustomType field is set. +// +// Use Float64Attribute for 64-bit floating point number attributes or +// Int64Attribute for 64-bit integer number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via a floating point or integer value. +// +// example_attribute = 123 +// +// Terraform configurations reference this attribute using the attribute name. +// +// .example_attribute +type NumberAttribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.NumberType. When retrieving data, the basetypes.NumberValuable + // associated with this custom type must be used in place of types.Number. + CustomType basetypes.NumberTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Number +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a NumberAttribute. +func (a NumberAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a NumberAttribute +// and all fields are equal. +func (a NumberAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(NumberAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a NumberAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a NumberAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a NumberAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.NumberType or the CustomType field value if defined. +func (a NumberAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.NumberType +} + +// IsComputed returns the Computed field value. +func (a NumberAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a NumberAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a NumberAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a NumberAttribute) IsSensitive() bool { + return a.Sensitive +} + +// NumberValidators returns the Validators field value. +func (a NumberAttribute) NumberValidators() []validator.Number { + return a.Validators +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/object_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/object_attribute.go new file mode 100644 index 000000000..1ff506f9d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/object_attribute.go @@ -0,0 +1,222 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = ObjectAttribute{} + _ fwschema.AttributeWithValidateImplementation = ObjectAttribute{} + _ fwxschema.AttributeWithObjectValidators = ObjectAttribute{} +) + +// ObjectAttribute represents a schema attribute that is an object with only +// type information for underlying attributes. When retrieving the value for +// this attribute, use types.Object as the value type unless the CustomType +// field is set. The AttributeTypes field must be set. +// +// Prefer SingleNestedAttribute over ObjectAttribute if the provider is +// using protocol version 6 and full attribute functionality is needed. +// +// Terraform configurations configure this attribute using expressions that +// return an object or directly via curly brace syntax. +// +// # object with one attribute +// example_attribute = { +// underlying_attribute = #... +// } +// +// Terraform configurations reference this attribute using expressions that +// accept an object or an attribute directly via period syntax: +// +// # underlying attribute +// .example_attribute.underlying_attribute +type ObjectAttribute struct { + // AttributeTypes is the mapping of underlying attribute names to attribute + // types. This field must be set. + // + // Attribute types that contain a collection with a nested dynamic type (i.e. types.List[types.Dynamic]) are not supported. + // If underlying dynamic collection values are required, replace this attribute definition with + // DynamicAttribute instead. + AttributeTypes map[string]attr.Type + + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.ObjectType. When retrieving data, the basetypes.ObjectValuable + // associated with this custom type must be used in place of types.Object. + CustomType basetypes.ObjectTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Object +} + +// ApplyTerraform5AttributePathStep returns the result of stepping into an +// attribute name or an error. +func (a ObjectAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a ObjectAttribute +// and all fields are equal. +func (a ObjectAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(ObjectAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a ObjectAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a ObjectAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a ObjectAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.ObjectType or the CustomType field value if defined. +func (a ObjectAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.ObjectType{ + AttrTypes: a.AttributeTypes, + } +} + +// IsComputed returns the Computed field value. +func (a ObjectAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a ObjectAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a ObjectAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a ObjectAttribute) IsSensitive() bool { + return a.Sensitive +} + +// ObjectValidators returns the Validators field value. +func (a ObjectAttribute) ObjectValidators() []validator.Object { + return a.Validators +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the attribute to prevent unexpected +// errors or panics. This logic runs during the GetProviderSchema RPC +// and should never include false positives. +func (a ObjectAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if a.AttributeTypes == nil && a.CustomType == nil { + resp.Diagnostics.Append(fwschema.AttributeMissingAttributeTypesDiag(req.Path)) + } + + if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) { + resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/schema.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/schema.go new file mode 100644 index 000000000..3c92269fb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/schema.go @@ -0,0 +1,187 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/path" +) + +// Schema must satify the fwschema.Schema interface. +var _ fwschema.Schema = Schema{} + +// Schema defines the structure and value types of ephemeral resource data. This type +// is used as the ephemeral.SchemaResponse type Schema field, which is +// implemented by the ephemeral.EphemeralResource type Schema method. +type Schema struct { + // Attributes is the mapping of underlying attribute names to attribute + // definitions. + // + // Names must only contain lowercase letters, numbers, and underscores. + // Names must not collide with any Blocks names. + Attributes map[string]Attribute + + // Blocks is the mapping of underlying block names to block definitions. + // + // Names must only contain lowercase letters, numbers, and underscores. + // Names must not collide with any Attributes names. + Blocks map[string]Block + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this ephemeral resource is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this ephemeral resource is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this ephemeral resource. The warning diagnostic + // summary is automatically set to "Ephemeral Resource Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Use examplecloud_other ephemeral resource instead. This ephemeral resource + // will be removed in the next major version of the provider." + // - "Remove this ephemeral resource as it no longer is valid and + // will be removed in the next major version of the provider." + // + DeprecationMessage string +} + +// ApplyTerraform5AttributePathStep applies the given AttributePathStep to the +// schema. +func (s Schema) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (any, error) { + return fwschema.SchemaApplyTerraform5AttributePathStep(s, step) +} + +// AttributeAtPath returns the Attribute at the passed path. If the path points +// to an element or attribute of a complex type, rather than to an Attribute, +// it will return an ErrPathInsideAtomicAttribute error. +func (s Schema) AttributeAtPath(ctx context.Context, p path.Path) (fwschema.Attribute, diag.Diagnostics) { + return fwschema.SchemaAttributeAtPath(ctx, s, p) +} + +// AttributeAtPath returns the Attribute at the passed path. If the path points +// to an element or attribute of a complex type, rather than to an Attribute, +// it will return an ErrPathInsideAtomicAttribute error. +func (s Schema) AttributeAtTerraformPath(ctx context.Context, p *tftypes.AttributePath) (fwschema.Attribute, error) { + return fwschema.SchemaAttributeAtTerraformPath(ctx, s, p) +} + +// GetAttributes returns the Attributes field value. +func (s Schema) GetAttributes() map[string]fwschema.Attribute { + return schemaAttributes(s.Attributes) +} + +// GetBlocks returns the Blocks field value. +func (s Schema) GetBlocks() map[string]fwschema.Block { + return schemaBlocks(s.Blocks) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (s Schema) GetDeprecationMessage() string { + return s.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (s Schema) GetDescription() string { + return s.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (s Schema) GetMarkdownDescription() string { + return s.MarkdownDescription +} + +// GetVersion always returns 0 as ephemeral resource schemas cannot be versioned. +func (s Schema) GetVersion() int64 { + return 0 +} + +// Type returns the framework type of the schema. +func (s Schema) Type() attr.Type { + return fwschema.SchemaType(s) +} + +// TypeAtPath returns the framework type at the given schema path. +func (s Schema) TypeAtPath(ctx context.Context, p path.Path) (attr.Type, diag.Diagnostics) { + return fwschema.SchemaTypeAtPath(ctx, s, p) +} + +// TypeAtTerraformPath returns the framework type at the given tftypes path. +func (s Schema) TypeAtTerraformPath(ctx context.Context, p *tftypes.AttributePath) (attr.Type, error) { + return fwschema.SchemaTypeAtTerraformPath(ctx, s, p) +} + +// Validate verifies that the schema is not using a reserved field name for a top-level attribute. +// +// Deprecated: Use the ValidateImplementation method instead. +func (s Schema) Validate() diag.Diagnostics { + return s.ValidateImplementation(context.Background()) +} + +// ValidateImplementation contains logic for validating the provider-defined +// implementation of the schema and underlying attributes and blocks to prevent +// unexpected errors or panics. This logic runs during the GetProviderSchema +// RPC, or via provider-defined unit testing, and should never include false +// positives. +func (s Schema) ValidateImplementation(ctx context.Context) diag.Diagnostics { + var diags diag.Diagnostics + + for attributeName, attribute := range s.GetAttributes() { + req := fwschema.ValidateImplementationRequest{ + Name: attributeName, + Path: path.Root(attributeName), + } + + diags.Append(fwschema.IsReservedResourceAttributeName(req.Name, req.Path)...) + diags.Append(fwschema.ValidateAttributeImplementation(ctx, attribute, req)...) + } + + for blockName, block := range s.GetBlocks() { + req := fwschema.ValidateImplementationRequest{ + Name: blockName, + Path: path.Root(blockName), + } + + diags.Append(fwschema.IsReservedResourceAttributeName(req.Name, req.Path)...) + diags.Append(fwschema.ValidateBlockImplementation(ctx, block, req)...) + } + + return diags +} + +// schemaAttributes is a ephemeral resource to fwschema type conversion function. +func schemaAttributes(attributes map[string]Attribute) map[string]fwschema.Attribute { + result := make(map[string]fwschema.Attribute, len(attributes)) + + for name, attribute := range attributes { + result[name] = attribute + } + + return result +} + +// schemaBlocks is a ephemeral resource to fwschema type conversion function. +func schemaBlocks(blocks map[string]Block) map[string]fwschema.Block { + result := make(map[string]fwschema.Block, len(blocks)) + + for name, block := range blocks { + result[name] = block + } + + return result +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_attribute.go new file mode 100644 index 000000000..7f6a408ed --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_attribute.go @@ -0,0 +1,218 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = SetAttribute{} + _ fwschema.AttributeWithValidateImplementation = SetAttribute{} + _ fwxschema.AttributeWithSetValidators = SetAttribute{} +) + +// SetAttribute represents a schema attribute that is a set with a single +// element type. When retrieving the value for this attribute, use types.Set +// as the value type unless the CustomType field is set. The ElementType field +// must be set. +// +// Use SetNestedAttribute if the underlying elements should be objects and +// require definition beyond type information. +// +// Terraform configurations configure this attribute using expressions that +// return a set or directly via square brace syntax. +// +// # set of strings +// example_attribute = ["first", "second"] +// +// Terraform configurations reference this attribute using expressions that +// accept a set. Sets cannot be indexed in Terraform, therefore an expression +// is required to access an explicit element. +type SetAttribute struct { + // ElementType is the type for all elements of the set. This field must be + // set. + // + // Element types that contain a dynamic type (i.e. types.Dynamic) are not supported. + // If underlying dynamic values are required, replace this attribute definition with + // DynamicAttribute instead. + ElementType attr.Type + + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.SetType. When retrieving data, the basetypes.SetValuable + // associated with this custom type must be used in place of types.Set. + CustomType basetypes.SetTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Set +} + +// ApplyTerraform5AttributePathStep returns the result of stepping into a set +// index or an error. +func (a SetAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a SetAttribute +// and all fields are equal. +func (a SetAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(SetAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a SetAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a SetAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a SetAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.SetType or the CustomType field value if defined. +func (a SetAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.SetType{ + ElemType: a.ElementType, + } +} + +// IsComputed returns the Computed field value. +func (a SetAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a SetAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a SetAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a SetAttribute) IsSensitive() bool { + return a.Sensitive +} + +// SetValidators returns the Validators field value. +func (a SetAttribute) SetValidators() []validator.Set { + return a.Validators +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the attribute to prevent unexpected +// errors or panics. This logic runs during the GetProviderSchema RPC +// and should never include false positives. +func (a SetAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if a.CustomType == nil && a.ElementType == nil { + resp.Diagnostics.Append(fwschema.AttributeMissingElementTypeDiag(req.Path)) + } + + if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) { + resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_nested_attribute.go new file mode 100644 index 000000000..7ed6d2fdf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_nested_attribute.go @@ -0,0 +1,240 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ NestedAttribute = SetNestedAttribute{} + _ fwschema.AttributeWithValidateImplementation = SetNestedAttribute{} + _ fwxschema.AttributeWithSetValidators = SetNestedAttribute{} +) + +// SetNestedAttribute represents an attribute that is a set of objects where +// the object attributes can be fully defined, including further nested +// attributes. When retrieving the value for this attribute, use types.Set +// as the value type unless the CustomType field is set. The NestedObject field +// must be set. Nested attributes are only compatible with protocol version 6. +// +// Use SetAttribute if the underlying elements are of a single type and do +// not require definition beyond type information. +// +// Terraform configurations configure this attribute using expressions that +// return a set of objects or directly via square and curly brace syntax. +// +// # set of objects +// example_attribute = [ +// { +// nested_attribute = #... +// }, +// ] +// +// Terraform configurations reference this attribute using expressions that +// accept a set of objects. Sets cannot be indexed in Terraform, therefore +// an expression is required to access an explicit element. +type SetNestedAttribute struct { + // NestedObject is the underlying object that contains nested attributes. + // This field must be set. + // + // Nested attributes that contain a dynamic type (i.e. DynamicAttribute) are not supported. + // If underlying dynamic values are required, replace this attribute definition with + // DynamicAttribute instead. + NestedObject NestedAttributeObject + + // CustomType enables the use of a custom attribute type in place of the + // default types.SetType of types.ObjectType. When retrieving data, the + // basetypes.SetValuable associated with this custom type must be used in + // place of types.Set. + CustomType basetypes.SetTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Set +} + +// ApplyTerraform5AttributePathStep returns the Attributes field value if step +// is ElementKeyValue, otherwise returns an error. +func (a SetNestedAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + _, ok := step.(tftypes.ElementKeyValue) + + if !ok { + return nil, fmt.Errorf("cannot apply step %T to SetNestedAttribute", step) + } + + return a.NestedObject, nil +} + +// Equal returns true if the given Attribute is a SetNestedAttribute +// and all fields are equal. +func (a SetNestedAttribute) Equal(o fwschema.Attribute) bool { + other, ok := o.(SetNestedAttribute) + + if !ok { + return false + } + + return fwschema.NestedAttributesEqual(a, other) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a SetNestedAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a SetNestedAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a SetNestedAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetNestedObject returns the NestedObject field value. +func (a SetNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { + return a.NestedObject +} + +// GetNestingMode always returns NestingModeSet. +func (a SetNestedAttribute) GetNestingMode() fwschema.NestingMode { + return fwschema.NestingModeSet +} + +// GetType returns SetType of ObjectType or CustomType. +func (a SetNestedAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.SetType{ + ElemType: a.NestedObject.Type(), + } +} + +// IsComputed returns the Computed field value. +func (a SetNestedAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a SetNestedAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a SetNestedAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a SetNestedAttribute) IsSensitive() bool { + return a.Sensitive +} + +// SetValidators returns the Validators field value. +func (a SetNestedAttribute) SetValidators() []validator.Set { + return a.Validators +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the attribute to prevent unexpected +// errors or panics. This logic runs during the GetProviderSchema RPC and +// should never include false positives. +func (a SetNestedAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) { + resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_nested_block.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_nested_block.go new file mode 100644 index 000000000..085163f37 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/set_nested_block.go @@ -0,0 +1,205 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Block = SetNestedBlock{} + _ fwschema.BlockWithValidateImplementation = SetNestedBlock{} + _ fwxschema.BlockWithSetValidators = SetNestedBlock{} +) + +// SetNestedBlock represents a block that is a set of objects where +// the object attributes can be fully defined, including further attributes +// or blocks. When retrieving the value for this block, use types.Set +// as the value type unless the CustomType field is set. The NestedObject field +// must be set. +// +// Prefer SetNestedAttribute over SetNestedBlock if the provider is +// using protocol version 6. Nested attributes allow practitioners to configure +// values directly with expressions. +// +// Terraform configurations configure this block repeatedly using curly brace +// syntax without an equals (=) sign or [Dynamic Block Expressions]. +// +// # set of blocks with two elements +// example_block { +// nested_attribute = #... +// } +// example_block { +// nested_attribute = #... +// } +// +// Terraform configurations reference this block using expressions that +// accept a set of objects or an element directly via square brace 0-based +// index syntax: +// +// # first known object +// .example_block[0] +// # first known object nested_attribute value +// .example_block[0].nested_attribute +// +// [Dynamic Block Expressions]: https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks +type SetNestedBlock struct { + // NestedObject is the underlying object that contains nested attributes or + // blocks. This field must be set. + // + // Nested attributes that contain a dynamic type (i.e. DynamicAttribute) are not supported. + // If underlying dynamic values are required, replace this block definition with + // a DynamicAttribute. + NestedObject NestedBlockObject + + // CustomType enables the use of a custom attribute type in place of the + // default types.SetType of types.ObjectType. When retrieving data, the + // basetypes.SetValuable associated with this custom type must be used in + // place of types.Set. + CustomType basetypes.SetTypable + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Set +} + +// ApplyTerraform5AttributePathStep returns the NestedObject field value if step +// is ElementKeyValue, otherwise returns an error. +func (b SetNestedBlock) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + _, ok := step.(tftypes.ElementKeyValue) + + if !ok { + return nil, fmt.Errorf("cannot apply step %T to SetNestedBlock", step) + } + + return b.NestedObject, nil +} + +// Equal returns true if the given Block is SetNestedBlock +// and all fields are equal. +func (b SetNestedBlock) Equal(o fwschema.Block) bool { + if _, ok := o.(SetNestedBlock); !ok { + return false + } + + return fwschema.BlocksEqual(b, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (b SetNestedBlock) GetDeprecationMessage() string { + return b.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (b SetNestedBlock) GetDescription() string { + return b.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (b SetNestedBlock) GetMarkdownDescription() string { + return b.MarkdownDescription +} + +// GetNestedObject returns the NestedObject field value. +func (b SetNestedBlock) GetNestedObject() fwschema.NestedBlockObject { + return b.NestedObject +} + +// GetNestingMode always returns BlockNestingModeSet. +func (b SetNestedBlock) GetNestingMode() fwschema.BlockNestingMode { + return fwschema.BlockNestingModeSet +} + +// SetValidators returns the Validators field value. +func (b SetNestedBlock) SetValidators() []validator.Set { + return b.Validators +} + +// Type returns SetType of ObjectType or CustomType. +func (b SetNestedBlock) Type() attr.Type { + if b.CustomType != nil { + return b.CustomType + } + + return types.SetType{ + ElemType: b.NestedObject.Type(), + } +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the block to prevent unexpected +// errors or panics. This logic runs during the GetProviderSchema RPC and +// should never include false positives. +func (b SetNestedBlock) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if b.CustomType == nil && fwtype.ContainsCollectionWithDynamic(b.Type()) { + resp.Diagnostics.Append(fwtype.BlockCollectionWithDynamicTypeDiag(req.Path)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/single_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/single_nested_attribute.go new file mode 100644 index 000000000..a57db9c65 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/single_nested_attribute.go @@ -0,0 +1,244 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ NestedAttribute = SingleNestedAttribute{} + _ fwxschema.AttributeWithObjectValidators = SingleNestedAttribute{} +) + +// SingleNestedAttribute represents an attribute that is a single object where +// the object attributes can be fully defined, including further nested +// attributes. When retrieving the value for this attribute, use types.Object +// as the value type unless the CustomType field is set. The Attributes field +// must be set. Nested attributes are only compatible with protocol version 6. +// +// Use ObjectAttribute if the underlying attributes do not require definition +// beyond type information. +// +// Terraform configurations configure this attribute using expressions that +// return an object or directly via curly brace syntax. +// +// # single object +// example_attribute = { +// nested_attribute = #... +// } +// +// Terraform configurations reference this attribute using expressions that +// accept an object or an attribute name directly via period syntax: +// +// # object nested_attribute value +// .example_attribute.nested_attribute +type SingleNestedAttribute struct { + // Attributes is the mapping of underlying attribute names to attribute + // definitions. This field must be set. + Attributes map[string]Attribute + + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.ObjectType. When retrieving data, the basetypes.ObjectValuable + // associated with this custom type must be used in place of types.Object. + CustomType basetypes.ObjectTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Object +} + +// ApplyTerraform5AttributePathStep returns the Attributes field value if step +// is AttributeName, otherwise returns an error. +func (a SingleNestedAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + name, ok := step.(tftypes.AttributeName) + + if !ok { + return nil, fmt.Errorf("cannot apply step %T to SingleNestedAttribute", step) + } + + attribute, ok := a.Attributes[string(name)] + + if !ok { + return nil, fmt.Errorf("no attribute %q on SingleNestedAttribute", name) + } + + return attribute, nil +} + +// Equal returns true if the given Attribute is a SingleNestedAttribute +// and all fields are equal. +func (a SingleNestedAttribute) Equal(o fwschema.Attribute) bool { + other, ok := o.(SingleNestedAttribute) + + if !ok { + return false + } + + return fwschema.NestedAttributesEqual(a, other) +} + +// GetAttributes returns the Attributes field value. +func (a SingleNestedAttribute) GetAttributes() fwschema.UnderlyingAttributes { + return schemaAttributes(a.Attributes) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a SingleNestedAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a SingleNestedAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a SingleNestedAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetNestedObject returns a generated NestedAttributeObject from the +// Attributes, CustomType, and Validators field values. +func (a SingleNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { + return NestedAttributeObject{ + Attributes: a.Attributes, + CustomType: a.CustomType, + Validators: a.Validators, + } +} + +// GetNestingMode always returns NestingModeSingle. +func (a SingleNestedAttribute) GetNestingMode() fwschema.NestingMode { + return fwschema.NestingModeSingle +} + +// GetType returns ListType of ObjectType or CustomType. +func (a SingleNestedAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + attrTypes := make(map[string]attr.Type, len(a.Attributes)) + + for name, attribute := range a.Attributes { + attrTypes[name] = attribute.GetType() + } + + return types.ObjectType{ + AttrTypes: attrTypes, + } +} + +// IsComputed returns the Computed field value. +func (a SingleNestedAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a SingleNestedAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a SingleNestedAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a SingleNestedAttribute) IsSensitive() bool { + return a.Sensitive +} + +// ObjectValidators returns the Validators field value. +func (a SingleNestedAttribute) ObjectValidators() []validator.Object { + return a.Validators +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/single_nested_block.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/single_nested_block.go new file mode 100644 index 000000000..926825a03 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/single_nested_block.go @@ -0,0 +1,213 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Block = SingleNestedBlock{} + _ fwxschema.BlockWithObjectValidators = SingleNestedBlock{} +) + +// SingleNestedBlock represents a block that is a single object where +// the object attributes can be fully defined, including further attributes +// or blocks. When retrieving the value for this block, use types.Object +// as the value type unless the CustomType field is set. +// +// Prefer SingleNestedAttribute over SingleNestedBlock if the provider is +// using protocol version 6. Nested attributes allow practitioners to configure +// values directly with expressions. +// +// Terraform configurations configure this block only once using curly brace +// syntax without an equals (=) sign or [Dynamic Block Expressions]. +// +// # single block +// example_block { +// nested_attribute = #... +// } +// +// Terraform configurations reference this block using expressions that +// accept an object or an attribute name directly via period syntax: +// +// # object nested_attribute value +// .example_block.nested_attribute +// +// [Dynamic Block Expressions]: https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks +type SingleNestedBlock struct { + // Attributes is the mapping of underlying attribute names to attribute + // definitions. + // + // Names must only contain lowercase letters, numbers, and underscores. + // Names must not collide with any Blocks names. + Attributes map[string]Attribute + + // Blocks is the mapping of underlying block names to block definitions. + // + // Names must only contain lowercase letters, numbers, and underscores. + // Names must not collide with any Attributes names. + Blocks map[string]Block + + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.ObjectType. When retrieving data, the basetypes.ObjectValuable + // associated with this custom type must be used in place of types.Object. + CustomType basetypes.ObjectTypable + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.Object +} + +// ApplyTerraform5AttributePathStep returns the Attributes field value if step +// is AttributeName, otherwise returns an error. +func (b SingleNestedBlock) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + name, ok := step.(tftypes.AttributeName) + + if !ok { + return nil, fmt.Errorf("cannot apply step %T to SingleNestedBlock", step) + } + + if attribute, ok := b.Attributes[string(name)]; ok { + return attribute, nil + } + + if block, ok := b.Blocks[string(name)]; ok { + return block, nil + } + + return nil, fmt.Errorf("no attribute or block %q on SingleNestedBlock", name) +} + +// Equal returns true if the given Attribute is b SingleNestedBlock +// and all fields are equal. +func (b SingleNestedBlock) Equal(o fwschema.Block) bool { + if _, ok := o.(SingleNestedBlock); !ok { + return false + } + + return fwschema.BlocksEqual(b, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (b SingleNestedBlock) GetDeprecationMessage() string { + return b.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (b SingleNestedBlock) GetDescription() string { + return b.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (b SingleNestedBlock) GetMarkdownDescription() string { + return b.MarkdownDescription +} + +// GetNestedObject returns a generated NestedBlockObject from the +// Attributes, CustomType, and Validators field values. +func (b SingleNestedBlock) GetNestedObject() fwschema.NestedBlockObject { + return NestedBlockObject{ + Attributes: b.Attributes, + Blocks: b.Blocks, + CustomType: b.CustomType, + Validators: b.Validators, + } +} + +// GetNestingMode always returns BlockNestingModeSingle. +func (b SingleNestedBlock) GetNestingMode() fwschema.BlockNestingMode { + return fwschema.BlockNestingModeSingle +} + +// ObjectValidators returns the Validators field value. +func (b SingleNestedBlock) ObjectValidators() []validator.Object { + return b.Validators +} + +// Type returns ObjectType or CustomType. +func (b SingleNestedBlock) Type() attr.Type { + if b.CustomType != nil { + return b.CustomType + } + + attrTypes := make(map[string]attr.Type, len(b.Attributes)+len(b.Blocks)) + + for name, attribute := range b.Attributes { + attrTypes[name] = attribute.GetType() + } + + for name, block := range b.Blocks { + attrTypes[name] = block.Type() + } + + return types.ObjectType{ + AttrTypes: attrTypes, + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/string_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/string_attribute.go new file mode 100644 index 000000000..1b5c7db22 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/schema/string_attribute.go @@ -0,0 +1,185 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// Ensure the implementation satisifies the desired interfaces. +var ( + _ Attribute = StringAttribute{} + _ fwxschema.AttributeWithStringValidators = StringAttribute{} +) + +// StringAttribute represents a schema attribute that is a string. When +// retrieving the value for this attribute, use types.String as the value type +// unless the CustomType field is set. +// +// Terraform configurations configure this attribute using expressions that +// return a string or directly via double quote syntax. +// +// example_attribute = "value" +// +// Terraform configurations reference this attribute using the attribute name. +// +// .example_attribute +type StringAttribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.StringType. When retrieving data, the basetypes.StringValuable + // associated with this custom type must be used in place of types.String. + CustomType basetypes.StringTypable + + // Required indicates whether the practitioner must enter a value for + // this attribute or not. Required and Optional cannot both be true, + // and Required and Computed cannot both be true. + Required bool + + // Optional indicates whether the practitioner can choose to enter a value + // for this attribute or not. Optional and Required cannot both be true. + Optional bool + + // Computed indicates whether the provider may return its own value for + // this Attribute or not. Required and Computed cannot both be true. If + // Required and Optional are both false, Computed must be true, and the + // attribute will be considered "read only" for the practitioner, with + // only the provider able to set its value. + Computed bool + + // Sensitive indicates whether the value of this attribute should be + // considered sensitive data. Setting it to true will obscure the value + // in CLI output. + Sensitive bool + + // Description is used in various tooling, like the language server, to + // give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It should be written as + // plain text, with no special formatting. + Description string + + // MarkdownDescription is used in various tooling, like the + // documentation generator, to give practitioners more information + // about what this attribute is, what it's for, and how it should be + // used. It should be formatted using Markdown. + MarkdownDescription string + + // DeprecationMessage defines warning diagnostic details to display when + // practitioner configurations use this Attribute. The warning diagnostic + // summary is automatically set to "Attribute Deprecated" along with + // configuration source file and line information. + // + // Set this field to a practitioner actionable message such as: + // + // - "Configure other_attribute instead. This attribute will be removed + // in the next major version of the provider." + // - "Remove this attribute's configuration as it no longer is used and + // the attribute will be removed in the next major version of the + // provider." + // + // In Terraform 1.2.7 and later, this warning diagnostic is displayed any + // time a practitioner attempts to configure a value for this attribute and + // certain scenarios where this attribute is referenced. + // + // In Terraform 1.2.6 and earlier, this warning diagnostic is only + // displayed when the Attribute is Required or Optional, and if the + // practitioner configuration sets the value to a known or unknown value + // (which may eventually be null). It has no effect when the Attribute is + // Computed-only (read-only; not Required or Optional). + // + // Across any Terraform version, there are no warnings raised for + // practitioner configuration values set directly to null, as there is no + // way for the framework to differentiate between an unset and null + // configuration due to how Terraform sends configuration information + // across the protocol. + // + // Additional information about deprecation enhancements for read-only + // attributes can be found in: + // + // - https://github.com/hashicorp/terraform/issues/7569 + // + DeprecationMessage string + + // Validators define value validation functionality for the attribute. All + // elements of the slice of AttributeValidator are run, regardless of any + // previous error diagnostics. + // + // Many common use case validators can be found in the + // github.com/hashicorp/terraform-plugin-framework-validators Go module. + // + // If the Type field points to a custom type that implements the + // xattr.TypeWithValidate interface, the validators defined in this field + // are run in addition to the validation defined by the type. + Validators []validator.String +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a StringAttribute. +func (a StringAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a StringAttribute +// and all fields are equal. +func (a StringAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(StringAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns the DeprecationMessage field value. +func (a StringAttribute) GetDeprecationMessage() string { + return a.DeprecationMessage +} + +// GetDescription returns the Description field value. +func (a StringAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the MarkdownDescription field value. +func (a StringAttribute) GetMarkdownDescription() string { + return a.MarkdownDescription +} + +// GetType returns types.StringType or the CustomType field value if defined. +func (a StringAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.StringType +} + +// IsComputed returns the Computed field value. +func (a StringAttribute) IsComputed() bool { + return a.Computed +} + +// IsOptional returns the Optional field value. +func (a StringAttribute) IsOptional() bool { + return a.Optional +} + +// IsRequired returns the Required field value. +func (a StringAttribute) IsRequired() bool { + return a.Required +} + +// IsSensitive returns the Sensitive field value. +func (a StringAttribute) IsSensitive() bool { + return a.Sensitive +} + +// StringValidators returns the Validators field value. +func (a StringAttribute) StringValidators() []validator.String { + return a.Validators +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/validate_config.go b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/validate_config.go new file mode 100644 index 000000000..ace26d48e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/ephemeral/validate_config.go @@ -0,0 +1,32 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package ephemeral + +import ( + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +// ValidateConfigRequest represents a request to validate the +// configuration of an ephemeral resource. An instance of this request struct is +// supplied as an argument to the EphemeralResource ValidateConfig receiver method +// or automatically passed through to each ConfigValidator. +type ValidateConfigRequest struct { + // Config is the configuration the user supplied for the ephemeral resource. + // + // This configuration may contain unknown values if a user uses + // interpolation or other functionality that would prevent Terraform + // from knowing the value at request time. + Config tfsdk.Config +} + +// ValidateConfigResponse represents a response to a +// ValidateConfigRequest. An instance of this response struct is +// supplied as an argument to the EphemeralResource ValidateConfig receiver method +// or automatically passed through to each ConfigValidator. +type ValidateConfigResponse struct { + // Diagnostics report errors or warnings related to validating the ephemeral resource + // configuration. An empty slice indicates success, with no warnings or + // errors generated. + Diagnostics diag.Diagnostics +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/client_capabilities.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/client_capabilities.go index 3a6347dc4..0eaf40c50 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/client_capabilities.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/client_capabilities.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-go/tfprotov5" "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/resource" ) @@ -75,3 +76,16 @@ func ImportStateClientCapabilities(in *tfprotov5.ImportResourceStateClientCapabi DeferralAllowed: in.DeferralAllowed, } } + +func OpenEphemeralResourceClientCapabilities(in *tfprotov5.OpenEphemeralResourceClientCapabilities) ephemeral.OpenClientCapabilities { + if in == nil { + // Client did not indicate any supported capabilities + return ephemeral.OpenClientCapabilities{ + DeferralAllowed: false, + } + } + + return ephemeral.OpenClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/closeephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/closeephemeralresource.go new file mode 100644 index 000000000..5aa352795 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/closeephemeralresource.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// CloseEphemeralResourceRequest returns the *fwserver.CloseEphemeralResourceRequest +// equivalent of a *tfprotov5.CloseEphemeralResourceRequest. +func CloseEphemeralResourceRequest(ctx context.Context, proto5 *tfprotov5.CloseEphemeralResourceRequest, ephemeralResource ephemeral.EphemeralResource, ephemeralResourceSchema fwschema.Schema) (*fwserver.CloseEphemeralResourceRequest, diag.Diagnostics) { + if proto5 == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if ephemeralResourceSchema == nil { + diags.AddError( + "Missing EphemeralResource Schema", + "An unexpected error was encountered when handling the request. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ) + + return nil, diags + } + + fw := &fwserver.CloseEphemeralResourceRequest{ + EphemeralResource: ephemeralResource, + EphemeralResourceSchema: ephemeralResourceSchema, + } + + privateData, privateDataDiags := privatestate.NewData(ctx, proto5.Private) + + diags.Append(privateDataDiags...) + + fw.Private = privateData + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/ephemeral_result_data.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/ephemeral_result_data.go new file mode 100644 index 000000000..b33e88965 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/ephemeral_result_data.go @@ -0,0 +1,53 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// EphemeralResultData returns the *tfsdk.EphemeralResultData for a *tfprotov5.DynamicValue and +// fwschema.Schema. +func EphemeralResultData(ctx context.Context, proto5DynamicValue *tfprotov5.DynamicValue, schema fwschema.Schema) (*tfsdk.EphemeralResultData, diag.Diagnostics) { + if proto5DynamicValue == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if schema == nil { + diags.AddError( + "Unable to Convert Ephemeral Result Data", + "An unexpected error was encountered when converting the ephemeral result data from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ) + + return nil, diags + } + + data, dynamicValueDiags := DynamicValue(ctx, proto5DynamicValue, schema, fwschemadata.DataDescriptionEphemeralResultData) + + diags.Append(dynamicValueDiags...) + + if diags.HasError() { + return nil, diags + } + + fw := &tfsdk.EphemeralResultData{ + Raw: data.TerraformValue, + Schema: schema, + } + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/openephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/openephemeralresource.go new file mode 100644 index 000000000..a1bcbcc8a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/openephemeralresource.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// OpenEphemeralResourceRequest returns the *fwserver.OpenEphemeralResourceRequest +// equivalent of a *tfprotov5.OpenEphemeralResourceRequest. +func OpenEphemeralResourceRequest(ctx context.Context, proto5 *tfprotov5.OpenEphemeralResourceRequest, ephemeralResource ephemeral.EphemeralResource, ephemeralResourceSchema fwschema.Schema) (*fwserver.OpenEphemeralResourceRequest, diag.Diagnostics) { + if proto5 == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if ephemeralResourceSchema == nil { + diags.AddError( + "Missing EphemeralResource Schema", + "An unexpected error was encountered when handling the request. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ) + + return nil, diags + } + + fw := &fwserver.OpenEphemeralResourceRequest{ + EphemeralResource: ephemeralResource, + EphemeralResourceSchema: ephemeralResourceSchema, + ClientCapabilities: OpenEphemeralResourceClientCapabilities(proto5.ClientCapabilities), + } + + config, configDiags := Config(ctx, proto5.Config, ephemeralResourceSchema) + + diags.Append(configDiags...) + + fw.Config = config + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/renewephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/renewephemeralresource.go new file mode 100644 index 000000000..c632310f4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/renewephemeralresource.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// RenewEphemeralResourceRequest returns the *fwserver.RenewEphemeralResourceRequest +// equivalent of a *tfprotov5.RenewEphemeralResourceRequest. +func RenewEphemeralResourceRequest(ctx context.Context, proto5 *tfprotov5.RenewEphemeralResourceRequest, ephemeralResource ephemeral.EphemeralResource, ephemeralResourceSchema fwschema.Schema) (*fwserver.RenewEphemeralResourceRequest, diag.Diagnostics) { + if proto5 == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if ephemeralResourceSchema == nil { + diags.AddError( + "Missing EphemeralResource Schema", + "An unexpected error was encountered when handling the request. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ) + + return nil, diags + } + + fw := &fwserver.RenewEphemeralResourceRequest{ + EphemeralResource: ephemeralResource, + EphemeralResourceSchema: ephemeralResourceSchema, + } + + privateData, privateDataDiags := privatestate.NewData(ctx, proto5.Private) + + diags.Append(privateDataDiags...) + + fw.Private = privateData + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/validateephemeralresourceconfig.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/validateephemeralresourceconfig.go new file mode 100644 index 000000000..ec1acb4b8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto5/validateephemeralresourceconfig.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// ValidateEphemeralResourceConfigRequest returns the *fwserver.ValidateEphemeralResourceConfigRequest +// equivalent of a *tfprotov5.ValidateEphemeralResourceConfigRequest. +func ValidateEphemeralResourceConfigRequest(ctx context.Context, proto5 *tfprotov5.ValidateEphemeralResourceConfigRequest, ephemeralResource ephemeral.EphemeralResource, ephemeralResourceSchema fwschema.Schema) (*fwserver.ValidateEphemeralResourceConfigRequest, diag.Diagnostics) { + if proto5 == nil { + return nil, nil + } + + fw := &fwserver.ValidateEphemeralResourceConfigRequest{} + + config, diags := Config(ctx, proto5.Config, ephemeralResourceSchema) + + fw.Config = config + fw.EphemeralResource = ephemeralResource + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/client_capabilities.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/client_capabilities.go index 6742a0303..cd9c92b9c 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/client_capabilities.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/client_capabilities.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/resource" ) @@ -75,3 +76,16 @@ func ImportStateClientCapabilities(in *tfprotov6.ImportResourceStateClientCapabi DeferralAllowed: in.DeferralAllowed, } } + +func OpenEphemeralResourceClientCapabilities(in *tfprotov6.OpenEphemeralResourceClientCapabilities) ephemeral.OpenClientCapabilities { + if in == nil { + // Client did not indicate any supported capabilities + return ephemeral.OpenClientCapabilities{ + DeferralAllowed: false, + } + } + + return ephemeral.OpenClientCapabilities{ + DeferralAllowed: in.DeferralAllowed, + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/closeephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/closeephemeralresource.go new file mode 100644 index 000000000..c98050fd1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/closeephemeralresource.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// CloseEphemeralResourceRequest returns the *fwserver.CloseEphemeralResourceRequest +// equivalent of a *tfprotov6.CloseEphemeralResourceRequest. +func CloseEphemeralResourceRequest(ctx context.Context, proto6 *tfprotov6.CloseEphemeralResourceRequest, ephemeralResource ephemeral.EphemeralResource, ephemeralResourceSchema fwschema.Schema) (*fwserver.CloseEphemeralResourceRequest, diag.Diagnostics) { + if proto6 == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if ephemeralResourceSchema == nil { + diags.AddError( + "Missing EphemeralResource Schema", + "An unexpected error was encountered when handling the request. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ) + + return nil, diags + } + + fw := &fwserver.CloseEphemeralResourceRequest{ + EphemeralResource: ephemeralResource, + EphemeralResourceSchema: ephemeralResourceSchema, + } + + privateData, privateDataDiags := privatestate.NewData(ctx, proto6.Private) + + diags.Append(privateDataDiags...) + + fw.Private = privateData + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/ephemeral_result_data.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/ephemeral_result_data.go new file mode 100644 index 000000000..1fef00304 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/ephemeral_result_data.go @@ -0,0 +1,53 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// EphemeralResultData returns the *tfsdk.EphemeralResultData for a *tfprotov6.DynamicValue and +// fwschema.Schema. +func EphemeralResultData(ctx context.Context, proto6DynamicValue *tfprotov6.DynamicValue, schema fwschema.Schema) (*tfsdk.EphemeralResultData, diag.Diagnostics) { + if proto6DynamicValue == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if schema == nil { + diags.AddError( + "Unable to Convert Ephemeral Result Data", + "An unexpected error was encountered when converting the ephemeral result data from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ) + + return nil, diags + } + + data, dynamicValueDiags := DynamicValue(ctx, proto6DynamicValue, schema, fwschemadata.DataDescriptionEphemeralResultData) + + diags.Append(dynamicValueDiags...) + + if diags.HasError() { + return nil, diags + } + + fw := &tfsdk.EphemeralResultData{ + Raw: data.TerraformValue, + Schema: schema, + } + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/openephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/openephemeralresource.go new file mode 100644 index 000000000..196c6c0b0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/openephemeralresource.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// OpenEphemeralResourceRequest returns the *fwserver.OpenEphemeralResourceRequest +// equivalent of a *tfprotov6.OpenEphemeralResourceRequest. +func OpenEphemeralResourceRequest(ctx context.Context, proto6 *tfprotov6.OpenEphemeralResourceRequest, ephemeralResource ephemeral.EphemeralResource, ephemeralResourceSchema fwschema.Schema) (*fwserver.OpenEphemeralResourceRequest, diag.Diagnostics) { + if proto6 == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if ephemeralResourceSchema == nil { + diags.AddError( + "Missing EphemeralResource Schema", + "An unexpected error was encountered when handling the request. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ) + + return nil, diags + } + + fw := &fwserver.OpenEphemeralResourceRequest{ + EphemeralResource: ephemeralResource, + EphemeralResourceSchema: ephemeralResourceSchema, + ClientCapabilities: OpenEphemeralResourceClientCapabilities(proto6.ClientCapabilities), + } + + config, configDiags := Config(ctx, proto6.Config, ephemeralResourceSchema) + + diags.Append(configDiags...) + + fw.Config = config + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/renewephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/renewephemeralresource.go new file mode 100644 index 000000000..9ee02f7c1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/renewephemeralresource.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// RenewEphemeralResourceRequest returns the *fwserver.RenewEphemeralResourceRequest +// equivalent of a *tfprotov6.RenewEphemeralResourceRequest. +func RenewEphemeralResourceRequest(ctx context.Context, proto6 *tfprotov6.RenewEphemeralResourceRequest, ephemeralResource ephemeral.EphemeralResource, ephemeralResourceSchema fwschema.Schema) (*fwserver.RenewEphemeralResourceRequest, diag.Diagnostics) { + if proto6 == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if ephemeralResourceSchema == nil { + diags.AddError( + "Missing EphemeralResource Schema", + "An unexpected error was encountered when handling the request. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ) + + return nil, diags + } + + fw := &fwserver.RenewEphemeralResourceRequest{ + EphemeralResource: ephemeralResource, + EphemeralResourceSchema: ephemeralResourceSchema, + } + + privateData, privateDataDiags := privatestate.NewData(ctx, proto6.Private) + + diags.Append(privateDataDiags...) + + fw.Private = privateData + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/validateephemeralresourceconfig.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/validateephemeralresourceconfig.go new file mode 100644 index 000000000..f913ede97 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fromproto6/validateephemeralresourceconfig.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// ValidateEphemeralResourceConfigRequest returns the *fwserver.ValidateEphemeralResourceConfigRequest +// equivalent of a *tfprotov6.ValidateEphemeralResourceConfigRequest. +func ValidateEphemeralResourceConfigRequest(ctx context.Context, proto6 *tfprotov6.ValidateEphemeralResourceConfigRequest, ephemeralResource ephemeral.EphemeralResource, ephemeralResourceSchema fwschema.Schema) (*fwserver.ValidateEphemeralResourceConfigRequest, diag.Diagnostics) { + if proto6 == nil { + return nil, nil + } + + fw := &fwserver.ValidateEphemeralResourceConfigRequest{} + + config, diags := Config(ctx, proto6.Config, ephemeralResourceSchema) + + fw.Config = config + fw.EphemeralResource = ephemeralResource + + return fw, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata/data_description.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata/data_description.go index c002e9883..70ae62c76 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata/data_description.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata/data_description.go @@ -15,6 +15,10 @@ const ( // DataDescriptionState is used for Data that represents // a state-based value. DataDescriptionState DataDescription = "state" + + // DataDescriptionEphemeralResultData is used for Data that represents + // the result of an ephemeral operation. + DataDescriptionEphemeralResultData DataDescription = "ephemeral result data" ) // DataDescription is a human friendly type for Data. Used in error @@ -40,6 +44,8 @@ func (d DataDescription) Title() string { return "Plan" case DataDescriptionState: return "State" + case DataDescriptionEphemeralResultData: + return "Ephemeral Result Data" default: return "Data" } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server.go index 5a0f90722..b6f2bc85e 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/logging" @@ -33,6 +34,11 @@ type Server struct { // to [resource.ConfigureRequest.ProviderData]. ResourceConfigureData any + // EphemeralResourceConfigureData is the + // [provider.ConfigureResponse.EphemeralResourceData] field value which is passed + // to [ephemeral.ConfigureRequest.ProviderData]. + EphemeralResourceConfigureData any + // dataSourceSchemas is the cached DataSource Schemas for RPCs that need to // convert configuration data from the protocol. If not found, it will be // fetched from the DataSourceType.GetSchema() method. @@ -56,6 +62,29 @@ type Server struct { // access from race conditions. dataSourceTypesMutex sync.Mutex + // ephemeralResourceSchemas is the cached EphemeralResource Schemas for RPCs that need to + // convert configuration data from the protocol. If not found, it will be + // fetched from the EphemeralResourceType.GetSchema() method. + ephemeralResourceSchemas map[string]fwschema.Schema + + // ephemeralResourceSchemasMutex is a mutex to protect concurrent ephemeralResourceSchemas + // access from race conditions. + ephemeralResourceSchemasMutex sync.RWMutex + + // ephemeralResourceFuncs is the cached EphemeralResource functions for RPCs that need to + // access ephemeral resources. If not found, it will be fetched from the + // Provider.EphemeralResources() method. + ephemeralResourceFuncs map[string]func() ephemeral.EphemeralResource + + // ephemeralResourceFuncsDiags is the cached Diagnostics obtained while populating + // ephemeralResourceFuncs. This is to ensure any warnings or errors are also + // returned appropriately when fetching ephemeralResourceFuncs. + ephemeralResourceFuncsDiags diag.Diagnostics + + // ephemeralResourceFuncsMutex is a mutex to protect concurrent ephemeralResourceFuncs + // access from race conditions. + ephemeralResourceFuncsMutex sync.Mutex + // deferred indicates an automatic provider deferral. When this is set, // the provider will automatically defer the PlanResourceChange, ReadResource, // ImportResourceState, and ReadDataSource RPCs. diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_closeephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_closeephemeralresource.go new file mode 100644 index 000000000..a3d4d68f6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_closeephemeralresource.go @@ -0,0 +1,76 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwserver + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" +) + +// CloseEphemeralResourceRequest is the framework server request for the +// CloseEphemeralResource RPC. +type CloseEphemeralResourceRequest struct { + Private *privatestate.Data + EphemeralResourceSchema fwschema.Schema + EphemeralResource ephemeral.EphemeralResource +} + +// CloseEphemeralResourceResponse is the framework server response for the +// CloseEphemeralResource RPC. +type CloseEphemeralResourceResponse struct { + Diagnostics diag.Diagnostics +} + +// CloseEphemeralResource implements the framework server CloseEphemeralResource RPC. +func (s *Server) CloseEphemeralResource(ctx context.Context, req *CloseEphemeralResourceRequest, resp *CloseEphemeralResourceResponse) { + if req == nil { + return + } + + if ephemeralResourceWithConfigure, ok := req.EphemeralResource.(ephemeral.EphemeralResourceWithConfigure); ok { + logging.FrameworkTrace(ctx, "EphemeralResource implements EphemeralResourceWithConfigure") + + configureReq := ephemeral.ConfigureRequest{ + ProviderData: s.EphemeralResourceConfigureData, + } + configureResp := ephemeral.ConfigureResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource Configure") + ephemeralResourceWithConfigure.Configure(ctx, configureReq, &configureResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource Configure") + + resp.Diagnostics.Append(configureResp.Diagnostics...) + + if resp.Diagnostics.HasError() { + return + } + } + + resourceWithClose, ok := req.EphemeralResource.(ephemeral.EphemeralResourceWithClose) + if !ok { + // Terraform will always give the ephemeral resource an opportunity to close, so if it's not implemented we can safely return. + return + } + + privateProviderData := privatestate.EmptyProviderData(ctx) + if req.Private != nil && req.Private.Provider != nil { + privateProviderData = req.Private.Provider + } + + closeReq := ephemeral.CloseRequest{ + Private: privateProviderData, + } + closeResp := ephemeral.CloseResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource Close") + resourceWithClose.Close(ctx, closeReq, &closeResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource Close") + + resp.Diagnostics = closeResp.Diagnostics +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_configureprovider.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_configureprovider.go index 2e04bc046..0b1807bce 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_configureprovider.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_configureprovider.go @@ -37,4 +37,5 @@ func (s *Server) ConfigureProvider(ctx context.Context, req *provider.ConfigureR s.deferred = resp.Deferred s.DataSourceConfigureData = resp.DataSourceData s.ResourceConfigureData = resp.ResourceData + s.EphemeralResourceConfigureData = resp.EphemeralResourceData } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_ephemeralresources.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_ephemeralresources.go new file mode 100644 index 000000000..15d54e118 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_ephemeralresources.go @@ -0,0 +1,198 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwserver + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/provider" +) + +// EphemeralResource returns the EphemeralResource for a given type name. +func (s *Server) EphemeralResource(ctx context.Context, typeName string) (ephemeral.EphemeralResource, diag.Diagnostics) { + ephemeralResourceFuncs, diags := s.EphemeralResourceFuncs(ctx) + + ephemeralResourceFunc, ok := ephemeralResourceFuncs[typeName] + + if !ok { + diags.AddError( + "Ephemeral Resource Type Not Found", + fmt.Sprintf("No ephemeral resource type named %q was found in the provider.", typeName), + ) + + return nil, diags + } + + return ephemeralResourceFunc(), diags +} + +// EphemeralResourceFuncs returns a map of EphemeralResource functions. The results are cached +// on first use. +func (s *Server) EphemeralResourceFuncs(ctx context.Context) (map[string]func() ephemeral.EphemeralResource, diag.Diagnostics) { + logging.FrameworkTrace(ctx, "Checking EphemeralResourceFuncs lock") + s.ephemeralResourceFuncsMutex.Lock() + defer s.ephemeralResourceFuncsMutex.Unlock() + + if s.ephemeralResourceFuncs != nil { + return s.ephemeralResourceFuncs, s.ephemeralResourceFuncsDiags + } + + providerTypeName := s.ProviderTypeName(ctx) + s.ephemeralResourceFuncs = make(map[string]func() ephemeral.EphemeralResource) + + provider, ok := s.Provider.(provider.ProviderWithEphemeralResources) + + if !ok { + // Only ephemeral resource specific RPCs should return diagnostics about the + // provider not implementing ephemeral resources or missing ephemeral resources. + return s.ephemeralResourceFuncs, s.ephemeralResourceFuncsDiags + } + + logging.FrameworkTrace(ctx, "Calling provider defined Provider EphemeralResources") + ephemeralResourceFuncsSlice := provider.EphemeralResources(ctx) + logging.FrameworkTrace(ctx, "Called provider defined Provider EphemeralResources") + + for _, ephemeralResourceFunc := range ephemeralResourceFuncsSlice { + ephemeralResource := ephemeralResourceFunc() + + ephemeralResourceTypeNameReq := ephemeral.MetadataRequest{ + ProviderTypeName: providerTypeName, + } + ephemeralResourceTypeNameResp := ephemeral.MetadataResponse{} + + ephemeralResource.Metadata(ctx, ephemeralResourceTypeNameReq, &ephemeralResourceTypeNameResp) + + if ephemeralResourceTypeNameResp.TypeName == "" { + s.ephemeralResourceFuncsDiags.AddError( + "Ephemeral Resource Type Name Missing", + fmt.Sprintf("The %T EphemeralResource returned an empty string from the Metadata method. ", ephemeralResource)+ + "This is always an issue with the provider and should be reported to the provider developers.", + ) + continue + } + + logging.FrameworkTrace(ctx, "Found ephemeral resource type", map[string]interface{}{logging.KeyEphemeralResourceType: ephemeralResourceTypeNameResp.TypeName}) + + if _, ok := s.ephemeralResourceFuncs[ephemeralResourceTypeNameResp.TypeName]; ok { + s.ephemeralResourceFuncsDiags.AddError( + "Duplicate Ephemeral Resource Type Defined", + fmt.Sprintf("The %s ephemeral resource type name was returned for multiple ephemeral resources. ", ephemeralResourceTypeNameResp.TypeName)+ + "Ephemeral resource type names must be unique. "+ + "This is always an issue with the provider and should be reported to the provider developers.", + ) + continue + } + + s.ephemeralResourceFuncs[ephemeralResourceTypeNameResp.TypeName] = ephemeralResourceFunc + } + + return s.ephemeralResourceFuncs, s.ephemeralResourceFuncsDiags +} + +// EphemeralResourceMetadatas returns a slice of EphemeralResourceMetadata for the GetMetadata +// RPC. +func (s *Server) EphemeralResourceMetadatas(ctx context.Context) ([]EphemeralResourceMetadata, diag.Diagnostics) { + ephemeralResourceFuncs, diags := s.EphemeralResourceFuncs(ctx) + + ephemeralResourceMetadatas := make([]EphemeralResourceMetadata, 0, len(ephemeralResourceFuncs)) + + for typeName := range ephemeralResourceFuncs { + ephemeralResourceMetadatas = append(ephemeralResourceMetadatas, EphemeralResourceMetadata{ + TypeName: typeName, + }) + } + + return ephemeralResourceMetadatas, diags +} + +// EphemeralResourceSchema returns the EphemeralResource Schema for the given type name and +// caches the result for later EphemeralResource operations. +func (s *Server) EphemeralResourceSchema(ctx context.Context, typeName string) (fwschema.Schema, diag.Diagnostics) { + s.ephemeralResourceSchemasMutex.RLock() + ephemeralResourceSchema, ok := s.ephemeralResourceSchemas[typeName] + s.ephemeralResourceSchemasMutex.RUnlock() + + if ok { + return ephemeralResourceSchema, nil + } + + var diags diag.Diagnostics + + ephemeralResource, ephemeralResourceDiags := s.EphemeralResource(ctx, typeName) + + diags.Append(ephemeralResourceDiags...) + + if diags.HasError() { + return nil, diags + } + + schemaReq := ephemeral.SchemaRequest{} + schemaResp := ephemeral.SchemaResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource Schema method", map[string]interface{}{logging.KeyEphemeralResourceType: typeName}) + ephemeralResource.Schema(ctx, schemaReq, &schemaResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource Schema method", map[string]interface{}{logging.KeyEphemeralResourceType: typeName}) + + diags.Append(schemaResp.Diagnostics...) + + if diags.HasError() { + return schemaResp.Schema, diags + } + + s.ephemeralResourceSchemasMutex.Lock() + + if s.ephemeralResourceSchemas == nil { + s.ephemeralResourceSchemas = make(map[string]fwschema.Schema) + } + + s.ephemeralResourceSchemas[typeName] = schemaResp.Schema + + s.ephemeralResourceSchemasMutex.Unlock() + + return schemaResp.Schema, diags +} + +// EphemeralResourceSchemas returns a map of EphemeralResource Schemas for the +// GetProviderSchema RPC without caching since not all schemas are guaranteed to +// be necessary for later provider operations. The schema implementations are +// also validated. +func (s *Server) EphemeralResourceSchemas(ctx context.Context) (map[string]fwschema.Schema, diag.Diagnostics) { + ephemeralResourceSchemas := make(map[string]fwschema.Schema) + + ephemeralResourceFuncs, diags := s.EphemeralResourceFuncs(ctx) + + for typeName, ephemeralResourceFunc := range ephemeralResourceFuncs { + ephemeralResource := ephemeralResourceFunc() + + schemaReq := ephemeral.SchemaRequest{} + schemaResp := ephemeral.SchemaResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource Schema", map[string]interface{}{logging.KeyEphemeralResourceType: typeName}) + ephemeralResource.Schema(ctx, schemaReq, &schemaResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource Schema", map[string]interface{}{logging.KeyEphemeralResourceType: typeName}) + + diags.Append(schemaResp.Diagnostics...) + + if schemaResp.Diagnostics.HasError() { + continue + } + + validateDiags := schemaResp.Schema.ValidateImplementation(ctx) + + diags.Append(validateDiags...) + + if validateDiags.HasError() { + continue + } + + ephemeralResourceSchemas[typeName] = schemaResp.Schema + } + + return ephemeralResourceSchemas, diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_getmetadata.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_getmetadata.go index ebd0728a9..458694f2b 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_getmetadata.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_getmetadata.go @@ -18,6 +18,7 @@ type GetMetadataRequest struct{} type GetMetadataResponse struct { DataSources []DataSourceMetadata Diagnostics diag.Diagnostics + EphemeralResources []EphemeralResourceMetadata Functions []FunctionMetadata Resources []ResourceMetadata ServerCapabilities *ServerCapabilities @@ -30,6 +31,13 @@ type DataSourceMetadata struct { TypeName string } +// EphemeralResourceMetadata is the framework server equivalent of the +// tfprotov5.EphemeralResourceMetadata and tfprotov6.EphemeralResourceMetadata types. +type EphemeralResourceMetadata struct { + // TypeName is the name of the ephemeral resource. + TypeName string +} + // FunctionMetadata is the framework server equivalent of the // tfprotov5.FunctionMetadata and tfprotov6.FunctionMetadata types. type FunctionMetadata struct { @@ -47,6 +55,7 @@ type ResourceMetadata struct { // GetMetadata implements the framework server GetMetadata RPC. func (s *Server) GetMetadata(ctx context.Context, req *GetMetadataRequest, resp *GetMetadataResponse) { resp.DataSources = []DataSourceMetadata{} + resp.EphemeralResources = []EphemeralResourceMetadata{} resp.Functions = []FunctionMetadata{} resp.Resources = []ResourceMetadata{} resp.ServerCapabilities = s.ServerCapabilities() @@ -55,6 +64,10 @@ func (s *Server) GetMetadata(ctx context.Context, req *GetMetadataRequest, resp resp.Diagnostics.Append(diags...) + ephemeralResourceMetadatas, diags := s.EphemeralResourceMetadatas(ctx) + + resp.Diagnostics.Append(diags...) + functionMetadatas, diags := s.FunctionMetadatas(ctx) resp.Diagnostics.Append(diags...) @@ -68,6 +81,7 @@ func (s *Server) GetMetadata(ctx context.Context, req *GetMetadataRequest, resp } resp.DataSources = datasourceMetadatas + resp.EphemeralResources = ephemeralResourceMetadatas resp.Functions = functionMetadatas resp.Resources = resourceMetadatas } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_getproviderschema.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_getproviderschema.go index afcca8352..b8061dd10 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_getproviderschema.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_getproviderschema.go @@ -18,13 +18,14 @@ type GetProviderSchemaRequest struct{} // GetProviderSchemaResponse is the framework server response for the // GetProviderSchema RPC. type GetProviderSchemaResponse struct { - ServerCapabilities *ServerCapabilities - Provider fwschema.Schema - ProviderMeta fwschema.Schema - ResourceSchemas map[string]fwschema.Schema - DataSourceSchemas map[string]fwschema.Schema - FunctionDefinitions map[string]function.Definition - Diagnostics diag.Diagnostics + ServerCapabilities *ServerCapabilities + Provider fwschema.Schema + ProviderMeta fwschema.Schema + ResourceSchemas map[string]fwschema.Schema + DataSourceSchemas map[string]fwschema.Schema + EphemeralResourceSchemas map[string]fwschema.Schema + FunctionDefinitions map[string]function.Definition + Diagnostics diag.Diagnostics } // GetProviderSchema implements the framework server GetProviderSchema RPC. @@ -80,4 +81,14 @@ func (s *Server) GetProviderSchema(ctx context.Context, req *GetProviderSchemaRe } resp.FunctionDefinitions = functions + + ephemeralResourceSchemas, diags := s.EphemeralResourceSchemas(ctx) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + resp.EphemeralResourceSchemas = ephemeralResourceSchemas } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_openephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_openephemeralresource.go new file mode 100644 index 000000000..18dc09bcf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_openephemeralresource.go @@ -0,0 +1,113 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwserver + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// OpenEphemeralResourceRequest is the framework server request for the +// OpenEphemeralResource RPC. +type OpenEphemeralResourceRequest struct { + ClientCapabilities ephemeral.OpenClientCapabilities + Config *tfsdk.Config + EphemeralResourceSchema fwschema.Schema + EphemeralResource ephemeral.EphemeralResource +} + +// OpenEphemeralResourceResponse is the framework server response for the +// OpenEphemeralResource RPC. +type OpenEphemeralResourceResponse struct { + Result *tfsdk.EphemeralResultData + Private *privatestate.Data + Diagnostics diag.Diagnostics + RenewAt time.Time + Deferred *ephemeral.Deferred +} + +// OpenEphemeralResource implements the framework server OpenEphemeralResource RPC. +func (s *Server) OpenEphemeralResource(ctx context.Context, req *OpenEphemeralResourceRequest, resp *OpenEphemeralResourceResponse) { + if req == nil { + return + } + + if s.deferred != nil { + logging.FrameworkDebug(ctx, "Provider has deferred response configured, automatically returning deferred response.", + map[string]interface{}{ + logging.KeyDeferredReason: s.deferred.Reason.String(), + }, + ) + // Send an unknown value for the ephemeral resource. This will replace any configured values + // for ease of implementation as Terraform Core currently does not use these values for + // deferred actions, but this design could change in the future. + resp.Result = &tfsdk.EphemeralResultData{ + Raw: tftypes.NewValue(req.EphemeralResourceSchema.Type().TerraformType(ctx), tftypes.UnknownValue), + Schema: req.EphemeralResourceSchema, + } + resp.Deferred = &ephemeral.Deferred{ + Reason: ephemeral.DeferredReason(s.deferred.Reason), + } + return + } + + if ephemeralResourceWithConfigure, ok := req.EphemeralResource.(ephemeral.EphemeralResourceWithConfigure); ok { + logging.FrameworkTrace(ctx, "EphemeralResource implements EphemeralResourceWithConfigure") + + configureReq := ephemeral.ConfigureRequest{ + ProviderData: s.EphemeralResourceConfigureData, + } + configureResp := ephemeral.ConfigureResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource Configure") + ephemeralResourceWithConfigure.Configure(ctx, configureReq, &configureResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource Configure") + + resp.Diagnostics.Append(configureResp.Diagnostics...) + + if resp.Diagnostics.HasError() { + return + } + } + + openReq := ephemeral.OpenRequest{ + ClientCapabilities: req.ClientCapabilities, + Config: tfsdk.Config{ + Schema: req.EphemeralResourceSchema, + }, + } + openResp := ephemeral.OpenResponse{ + Result: tfsdk.EphemeralResultData{ + Schema: req.EphemeralResourceSchema, + }, + Private: privatestate.EmptyProviderData(ctx), + } + + if req.Config != nil { + openReq.Config = *req.Config + openResp.Result.Raw = req.Config.Raw.Copy() + } + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource Open") + req.EphemeralResource.Open(ctx, openReq, &openResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource Open") + + resp.Diagnostics = openResp.Diagnostics + resp.Result = &openResp.Result + resp.RenewAt = openResp.RenewAt + resp.Deferred = openResp.Deferred + + resp.Private = privatestate.EmptyData(ctx) + if openResp.Private != nil { + resp.Private.Provider = openResp.Private + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_renewephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_renewephemeralresource.go new file mode 100644 index 000000000..d23308de6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_renewephemeralresource.go @@ -0,0 +1,98 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwserver + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" +) + +// RenewEphemeralResourceRequest is the framework server request for the +// RenewEphemeralResource RPC. +type RenewEphemeralResourceRequest struct { + Private *privatestate.Data + EphemeralResourceSchema fwschema.Schema + EphemeralResource ephemeral.EphemeralResource +} + +// RenewEphemeralResourceResponse is the framework server response for the +// RenewEphemeralResource RPC. +type RenewEphemeralResourceResponse struct { + Private *privatestate.Data + Diagnostics diag.Diagnostics + RenewAt time.Time +} + +// RenewEphemeralResource implements the framework server RenewEphemeralResource RPC. +func (s *Server) RenewEphemeralResource(ctx context.Context, req *RenewEphemeralResourceRequest, resp *RenewEphemeralResourceResponse) { + if req == nil { + return + } + + if ephemeralResourceWithConfigure, ok := req.EphemeralResource.(ephemeral.EphemeralResourceWithConfigure); ok { + logging.FrameworkTrace(ctx, "EphemeralResource implements EphemeralResourceWithConfigure") + + configureReq := ephemeral.ConfigureRequest{ + ProviderData: s.EphemeralResourceConfigureData, + } + configureResp := ephemeral.ConfigureResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource Configure") + ephemeralResourceWithConfigure.Configure(ctx, configureReq, &configureResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource Configure") + + resp.Diagnostics.Append(configureResp.Diagnostics...) + + if resp.Diagnostics.HasError() { + return + } + } + + resourceWithRenew, ok := req.EphemeralResource.(ephemeral.EphemeralResourceWithRenew) + if !ok { + resp.Diagnostics.AddError( + "Ephemeral Resource Renew Not Implemented", + "An unexpected error was encountered when renewing the ephemeral resource. Terraform sent a renewal request for an "+ + "ephemeral resource that has not implemented renewal logic.\n\n"+ + "Please report this to the provider developer.", + ) + return + } + + // Ensure that resp.Private is never nil. + resp.Private = privatestate.EmptyData(ctx) + if req.Private != nil { + // Overwrite resp.Private with req.Private providing it is not nil. + resp.Private = req.Private + + // Ensure that resp.Private.Provider is never nil. + if resp.Private.Provider == nil { + resp.Private.Provider = privatestate.EmptyProviderData(ctx) + } + } + + renewReq := ephemeral.RenewRequest{ + Private: resp.Private.Provider, + } + renewResp := ephemeral.RenewResponse{ + Private: renewReq.Private, + } + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource Renew") + resourceWithRenew.Renew(ctx, renewReq, &renewResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource Renew") + + resp.Diagnostics = renewResp.Diagnostics + resp.RenewAt = renewResp.RenewAt + + if renewResp.Private != nil { + resp.Private.Provider = renewResp.Private + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_validateephemeralresourceconfig.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_validateephemeralresourceconfig.go new file mode 100644 index 000000000..a99a0dbfb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/fwserver/server_validateephemeralresourceconfig.go @@ -0,0 +1,109 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwserver + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +// ValidateEphemeralResourceConfigRequest is the framework server request for the +// ValidateEphemeralResourceConfig RPC. +type ValidateEphemeralResourceConfigRequest struct { + Config *tfsdk.Config + EphemeralResource ephemeral.EphemeralResource +} + +// ValidateEphemeralResourceConfigResponse is the framework server response for the +// ValidateEphemeralResourceConfig RPC. +type ValidateEphemeralResourceConfigResponse struct { + Diagnostics diag.Diagnostics +} + +// ValidateEphemeralResourceConfig implements the framework server ValidateEphemeralResourceConfig RPC. +func (s *Server) ValidateEphemeralResourceConfig(ctx context.Context, req *ValidateEphemeralResourceConfigRequest, resp *ValidateEphemeralResourceConfigResponse) { + if req == nil || req.Config == nil { + return + } + + if ephemeralResourceWithConfigure, ok := req.EphemeralResource.(ephemeral.EphemeralResourceWithConfigure); ok { + logging.FrameworkTrace(ctx, "EphemeralResource implements EphemeralResourceWithConfigure") + + configureReq := ephemeral.ConfigureRequest{ + ProviderData: s.EphemeralResourceConfigureData, + } + configureResp := ephemeral.ConfigureResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource Configure") + ephemeralResourceWithConfigure.Configure(ctx, configureReq, &configureResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource Configure") + + resp.Diagnostics.Append(configureResp.Diagnostics...) + + if resp.Diagnostics.HasError() { + return + } + } + + vdscReq := ephemeral.ValidateConfigRequest{ + Config: *req.Config, + } + + if ephemeralResourceWithConfigValidators, ok := req.EphemeralResource.(ephemeral.EphemeralResourceWithConfigValidators); ok { + logging.FrameworkTrace(ctx, "EphemeralResource implements EphemeralResourceWithConfigValidators") + + for _, configValidator := range ephemeralResourceWithConfigValidators.ConfigValidators(ctx) { + // Instantiate a new response for each request to prevent validators + // from modifying or removing diagnostics. + vdscResp := &ephemeral.ValidateConfigResponse{} + + logging.FrameworkTrace( + ctx, + "Calling provider defined EphemeralResourceConfigValidator", + map[string]interface{}{ + logging.KeyDescription: configValidator.Description(ctx), + }, + ) + configValidator.ValidateEphemeralResource(ctx, vdscReq, vdscResp) + logging.FrameworkTrace( + ctx, + "Called provider defined EphemeralResourceConfigValidator", + map[string]interface{}{ + logging.KeyDescription: configValidator.Description(ctx), + }, + ) + + resp.Diagnostics.Append(vdscResp.Diagnostics...) + } + } + + if ephemeralResourceWithValidateConfig, ok := req.EphemeralResource.(ephemeral.EphemeralResourceWithValidateConfig); ok { + logging.FrameworkTrace(ctx, "EphemeralResource implements EphemeralResourceWithValidateConfig") + + // Instantiate a new response for each request to prevent validators + // from modifying or removing diagnostics. + vdscResp := &ephemeral.ValidateConfigResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined EphemeralResource ValidateConfig") + ephemeralResourceWithValidateConfig.ValidateConfig(ctx, vdscReq, vdscResp) + logging.FrameworkTrace(ctx, "Called provider defined EphemeralResource ValidateConfig") + + resp.Diagnostics.Append(vdscResp.Diagnostics...) + } + + validateSchemaReq := ValidateSchemaRequest{ + Config: *req.Config, + } + // Instantiate a new response for each request to prevent validators + // from modifying or removing diagnostics. + validateSchemaResp := ValidateSchemaResponse{} + + SchemaValidate(ctx, req.Config.Schema, validateSchemaReq, &validateSchemaResp) + + resp.Diagnostics.Append(validateSchemaResp.Diagnostics...) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/logging/keys.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/logging/keys.go index 312a839ac..1443710c9 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/logging/keys.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/logging/keys.go @@ -18,6 +18,9 @@ const ( // The type of data source being operated on, such as "archive_file" KeyDataSourceType = "tf_data_source_type" + // The type of ephemeral resource being operated on, such as "random_password" + KeyEphemeralResourceType = "tf_ephemeral_resource_type" + // The Deferred reason for an RPC response KeyDeferredReason = "tf_deferred_reason" diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/privatestate/data.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/privatestate/data.go index 3e858026a..5493a9d97 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/privatestate/data.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/privatestate/data.go @@ -72,9 +72,9 @@ func (d *Data) Bytes(ctx context.Context) ([]byte, diag.Diagnostics) { if !json.Valid(v) { diags.AddError( "Error Encoding Private State", - fmt.Sprintf("An error was encountered when validating private state value."+ + "An error was encountered when validating private state value."+ fmt.Sprintf("The value associated with key %q is is not valid JSON.\n\n", k)+ - "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer."), + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", ) tflog.Error(ctx, "error encoding private state: invalid JSON value", map[string]interface{}{"key": k, "value": v}) diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_closeephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_closeephemeralresource.go new file mode 100644 index 000000000..1e07cef33 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_closeephemeralresource.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto5server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// CloseEphemeralResource satisfies the tfprotov5.ProviderServer interface. +func (s *Server) CloseEphemeralResource(ctx context.Context, proto5Req *tfprotov5.CloseEphemeralResourceRequest) (*tfprotov5.CloseEphemeralResourceResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwResp := &fwserver.CloseEphemeralResourceResponse{} + + ephemeralResource, diags := s.FrameworkServer.EphemeralResource(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.CloseEphemeralResourceResponse(ctx, fwResp), nil + } + + ephemeralResourceSchema, diags := s.FrameworkServer.EphemeralResourceSchema(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.CloseEphemeralResourceResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto5.CloseEphemeralResourceRequest(ctx, proto5Req, ephemeralResource, ephemeralResourceSchema) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.CloseEphemeralResourceResponse(ctx, fwResp), nil + } + + s.FrameworkServer.CloseEphemeralResource(ctx, fwReq, fwResp) + + return toproto5.CloseEphemeralResourceResponse(ctx, fwResp), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_openephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_openephemeralresource.go new file mode 100644 index 000000000..b972bd4d8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_openephemeralresource.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto5server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// OpenEphemeralResource satisfies the tfprotov5.ProviderServer interface. +func (s *Server) OpenEphemeralResource(ctx context.Context, proto5Req *tfprotov5.OpenEphemeralResourceRequest) (*tfprotov5.OpenEphemeralResourceResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwResp := &fwserver.OpenEphemeralResourceResponse{} + + ephemeralResource, diags := s.FrameworkServer.EphemeralResource(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.OpenEphemeralResourceResponse(ctx, fwResp), nil + } + + ephemeralResourceSchema, diags := s.FrameworkServer.EphemeralResourceSchema(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.OpenEphemeralResourceResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto5.OpenEphemeralResourceRequest(ctx, proto5Req, ephemeralResource, ephemeralResourceSchema) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.OpenEphemeralResourceResponse(ctx, fwResp), nil + } + + s.FrameworkServer.OpenEphemeralResource(ctx, fwReq, fwResp) + + return toproto5.OpenEphemeralResourceResponse(ctx, fwResp), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_renewephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_renewephemeralresource.go new file mode 100644 index 000000000..76be1f019 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_renewephemeralresource.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto5server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// RenewEphemeralResource satisfies the tfprotov5.ProviderServer interface. +func (s *Server) RenewEphemeralResource(ctx context.Context, proto5Req *tfprotov5.RenewEphemeralResourceRequest) (*tfprotov5.RenewEphemeralResourceResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwResp := &fwserver.RenewEphemeralResourceResponse{} + + ephemeralResource, diags := s.FrameworkServer.EphemeralResource(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.RenewEphemeralResourceResponse(ctx, fwResp), nil + } + + ephemeralResourceSchema, diags := s.FrameworkServer.EphemeralResourceSchema(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.RenewEphemeralResourceResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto5.RenewEphemeralResourceRequest(ctx, proto5Req, ephemeralResource, ephemeralResourceSchema) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.RenewEphemeralResourceResponse(ctx, fwResp), nil + } + + s.FrameworkServer.RenewEphemeralResource(ctx, fwReq, fwResp) + + return toproto5.RenewEphemeralResourceResponse(ctx, fwResp), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_validateephemeralresourceconfig.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_validateephemeralresourceconfig.go new file mode 100644 index 000000000..04018a01b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto5server/server_validateephemeralresourceconfig.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto5server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// ValidateEphemeralResourceConfig satisfies the tfprotov5.ProviderServer interface. +func (s *Server) ValidateEphemeralResourceConfig(ctx context.Context, proto5Req *tfprotov5.ValidateEphemeralResourceConfigRequest) (*tfprotov5.ValidateEphemeralResourceConfigResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwResp := &fwserver.ValidateEphemeralResourceConfigResponse{} + + ephemeralResource, diags := s.FrameworkServer.EphemeralResource(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.ValidateEphemeralResourceConfigResponse(ctx, fwResp), nil + } + + ephemeralResourceSchema, diags := s.FrameworkServer.EphemeralResourceSchema(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.ValidateEphemeralResourceConfigResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto5.ValidateEphemeralResourceConfigRequest(ctx, proto5Req, ephemeralResource, ephemeralResourceSchema) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.ValidateEphemeralResourceConfigResponse(ctx, fwResp), nil + } + + s.FrameworkServer.ValidateEphemeralResourceConfig(ctx, fwReq, fwResp) + + return toproto5.ValidateEphemeralResourceConfigResponse(ctx, fwResp), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_closeephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_closeephemeralresource.go new file mode 100644 index 000000000..430ff2eaa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_closeephemeralresource.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto6server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// CloseEphemeralResource satisfies the tfprotov6.ProviderServer interface. +func (s *Server) CloseEphemeralResource(ctx context.Context, proto6Req *tfprotov6.CloseEphemeralResourceRequest) (*tfprotov6.CloseEphemeralResourceResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwResp := &fwserver.CloseEphemeralResourceResponse{} + + ephemeralResource, diags := s.FrameworkServer.EphemeralResource(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.CloseEphemeralResourceResponse(ctx, fwResp), nil + } + + ephemeralResourceSchema, diags := s.FrameworkServer.EphemeralResourceSchema(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.CloseEphemeralResourceResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto6.CloseEphemeralResourceRequest(ctx, proto6Req, ephemeralResource, ephemeralResourceSchema) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.CloseEphemeralResourceResponse(ctx, fwResp), nil + } + + s.FrameworkServer.CloseEphemeralResource(ctx, fwReq, fwResp) + + return toproto6.CloseEphemeralResourceResponse(ctx, fwResp), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_openephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_openephemeralresource.go new file mode 100644 index 000000000..5ec9b2dff --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_openephemeralresource.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto6server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// OpenEphemeralResource satisfies the tfprotov6.ProviderServer interface. +func (s *Server) OpenEphemeralResource(ctx context.Context, proto6Req *tfprotov6.OpenEphemeralResourceRequest) (*tfprotov6.OpenEphemeralResourceResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwResp := &fwserver.OpenEphemeralResourceResponse{} + + ephemeralResource, diags := s.FrameworkServer.EphemeralResource(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.OpenEphemeralResourceResponse(ctx, fwResp), nil + } + + ephemeralResourceSchema, diags := s.FrameworkServer.EphemeralResourceSchema(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.OpenEphemeralResourceResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto6.OpenEphemeralResourceRequest(ctx, proto6Req, ephemeralResource, ephemeralResourceSchema) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.OpenEphemeralResourceResponse(ctx, fwResp), nil + } + + s.FrameworkServer.OpenEphemeralResource(ctx, fwReq, fwResp) + + return toproto6.OpenEphemeralResourceResponse(ctx, fwResp), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_renewephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_renewephemeralresource.go new file mode 100644 index 000000000..e60657d4f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_renewephemeralresource.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto6server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// RenewEphemeralResource satisfies the tfprotov6.ProviderServer interface. +func (s *Server) RenewEphemeralResource(ctx context.Context, proto6Req *tfprotov6.RenewEphemeralResourceRequest) (*tfprotov6.RenewEphemeralResourceResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwResp := &fwserver.RenewEphemeralResourceResponse{} + + ephemeralResource, diags := s.FrameworkServer.EphemeralResource(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.RenewEphemeralResourceResponse(ctx, fwResp), nil + } + + ephemeralResourceSchema, diags := s.FrameworkServer.EphemeralResourceSchema(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.RenewEphemeralResourceResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto6.RenewEphemeralResourceRequest(ctx, proto6Req, ephemeralResource, ephemeralResourceSchema) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.RenewEphemeralResourceResponse(ctx, fwResp), nil + } + + s.FrameworkServer.RenewEphemeralResource(ctx, fwReq, fwResp) + + return toproto6.RenewEphemeralResourceResponse(ctx, fwResp), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_validateephemeralresourceconfig.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_validateephemeralresourceconfig.go new file mode 100644 index 000000000..822860326 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/proto6server/server_validateephemeralresourceconfig.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto6server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// ValidateEphemeralResourceConfig satisfies the tfprotov6.ProviderServer interface. +func (s *Server) ValidateEphemeralResourceConfig(ctx context.Context, proto6Req *tfprotov6.ValidateEphemeralResourceConfigRequest) (*tfprotov6.ValidateEphemeralResourceConfigResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwResp := &fwserver.ValidateEphemeralResourceConfigResponse{} + + ephemeralResource, diags := s.FrameworkServer.EphemeralResource(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.ValidateEphemeralResourceConfigResponse(ctx, fwResp), nil + } + + ephemeralResourceSchema, diags := s.FrameworkServer.EphemeralResourceSchema(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.ValidateEphemeralResourceConfigResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto6.ValidateEphemeralResourceConfigRequest(ctx, proto6Req, ephemeralResource, ephemeralResourceSchema) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.ValidateEphemeralResourceConfigResponse(ctx, fwResp), nil + } + + s.FrameworkServer.ValidateEphemeralResourceConfig(ctx, fwReq, fwResp) + + return toproto6.ValidateEphemeralResourceConfigResponse(ctx, fwResp), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/closeephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/closeephemeralresource.go new file mode 100644 index 000000000..5bc9484db --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/closeephemeralresource.go @@ -0,0 +1,25 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// CloseEphemeralResourceResponse returns the *tfprotov5.CloseEphemeralResourceResponse +// equivalent of a *fwserver.CloseEphemeralResourceResponse. +func CloseEphemeralResourceResponse(ctx context.Context, fw *fwserver.CloseEphemeralResourceResponse) *tfprotov5.CloseEphemeralResourceResponse { + if fw == nil { + return nil + } + + proto5 := &tfprotov5.CloseEphemeralResourceResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + } + + return proto5 +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/deferred.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/deferred.go index 3c5c4b1dc..42049a4da 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/deferred.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/deferred.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-go/tfprotov5" "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" "github.com/hashicorp/terraform-plugin-framework/resource" ) @@ -27,3 +28,12 @@ func ResourceDeferred(fw *resource.Deferred) *tfprotov5.Deferred { Reason: tfprotov5.DeferredReason(fw.Reason), } } + +func EphemeralResourceDeferred(fw *ephemeral.Deferred) *tfprotov5.Deferred { + if fw == nil { + return nil + } + return &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReason(fw.Reason), + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/ephemeral_result_data.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/ephemeral_result_data.go new file mode 100644 index 000000000..96490446f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/ephemeral_result_data.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// EphemeralResultData returns the *tfprotov5.DynamicValue for a *tfsdk.EphemeralResultData. +func EphemeralResultData(ctx context.Context, fw *tfsdk.EphemeralResultData) (*tfprotov5.DynamicValue, diag.Diagnostics) { + if fw == nil { + return nil, nil + } + + data := &fwschemadata.Data{ + Description: fwschemadata.DataDescriptionEphemeralResultData, + Schema: fw.Schema, + TerraformValue: fw.Raw, + } + + return DynamicValue(ctx, data) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/ephemeralresourcemetadata.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/ephemeralresourcemetadata.go new file mode 100644 index 000000000..e301fa35b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/ephemeralresourcemetadata.go @@ -0,0 +1,19 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// EphemeralResourceMetadata returns the tfprotov5.EphemeralResourceMetadata for a +// fwserver.EphemeralResourceMetadata. +func EphemeralResourceMetadata(ctx context.Context, fw fwserver.EphemeralResourceMetadata) tfprotov5.EphemeralResourceMetadata { + return tfprotov5.EphemeralResourceMetadata{ + TypeName: fw.TypeName, + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/getmetadata.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/getmetadata.go index 9c1892d8a..4150c2473 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/getmetadata.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/getmetadata.go @@ -17,25 +17,30 @@ func GetMetadataResponse(ctx context.Context, fw *fwserver.GetMetadataResponse) return nil } - protov6 := &tfprotov5.GetMetadataResponse{ + protov5 := &tfprotov5.GetMetadataResponse{ DataSources: make([]tfprotov5.DataSourceMetadata, 0, len(fw.DataSources)), Diagnostics: Diagnostics(ctx, fw.Diagnostics), + EphemeralResources: make([]tfprotov5.EphemeralResourceMetadata, 0, len(fw.EphemeralResources)), Functions: make([]tfprotov5.FunctionMetadata, 0, len(fw.Functions)), Resources: make([]tfprotov5.ResourceMetadata, 0, len(fw.Resources)), ServerCapabilities: ServerCapabilities(ctx, fw.ServerCapabilities), } for _, datasource := range fw.DataSources { - protov6.DataSources = append(protov6.DataSources, DataSourceMetadata(ctx, datasource)) + protov5.DataSources = append(protov5.DataSources, DataSourceMetadata(ctx, datasource)) + } + + for _, ephemeralResource := range fw.EphemeralResources { + protov5.EphemeralResources = append(protov5.EphemeralResources, EphemeralResourceMetadata(ctx, ephemeralResource)) } for _, function := range fw.Functions { - protov6.Functions = append(protov6.Functions, FunctionMetadata(ctx, function)) + protov5.Functions = append(protov5.Functions, FunctionMetadata(ctx, function)) } for _, resource := range fw.Resources { - protov6.Resources = append(protov6.Resources, ResourceMetadata(ctx, resource)) + protov5.Resources = append(protov5.Resources, ResourceMetadata(ctx, resource)) } - return protov6 + return protov5 } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/getproviderschema.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/getproviderschema.go index 1fec486ae..28b8906ff 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/getproviderschema.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/getproviderschema.go @@ -18,11 +18,12 @@ func GetProviderSchemaResponse(ctx context.Context, fw *fwserver.GetProviderSche } protov5 := &tfprotov5.GetProviderSchemaResponse{ - DataSourceSchemas: make(map[string]*tfprotov5.Schema, len(fw.DataSourceSchemas)), - Diagnostics: Diagnostics(ctx, fw.Diagnostics), - Functions: make(map[string]*tfprotov5.Function, len(fw.FunctionDefinitions)), - ResourceSchemas: make(map[string]*tfprotov5.Schema, len(fw.ResourceSchemas)), - ServerCapabilities: ServerCapabilities(ctx, fw.ServerCapabilities), + DataSourceSchemas: make(map[string]*tfprotov5.Schema, len(fw.DataSourceSchemas)), + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + EphemeralResourceSchemas: make(map[string]*tfprotov5.Schema, len(fw.EphemeralResourceSchemas)), + Functions: make(map[string]*tfprotov5.Function, len(fw.FunctionDefinitions)), + ResourceSchemas: make(map[string]*tfprotov5.Schema, len(fw.ResourceSchemas)), + ServerCapabilities: ServerCapabilities(ctx, fw.ServerCapabilities), } var err error @@ -83,5 +84,19 @@ func GetProviderSchemaResponse(ctx context.Context, fw *fwserver.GetProviderSche } } + for ephemeralResourceType, ephemeralResourceSchema := range fw.EphemeralResourceSchemas { + protov5.EphemeralResourceSchemas[ephemeralResourceType], err = Schema(ctx, ephemeralResourceSchema) + + if err != nil { + protov5.Diagnostics = append(protov5.Diagnostics, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Error converting ephemeral resource schema", + Detail: "The schema for the ephemeral resource \"" + ephemeralResourceType + "\" couldn't be converted into a usable type. This is always a problem with the provider. Please report the following to the provider developer:\n\n" + err.Error(), + }) + + return protov5 + } + } + return protov5 } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/openephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/openephemeralresource.go new file mode 100644 index 000000000..2ea4fac50 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/openephemeralresource.go @@ -0,0 +1,37 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// OpenEphemeralResourceResponse returns the *tfprotov5.OpenEphemeralResourceResponse +// equivalent of a *fwserver.OpenEphemeralResourceResponse. +func OpenEphemeralResourceResponse(ctx context.Context, fw *fwserver.OpenEphemeralResourceResponse) *tfprotov5.OpenEphemeralResourceResponse { + if fw == nil { + return nil + } + + proto5 := &tfprotov5.OpenEphemeralResourceResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + RenewAt: fw.RenewAt, + Deferred: EphemeralResourceDeferred(fw.Deferred), + } + + result, diags := EphemeralResultData(ctx, fw.Result) + + proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) + proto5.Result = result + + newPrivate, diags := fw.Private.Bytes(ctx) + + proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) + proto5.Private = newPrivate + + return proto5 +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/renewephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/renewephemeralresource.go new file mode 100644 index 000000000..5947c9dd1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/renewephemeralresource.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// RenewEphemeralResourceResponse returns the *tfprotov5.RenewEphemeralResourceResponse +// equivalent of a *fwserver.RenewEphemeralResourceResponse. +func RenewEphemeralResourceResponse(ctx context.Context, fw *fwserver.RenewEphemeralResourceResponse) *tfprotov5.RenewEphemeralResourceResponse { + if fw == nil { + return nil + } + + proto5 := &tfprotov5.RenewEphemeralResourceResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + RenewAt: fw.RenewAt, + } + + newPrivate, diags := fw.Private.Bytes(ctx) + + proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) + proto5.Private = newPrivate + + return proto5 +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/server_capabilities.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/server_capabilities.go index fd968906d..2ed77acbe 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/server_capabilities.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/server_capabilities.go @@ -19,6 +19,7 @@ func ServerCapabilities(ctx context.Context, fw *fwserver.ServerCapabilities) *t return &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: fw.GetProviderSchemaOptional, + MoveResourceState: fw.MoveResourceState, PlanDestroy: fw.PlanDestroy, } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/validateephemeralresourceconfig.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/validateephemeralresourceconfig.go new file mode 100644 index 000000000..fcd19ac99 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto5/validateephemeralresourceconfig.go @@ -0,0 +1,25 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// ValidateEphemeralResourceConfigResponse returns the *tfprotov5.ValidateEphemeralResourceConfigResponse +// equivalent of a *fwserver.ValidateEphemeralResourceConfigResponse. +func ValidateEphemeralResourceConfigResponse(ctx context.Context, fw *fwserver.ValidateEphemeralResourceConfigResponse) *tfprotov5.ValidateEphemeralResourceConfigResponse { + if fw == nil { + return nil + } + + proto5 := &tfprotov5.ValidateEphemeralResourceConfigResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + } + + return proto5 +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/closeephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/closeephemeralresource.go new file mode 100644 index 000000000..46810b9d7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/closeephemeralresource.go @@ -0,0 +1,25 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// CloseEphemeralResourceResponse returns the *tfprotov6.CloseEphemeralResourceResponse +// equivalent of a *fwserver.CloseEphemeralResourceResponse. +func CloseEphemeralResourceResponse(ctx context.Context, fw *fwserver.CloseEphemeralResourceResponse) *tfprotov6.CloseEphemeralResourceResponse { + if fw == nil { + return nil + } + + proto6 := &tfprotov6.CloseEphemeralResourceResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + } + + return proto6 +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/deferred.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/deferred.go index 10afa8fdc..fab64fe05 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/deferred.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/deferred.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" "github.com/hashicorp/terraform-plugin-framework/resource" ) @@ -27,3 +28,12 @@ func ResourceDeferred(fw *resource.Deferred) *tfprotov6.Deferred { Reason: tfprotov6.DeferredReason(fw.Reason), } } + +func EphemeralResourceDeferred(fw *ephemeral.Deferred) *tfprotov6.Deferred { + if fw == nil { + return nil + } + return &tfprotov6.Deferred{ + Reason: tfprotov6.DeferredReason(fw.Reason), + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/ephemeral_result_data.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/ephemeral_result_data.go new file mode 100644 index 000000000..14144adb2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/ephemeral_result_data.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// EphemeralResultData returns the *tfprotov6.DynamicValue for a *tfsdk.EphemeralResultData. +func EphemeralResultData(ctx context.Context, fw *tfsdk.EphemeralResultData) (*tfprotov6.DynamicValue, diag.Diagnostics) { + if fw == nil { + return nil, nil + } + + data := &fwschemadata.Data{ + Description: fwschemadata.DataDescriptionEphemeralResultData, + Schema: fw.Schema, + TerraformValue: fw.Raw, + } + + return DynamicValue(ctx, data) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/ephemeralresourcemetadata.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/ephemeralresourcemetadata.go new file mode 100644 index 000000000..56fab9951 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/ephemeralresourcemetadata.go @@ -0,0 +1,19 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// EphemeralResourceMetadata returns the tfprotov6.EphemeralResourceMetadata for a +// fwserver.EphemeralResourceMetadata. +func EphemeralResourceMetadata(ctx context.Context, fw fwserver.EphemeralResourceMetadata) tfprotov6.EphemeralResourceMetadata { + return tfprotov6.EphemeralResourceMetadata{ + TypeName: fw.TypeName, + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/getmetadata.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/getmetadata.go index 0924f3c9f..314072392 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/getmetadata.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/getmetadata.go @@ -20,6 +20,7 @@ func GetMetadataResponse(ctx context.Context, fw *fwserver.GetMetadataResponse) protov6 := &tfprotov6.GetMetadataResponse{ DataSources: make([]tfprotov6.DataSourceMetadata, 0, len(fw.DataSources)), Diagnostics: Diagnostics(ctx, fw.Diagnostics), + EphemeralResources: make([]tfprotov6.EphemeralResourceMetadata, 0, len(fw.EphemeralResources)), Functions: make([]tfprotov6.FunctionMetadata, 0, len(fw.Functions)), Resources: make([]tfprotov6.ResourceMetadata, 0, len(fw.Resources)), ServerCapabilities: ServerCapabilities(ctx, fw.ServerCapabilities), @@ -29,6 +30,10 @@ func GetMetadataResponse(ctx context.Context, fw *fwserver.GetMetadataResponse) protov6.DataSources = append(protov6.DataSources, DataSourceMetadata(ctx, datasource)) } + for _, ephemeralResource := range fw.EphemeralResources { + protov6.EphemeralResources = append(protov6.EphemeralResources, EphemeralResourceMetadata(ctx, ephemeralResource)) + } + for _, function := range fw.Functions { protov6.Functions = append(protov6.Functions, FunctionMetadata(ctx, function)) } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/getproviderschema.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/getproviderschema.go index ee221abbf..d88a5381e 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/getproviderschema.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/getproviderschema.go @@ -18,11 +18,12 @@ func GetProviderSchemaResponse(ctx context.Context, fw *fwserver.GetProviderSche } protov6 := &tfprotov6.GetProviderSchemaResponse{ - DataSourceSchemas: make(map[string]*tfprotov6.Schema, len(fw.DataSourceSchemas)), - Diagnostics: Diagnostics(ctx, fw.Diagnostics), - Functions: make(map[string]*tfprotov6.Function, len(fw.FunctionDefinitions)), - ResourceSchemas: make(map[string]*tfprotov6.Schema, len(fw.ResourceSchemas)), - ServerCapabilities: ServerCapabilities(ctx, fw.ServerCapabilities), + DataSourceSchemas: make(map[string]*tfprotov6.Schema, len(fw.DataSourceSchemas)), + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + EphemeralResourceSchemas: make(map[string]*tfprotov6.Schema, len(fw.EphemeralResourceSchemas)), + Functions: make(map[string]*tfprotov6.Function, len(fw.FunctionDefinitions)), + ResourceSchemas: make(map[string]*tfprotov6.Schema, len(fw.ResourceSchemas)), + ServerCapabilities: ServerCapabilities(ctx, fw.ServerCapabilities), } var err error @@ -83,5 +84,19 @@ func GetProviderSchemaResponse(ctx context.Context, fw *fwserver.GetProviderSche } } + for ephemeralResourceType, ephemeralResourceSchema := range fw.EphemeralResourceSchemas { + protov6.EphemeralResourceSchemas[ephemeralResourceType], err = Schema(ctx, ephemeralResourceSchema) + + if err != nil { + protov6.Diagnostics = append(protov6.Diagnostics, &tfprotov6.Diagnostic{ + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Error converting ephemeral resource schema", + Detail: "The schema for the ephemeral resource \"" + ephemeralResourceType + "\" couldn't be converted into a usable type. This is always a problem with the provider. Please report the following to the provider developer:\n\n" + err.Error(), + }) + + return protov6 + } + } + return protov6 } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/openephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/openephemeralresource.go new file mode 100644 index 000000000..7da9c35f6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/openephemeralresource.go @@ -0,0 +1,37 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// OpenEphemeralResourceResponse returns the *tfprotov6.OpenEphemeralResourceResponse +// equivalent of a *fwserver.OpenEphemeralResourceResponse. +func OpenEphemeralResourceResponse(ctx context.Context, fw *fwserver.OpenEphemeralResourceResponse) *tfprotov6.OpenEphemeralResourceResponse { + if fw == nil { + return nil + } + + proto6 := &tfprotov6.OpenEphemeralResourceResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + RenewAt: fw.RenewAt, + Deferred: EphemeralResourceDeferred(fw.Deferred), + } + + result, diags := EphemeralResultData(ctx, fw.Result) + + proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) + proto6.Result = result + + newPrivate, diags := fw.Private.Bytes(ctx) + + proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) + proto6.Private = newPrivate + + return proto6 +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/renewephemeralresource.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/renewephemeralresource.go new file mode 100644 index 000000000..4afcb9962 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/renewephemeralresource.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// RenewEphemeralResourceResponse returns the *tfprotov6.RenewEphemeralResourceResponse +// equivalent of a *fwserver.RenewEphemeralResourceResponse. +func RenewEphemeralResourceResponse(ctx context.Context, fw *fwserver.RenewEphemeralResourceResponse) *tfprotov6.RenewEphemeralResourceResponse { + if fw == nil { + return nil + } + + proto6 := &tfprotov6.RenewEphemeralResourceResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + RenewAt: fw.RenewAt, + } + + newPrivate, diags := fw.Private.Bytes(ctx) + + proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) + proto6.Private = newPrivate + + return proto6 +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/server_capabilities.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/server_capabilities.go index ef46cbf16..26c24f7c6 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/server_capabilities.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/server_capabilities.go @@ -19,6 +19,7 @@ func ServerCapabilities(ctx context.Context, fw *fwserver.ServerCapabilities) *t return &tfprotov6.ServerCapabilities{ GetProviderSchemaOptional: fw.GetProviderSchemaOptional, + MoveResourceState: fw.MoveResourceState, PlanDestroy: fw.PlanDestroy, } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/validateephemeralresourceconfig.go b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/validateephemeralresourceconfig.go new file mode 100644 index 000000000..5237b35dc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/internal/toproto6/validateephemeralresourceconfig.go @@ -0,0 +1,25 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// ValidateEphemeralResourceConfigResponse returns the *tfprotov6.ValidateEphemeralResourceConfigResponse +// equivalent of a *fwserver.ValidateEphemeralResourceConfigResponse. +func ValidateEphemeralResourceConfigResponse(ctx context.Context, fw *fwserver.ValidateEphemeralResourceConfigResponse) *tfprotov6.ValidateEphemeralResourceConfigResponse { + if fw == nil { + return nil + } + + proto6 := &tfprotov6.ValidateEphemeralResourceConfigResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + } + + return proto6 +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/configure.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/configure.go index 9b6678bf7..59e9ead44 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/configure.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/configure.go @@ -62,6 +62,11 @@ type ConfigureResponse struct { // that implements the Configure method. ResourceData any + // EphemeralResourceData is provider-defined data, clients, etc. that is + // passed to [ephemeral.ConfigureRequest.ProviderData] for each + // EphemeralResource type that implements the Configure method. + EphemeralResourceData any + // Deferred indicates that Terraform should automatically defer // all resources and data sources for this provider. // diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/map_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/map_attribute.go index d75cec982..51ee02edb 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/map_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/map_attribute.go @@ -19,7 +19,7 @@ var ( _ fwschema.AttributeWithValidateImplementation = MapAttribute{} ) -// MapAttribute represents a schema attribute that is a list with a single +// MapAttribute represents a schema attribute that is a map with a single // element type. When retrieving the value for this attribute, use types.Map // as the value type unless the CustomType field is set. The ElementType field // must be set. @@ -28,7 +28,7 @@ var ( // require definition beyond type information. // // Terraform configurations configure this attribute using expressions that -// return a list or directly via curly brace syntax. +// return a map or directly via curly brace syntax. // // # map of strings // example_attribute = { diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/map_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/map_nested_attribute.go index a8809b7a6..47a3b4348 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/map_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/map_nested_attribute.go @@ -18,7 +18,7 @@ var ( _ NestedAttribute = MapNestedAttribute{} ) -// MapNestedAttribute represents an attribute that is a set of objects where +// MapNestedAttribute represents an attribute that is a map of objects where // the object attributes can be fully defined, including further nested // attributes. When retrieving the value for this attribute, use types.Map // as the value type unless the CustomType field is set. The NestedObject field @@ -28,7 +28,7 @@ var ( // not require definition beyond type information. // // Terraform configurations configure this attribute using expressions that -// return a set of objects or directly via curly brace syntax. +// return a map of objects or directly via curly brace syntax. // // # map of objects // example_attribute = { @@ -123,7 +123,7 @@ func (a MapNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { return a.NestedObject } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeMap. func (a MapNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeMap } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/set_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/set_nested_attribute.go index 8bbcf1259..233866a00 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/set_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/set_nested_attribute.go @@ -118,7 +118,7 @@ func (a SetNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { return a.NestedObject } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeSet. func (a SetNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeSet } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/single_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/single_nested_attribute.go index de4dc07a4..0ed1a22fd 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/single_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/metaschema/single_nested_attribute.go @@ -132,7 +132,7 @@ func (a SingleNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject } } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeSingle. func (a SingleNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeSingle } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/provider.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/provider.go index ff0d18e81..ba18927cb 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/provider.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/provider.go @@ -7,6 +7,7 @@ import ( "context" "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/ephemeral" "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/resource" ) @@ -85,6 +86,24 @@ type ProviderWithFunctions interface { Functions(context.Context) []func() function.Function } +// ProviderWithEphemeralResources is an interface type that extends Provider to +// include ephemeral resources for usage in practitioner configurations. +// +// Ephemeral resources are supported in Terraform version 1.10 and later. +// +// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until +// these notices are removed. +type ProviderWithEphemeralResources interface { + Provider + + // EphemeralResources returns a slice of functions to instantiate each EphemeralResource + // implementation. + // + // The ephemeral resource type name is determined by the EphemeralResource implementing + // the Metadata method. All ephemeral resources must have unique names. + EphemeralResources(context.Context) []func() ephemeral.EphemeralResource +} + // ProviderWithMetaSchema is a provider with a provider meta schema, which // is configured by practitioners via the provider_meta configuration block // and the configuration data is included with certain data source and resource diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/attribute.go index 4a0feceec..67294bfc6 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/attribute.go @@ -10,7 +10,10 @@ import ( // Attribute define a value field inside the Schema. Implementations in this // package include: // - BoolAttribute +// - DynamicAttribute +// - Float32Attribute // - Float64Attribute +// - Int32Attribute // - Int64Attribute // - ListAttribute // - MapAttribute diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/map_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/map_attribute.go index 079535e77..77dc2b61d 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/map_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/map_attribute.go @@ -23,7 +23,7 @@ var ( _ fwxschema.AttributeWithMapValidators = MapAttribute{} ) -// MapAttribute represents a schema attribute that is a list with a single +// MapAttribute represents a schema attribute that is a map with a single // element type. When retrieving the value for this attribute, use types.Map // as the value type unless the CustomType field is set. The ElementType field // must be set. @@ -32,7 +32,7 @@ var ( // require definition beyond type information. // // Terraform configurations configure this attribute using expressions that -// return a list or directly via curly brace syntax. +// return a map or directly via curly brace syntax. // // # map of strings // example_attribute = { diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/map_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/map_nested_attribute.go index 0cdc9b5e0..2eed2fa08 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/map_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/map_nested_attribute.go @@ -24,7 +24,7 @@ var ( _ fwxschema.AttributeWithMapValidators = MapNestedAttribute{} ) -// MapNestedAttribute represents an attribute that is a set of objects where +// MapNestedAttribute represents an attribute that is a map of objects where // the object attributes can be fully defined, including further nested // attributes. When retrieving the value for this attribute, use types.Map // as the value type unless the CustomType field is set. The NestedObject field @@ -34,7 +34,7 @@ var ( // not require definition beyond type information. // // Terraform configurations configure this attribute using expressions that -// return a set of objects or directly via curly brace syntax. +// return a map of objects or directly via curly brace syntax. // // # map of objects // example_attribute = { @@ -187,7 +187,7 @@ func (a MapNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { return a.NestedObject } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeMap. func (a MapNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeMap } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/set_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/set_nested_attribute.go index 64fed1ea2..7a2fb6060 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/set_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/set_nested_attribute.go @@ -183,7 +183,7 @@ func (a SetNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { return a.NestedObject } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeSet. func (a SetNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeSet } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/single_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/single_nested_attribute.go index aac9875af..4aa669bf1 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/single_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/provider/schema/single_nested_attribute.go @@ -191,7 +191,7 @@ func (a SingleNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject } } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeSingle. func (a SingleNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeSingle } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/attribute.go index 4a0feceec..67294bfc6 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/attribute.go @@ -10,7 +10,10 @@ import ( // Attribute define a value field inside the Schema. Implementations in this // package include: // - BoolAttribute +// - DynamicAttribute +// - Float32Attribute // - Float64Attribute +// - Int32Attribute // - Int64Attribute // - ListAttribute // - MapAttribute diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/doc.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/doc.go new file mode 100644 index 000000000..115960d26 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/doc.go @@ -0,0 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package boolplanmodifier provides plan modifiers for types.Bool attributes. +package boolplanmodifier diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace.go new file mode 100644 index 000000000..10e84a33d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package boolplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplace returns a plan modifier that conditionally requires +// resource replacement if: +// +// - The resource is planned for update. +// - The plan and state values are not equal. +// +// Use RequiresReplaceIfConfigured if the resource replacement should +// only occur if there is a configuration value (ignore unconfigured drift +// detection changes). Use RequiresReplaceIf if the resource replacement +// should check provider-defined conditional logic. +func RequiresReplace() planmodifier.Bool { + return RequiresReplaceIf( + func(_ context.Context, _ planmodifier.BoolRequest, resp *RequiresReplaceIfFuncResponse) { + resp.RequiresReplace = true + }, + "If the value of this attribute changes, Terraform will destroy and recreate the resource.", + "If the value of this attribute changes, Terraform will destroy and recreate the resource.", + ) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if.go new file mode 100644 index 000000000..389ddb937 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if.go @@ -0,0 +1,73 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package boolplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplaceIf returns a plan modifier that conditionally requires +// resource replacement if: +// +// - The resource is planned for update. +// - The plan and state values are not equal. +// - The given function returns true. Returning false will not unset any +// prior resource replacement. +// +// Use RequiresReplace if the resource replacement should always occur on value +// changes. Use RequiresReplaceIfConfigured if the resource replacement should +// occur on value changes, but only if there is a configuration value (ignore +// unconfigured drift detection changes). +func RequiresReplaceIf(f RequiresReplaceIfFunc, description, markdownDescription string) planmodifier.Bool { + return requiresReplaceIfModifier{ + ifFunc: f, + description: description, + markdownDescription: markdownDescription, + } +} + +// requiresReplaceIfModifier is an plan modifier that sets RequiresReplace +// on the attribute if a given function is true. +type requiresReplaceIfModifier struct { + ifFunc RequiresReplaceIfFunc + description string + markdownDescription string +} + +// Description returns a human-readable description of the plan modifier. +func (m requiresReplaceIfModifier) Description(_ context.Context) string { + return m.description +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m requiresReplaceIfModifier) MarkdownDescription(_ context.Context) string { + return m.markdownDescription +} + +// PlanModifyBool implements the plan modification logic. +func (m requiresReplaceIfModifier) PlanModifyBool(ctx context.Context, req planmodifier.BoolRequest, resp *planmodifier.BoolResponse) { + // Do not replace on resource creation. + if req.State.Raw.IsNull() { + return + } + + // Do not replace on resource destroy. + if req.Plan.Raw.IsNull() { + return + } + + // Do not replace if the plan and state values are equal. + if req.PlanValue.Equal(req.StateValue) { + return + } + + ifFuncResp := &RequiresReplaceIfFuncResponse{} + + m.ifFunc(ctx, req, ifFuncResp) + + resp.Diagnostics.Append(ifFuncResp.Diagnostics...) + resp.RequiresReplace = ifFuncResp.RequiresReplace +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if_configured.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if_configured.go new file mode 100644 index 000000000..ca6c4348d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if_configured.go @@ -0,0 +1,34 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package boolplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplaceIfConfigured returns a plan modifier that conditionally requires +// resource replacement if: +// +// - The resource is planned for update. +// - The plan and state values are not equal. +// - The configuration value is not null. +// +// Use RequiresReplace if the resource replacement should occur regardless of +// the presence of a configuration value. Use RequiresReplaceIf if the resource +// replacement should check provider-defined conditional logic. +func RequiresReplaceIfConfigured() planmodifier.Bool { + return RequiresReplaceIf( + func(_ context.Context, req planmodifier.BoolRequest, resp *RequiresReplaceIfFuncResponse) { + if req.ConfigValue.IsNull() { + return + } + + resp.RequiresReplace = true + }, + "If the value of this attribute is configured and changes, Terraform will destroy and recreate the resource.", + "If the value of this attribute is configured and changes, Terraform will destroy and recreate the resource.", + ) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if_func.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if_func.go new file mode 100644 index 000000000..d38abd6c4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/requires_replace_if_func.go @@ -0,0 +1,25 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package boolplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplaceIfFunc is a conditional function used in the RequiresReplaceIf +// plan modifier to determine whether the attribute requires replacement. +type RequiresReplaceIfFunc func(context.Context, planmodifier.BoolRequest, *RequiresReplaceIfFuncResponse) + +// RequiresReplaceIfFuncResponse is the response type for a RequiresReplaceIfFunc. +type RequiresReplaceIfFuncResponse struct { + // Diagnostics report errors or warnings related to this logic. An empty + // or unset slice indicates success, with no warnings or errors generated. + Diagnostics diag.Diagnostics + + // RequiresReplace should be enabled if the resource should be replaced. + RequiresReplace bool +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/use_state_for_unknown.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/use_state_for_unknown.go new file mode 100644 index 000000000..efab19637 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier/use_state_for_unknown.go @@ -0,0 +1,55 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package boolplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// UseStateForUnknown returns a plan modifier that copies a known prior state +// value into the planned value. Use this when it is known that an unconfigured +// value will remain the same after a resource update. +// +// To prevent Terraform errors, the framework automatically sets unconfigured +// and Computed attributes to an unknown value "(known after apply)" on update. +// Using this plan modifier will instead display the prior state value in the +// plan, unless a prior plan modifier adjusts the value. +func UseStateForUnknown() planmodifier.Bool { + return useStateForUnknownModifier{} +} + +// useStateForUnknownModifier implements the plan modifier. +type useStateForUnknownModifier struct{} + +// Description returns a human-readable description of the plan modifier. +func (m useStateForUnknownModifier) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m useStateForUnknownModifier) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyBool implements the plan modification logic. +func (m useStateForUnknownModifier) PlanModifyBool(_ context.Context, req planmodifier.BoolRequest, resp *planmodifier.BoolResponse) { + // Do nothing if there is no state value. + if req.StateValue.IsNull() { + return + } + + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value, otherwise interpolation gets messed up. + if req.ConfigValue.IsUnknown() { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/map_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/map_attribute.go index ea08fa7b2..ac50f63f8 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/map_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/map_attribute.go @@ -28,7 +28,7 @@ var ( _ fwxschema.AttributeWithMapValidators = MapAttribute{} ) -// MapAttribute represents a schema attribute that is a list with a single +// MapAttribute represents a schema attribute that is a map with a single // element type. When retrieving the value for this attribute, use types.Map // as the value type unless the CustomType field is set. The ElementType field // must be set. @@ -37,7 +37,7 @@ var ( // require definition beyond type information. // // Terraform configurations configure this attribute using expressions that -// return a list or directly via curly brace syntax. +// return a map or directly via curly brace syntax. // // # map of strings // example_attribute = { diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/map_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/map_nested_attribute.go index faa1f3276..ab2230b3b 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/map_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/map_nested_attribute.go @@ -29,7 +29,7 @@ var ( _ fwxschema.AttributeWithMapValidators = MapNestedAttribute{} ) -// MapNestedAttribute represents an attribute that is a set of objects where +// MapNestedAttribute represents an attribute that is a map of objects where // the object attributes can be fully defined, including further nested // attributes. When retrieving the value for this attribute, use types.Map // as the value type unless the CustomType field is set. The NestedObject field @@ -39,7 +39,7 @@ var ( // not require definition beyond type information. // // Terraform configurations configure this attribute using expressions that -// return a set of objects or directly via curly brace syntax. +// return a map of objects or directly via curly brace syntax. // // # map of objects // example_attribute = { @@ -224,7 +224,7 @@ func (a MapNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { return a.NestedObject } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeMap. func (a MapNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeMap } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/set_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/set_nested_attribute.go index 3f2b3d1bb..dee37b591 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/set_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/set_nested_attribute.go @@ -219,7 +219,7 @@ func (a SetNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject { return a.NestedObject } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeSet. func (a SetNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeSet } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/single_nested_attribute.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/single_nested_attribute.go index a47ef6541..3dbda942a 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/single_nested_attribute.go +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/single_nested_attribute.go @@ -229,7 +229,7 @@ func (a SingleNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject } } -// GetNestingMode always returns NestingModeList. +// GetNestingMode always returns NestingModeSingle. func (a SingleNestedAttribute) GetNestingMode() fwschema.NestingMode { return fwschema.NestingModeSingle } diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/ephemeral_result_data.go b/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/ephemeral_result_data.go new file mode 100644 index 000000000..b3990ca77 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/tfsdk/ephemeral_result_data.go @@ -0,0 +1,94 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tfsdk + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// EphemeralResultData represents the data returned after opening a Terraform ephemeral resource. +type EphemeralResultData struct { + Raw tftypes.Value + Schema fwschema.Schema +} + +// Get populates the struct passed as `target` with the entire ephemeral result data object. +func (s EphemeralResultData) Get(ctx context.Context, target interface{}) diag.Diagnostics { + return s.data().Get(ctx, target) +} + +// GetAttribute retrieves the attribute or block found at `path` and populates +// the `target` with the value. This method is intended for top level schema +// attributes or blocks. Use `types` package methods or custom types to step +// into collections. +// +// Attributes or elements under null or unknown collections return null +// values, however this behavior is not protected by compatibility promises. +func (s EphemeralResultData) GetAttribute(ctx context.Context, path path.Path, target interface{}) diag.Diagnostics { + return s.data().GetAtPath(ctx, path, target) +} + +// PathMatches returns all matching path.Paths from the given path.Expression. +// +// If a parent path is null or unknown, which would prevent a full expression +// from matching, the parent path is returned rather than no match to prevent +// false positives. +func (s EphemeralResultData) PathMatches(ctx context.Context, pathExpr path.Expression) (path.Paths, diag.Diagnostics) { + return s.data().PathMatches(ctx, pathExpr) +} + +// Set populates the entire ephemeral result data object using the supplied Go value. The value `val` +// should be a struct whose values have one of the attr.Value types. Each field +// must be tagged with the corresponding schema field. +func (s *EphemeralResultData) Set(ctx context.Context, val interface{}) diag.Diagnostics { + data := s.data() + diags := data.Set(ctx, val) + + if diags.HasError() { + return diags + } + + s.Raw = data.TerraformValue + + return diags +} + +// SetAttribute sets the attribute at `path` using the supplied Go value. +// +// The attribute path and value must be valid with the current schema. If the +// attribute path already has a value, it will be overwritten. If the attribute +// path does not have a value, it will be added, including any parent attribute +// paths as necessary. +// +// The value must not be an untyped nil. Use a typed nil or types package null +// value function instead. For example with a types.StringType attribute, +// use (*string)(nil) or types.StringNull(). +// +// Lists can only have the next element added according to the current length. +func (s *EphemeralResultData) SetAttribute(ctx context.Context, path path.Path, val interface{}) diag.Diagnostics { + data := s.data() + diags := data.SetAtPath(ctx, path, val) + + if diags.HasError() { + return diags + } + + s.Raw = data.TerraformValue + + return diags +} + +func (s EphemeralResultData) data() *fwschemadata.Data { + return &fwschemadata.Data{ + Description: fwschemadata.DataDescriptionEphemeralResultData, + Schema: s.Schema, + TerraformValue: s.Raw, + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 0bddbd71e..dc8a0cb10 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -324,13 +324,15 @@ github.com/hashicorp/terraform-plugin-docs/internal/mdplain github.com/hashicorp/terraform-plugin-docs/internal/provider github.com/hashicorp/terraform-plugin-docs/internal/tmplfuncs github.com/hashicorp/terraform-plugin-docs/schemamd -# github.com/hashicorp/terraform-plugin-framework v1.11.0 -## explicit; go 1.21 +# github.com/hashicorp/terraform-plugin-framework v1.13.0 +## explicit; go 1.22.0 github.com/hashicorp/terraform-plugin-framework/attr github.com/hashicorp/terraform-plugin-framework/attr/xattr github.com/hashicorp/terraform-plugin-framework/datasource github.com/hashicorp/terraform-plugin-framework/datasource/schema github.com/hashicorp/terraform-plugin-framework/diag +github.com/hashicorp/terraform-plugin-framework/ephemeral +github.com/hashicorp/terraform-plugin-framework/ephemeral/schema github.com/hashicorp/terraform-plugin-framework/function github.com/hashicorp/terraform-plugin-framework/internal/fromproto5 github.com/hashicorp/terraform-plugin-framework/internal/fromproto6 @@ -357,6 +359,7 @@ github.com/hashicorp/terraform-plugin-framework/providerserver github.com/hashicorp/terraform-plugin-framework/resource github.com/hashicorp/terraform-plugin-framework/resource/schema github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault +github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier github.com/hashicorp/terraform-plugin-framework/resource/schema/defaults github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault From b12d9741c251509a98c58f4d601b809678397183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Fri, 6 Dec 2024 11:27:08 +0100 Subject: [PATCH 02/15] Fixup --- pkg/resources/database/resource_user.go | 45 ++++++++++++------- pkg/resources/database/resource_user_kafka.go | 13 ++++-- pkg/resources/database/resource_user_mysql.go | 13 ++++-- .../database/resource_user_opensearch.go | 13 ++++-- pkg/resources/database/resource_user_pg.go | 13 ++++-- 5 files changed, 70 insertions(+), 27 deletions(-) diff --git a/pkg/resources/database/resource_user.go b/pkg/resources/database/resource_user.go index 0bb9ae0da..13e0fa4ce 100644 --- a/pkg/resources/database/resource_user.go +++ b/pkg/resources/database/resource_user.go @@ -67,7 +67,7 @@ var commonAttributes = map[string]schema.Attribute{ // Computed attributes "id": schema.StringAttribute{ - MarkdownDescription: "The ID of this resource, as SERVICENAME/USERNAME", + MarkdownDescription: "The ID of this resource, computed as SERVICENAME/USERNAME", Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), @@ -94,19 +94,32 @@ func buildUserAttributes(newAttributes map[string]schema.Attribute) map[string]s } -type DatabaseServiceUserModel interface { - Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) - Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) +// ResourceModelInterface defines necessary functions for interacting with resources +type ResourceModelInterface interface { + // ReadResource reads resource from remote and populate the model accordingly + ReadResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) + // CreateResource creates the resource according to the model, and then + // update computed fields if applicable + CreateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) + // DeleteResource deletes the resource Delete(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) - Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) + // UpdateResource updates the remote resource w/ the new model + UpdateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) + + // WaitForService waits for the service to be RUNNING. + WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) + + // Accessing and setting attributes GetTimeouts() timeouts.Value SetTimeouts(timeouts.Value) - GenerateID() GetID() basetypes.StringValue GetZone() basetypes.StringValue + + // Should set the return value of .GetID() to service/username + GenerateID() } -func UserRead[T DatabaseServiceUserModel](ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, data T, client *exoscale.Client) { +func UserRead[T ResourceModelInterface](ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, data T, client *exoscale.Client) { // Read Terraform prior state data into the model resp.Diagnostics.Append(req.State.Get(ctx, &data)...) @@ -138,7 +151,7 @@ func UserRead[T DatabaseServiceUserModel](ctx context.Context, req resource.Read return } - data.Read(ctx, client, &resp.Diagnostics) + data.ReadResource(ctx, client, &resp.Diagnostics) if resp.Diagnostics.HasError() { return @@ -153,7 +166,7 @@ func UserRead[T DatabaseServiceUserModel](ctx context.Context, req resource.Read } -func UserReadForImport[T DatabaseServiceUserModel](ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, data T, client *exoscale.Client) { +func UserReadForImport[T ResourceModelInterface](ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, data T, client *exoscale.Client) { // Set timeout t, diags := data.GetTimeouts().Read(ctx, config.DefaultTimeout) @@ -179,7 +192,7 @@ func UserReadForImport[T DatabaseServiceUserModel](ctx context.Context, req reso return } - data.Read(ctx, client, &resp.Diagnostics) + data.ReadResource(ctx, client, &resp.Diagnostics) if resp.Diagnostics.HasError() { return @@ -194,7 +207,7 @@ func UserReadForImport[T DatabaseServiceUserModel](ctx context.Context, req reso } -func UserCreate[T DatabaseServiceUserModel](ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, data T, client *exoscale.Client) { +func UserCreate[T ResourceModelInterface](ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, data T, client *exoscale.Client) { // Read Terraform prior state data into the model resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) @@ -226,7 +239,8 @@ func UserCreate[T DatabaseServiceUserModel](ctx context.Context, req resource.Cr return } - data.Create(ctx, client, &diags) + data.WaitForService(ctx, client, &resp.Diagnostics) + data.CreateResource(ctx, client, &diags) if resp.Diagnostics.HasError() { return @@ -241,7 +255,7 @@ func UserCreate[T DatabaseServiceUserModel](ctx context.Context, req resource.Cr } -func UserUpdate[T DatabaseServiceUserModel](ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, stateData, planData T, client *exoscale.Client) { +func UserUpdate[T ResourceModelInterface](ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, stateData, planData T, client *exoscale.Client) { // Read Terraform plan data into the model resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) // Read Terraform state data (for comparison) into the model @@ -272,7 +286,8 @@ func UserUpdate[T DatabaseServiceUserModel](ctx context.Context, req resource.Up return } - planData.Update(ctx, client, &diags) + planData.WaitForService(ctx, client, &resp.Diagnostics) + planData.UpdateResource(ctx, client, &diags) if resp.Diagnostics.HasError() { return @@ -286,7 +301,7 @@ func UserUpdate[T DatabaseServiceUserModel](ctx context.Context, req resource.Up }) } -func UserDelete[T DatabaseServiceUserModel](ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, data T, client *exoscale.Client) { +func UserDelete[T ResourceModelInterface](ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, data T, client *exoscale.Client) { // Read Terraform prior state data into the model resp.Diagnostics.Append(req.State.Get(ctx, &data)...) diff --git a/pkg/resources/database/resource_user_kafka.go b/pkg/resources/database/resource_user_kafka.go index fcc8ea3c7..926b38283 100644 --- a/pkg/resources/database/resource_user_kafka.go +++ b/pkg/resources/database/resource_user_kafka.go @@ -141,7 +141,7 @@ func (r *KafkaUserResource) ImportState(ctx context.Context, req resource.Import } -func (data *KafkaUserResourceModel) Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *KafkaUserResourceModel) CreateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { createRequest := exoscale.CreateDBAASKafkaUserRequest{ Username: exoscale.DBAASUserUsername(data.Username.ValueString()), @@ -205,7 +205,7 @@ func (data *KafkaUserResourceModel) Delete(ctx context.Context, client *exoscale } -func (data *KafkaUserResourceModel) Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *KafkaUserResourceModel) ReadResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { svc, err := client.GetDBAASServiceKafka(ctx, data.Service.ValueString()) if err != nil { @@ -225,11 +225,18 @@ func (data *KafkaUserResourceModel) Read(ctx context.Context, client *exoscale.C diagnostics.AddError("Client Error", "Unable to read user for the service") } -func (data *KafkaUserResourceModel) Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *KafkaUserResourceModel) UpdateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { // Nothing to do here as all fields of this resource are immutable; replaces will be required // automatically } +func (data *KafkaUserResourceModel) WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + _, err := waitForDBAASService(ctx, client.GetDBAASServiceKafka, data.Service.ValueString(), func(t *exoscale.DBAASServiceKafka) string { return string(t.State) }) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Database service Kafka %s", err.Error())) + } +} + func (data *KafkaUserResourceModel) GetTimeouts() timeouts.Value { return data.Timeouts } diff --git a/pkg/resources/database/resource_user_mysql.go b/pkg/resources/database/resource_user_mysql.go index 059b883db..3c133fd50 100644 --- a/pkg/resources/database/resource_user_mysql.go +++ b/pkg/resources/database/resource_user_mysql.go @@ -140,7 +140,7 @@ func (r *MysqlUserResource) ImportState(ctx context.Context, req resource.Import } -func (data *MysqlUserResourceModel) Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *MysqlUserResourceModel) CreateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { createRequest := exoscale.CreateDBAASMysqlUserRequest{ Username: exoscale.DBAASUserUsername(data.Username.ValueString()), @@ -207,7 +207,7 @@ func (data *MysqlUserResourceModel) Delete(ctx context.Context, client *exoscale } -func (data *MysqlUserResourceModel) Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *MysqlUserResourceModel) ReadResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { svc, err := client.GetDBAASServiceMysql(ctx, data.Service.ValueString()) if err != nil { @@ -225,11 +225,18 @@ func (data *MysqlUserResourceModel) Read(ctx context.Context, client *exoscale.C diagnostics.AddError("Client Error", "Unable to read user for the service") } -func (data *MysqlUserResourceModel) Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *MysqlUserResourceModel) UpdateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { // Nothing to do here as all fields of this resource are immutable; replaces will be required // automatically } +func (data *MysqlUserResourceModel) WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + _, err := waitForDBAASService(ctx, client.GetDBAASServiceMysql, data.Service.ValueString(), func(t *exoscale.DBAASServiceMysql) string { return string(t.State) }) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Database service MySQL %s", err.Error())) + } +} + func (data *MysqlUserResourceModel) GetTimeouts() timeouts.Value { return data.Timeouts } diff --git a/pkg/resources/database/resource_user_opensearch.go b/pkg/resources/database/resource_user_opensearch.go index 3b82f28c9..5d63b35ff 100644 --- a/pkg/resources/database/resource_user_opensearch.go +++ b/pkg/resources/database/resource_user_opensearch.go @@ -122,7 +122,7 @@ func (r *OpensearchUserResource) ImportState(ctx context.Context, req resource.I } -func (data *OpensearchUserResourceModel) Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *OpensearchUserResourceModel) CreateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { createRequest := exoscale.CreateDBAASOpensearchUserRequest{ Username: exoscale.DBAASUserUsername(data.Username.ValueString()), @@ -183,7 +183,7 @@ func (data *OpensearchUserResourceModel) Delete(ctx context.Context, client *exo } -func (data *OpensearchUserResourceModel) Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *OpensearchUserResourceModel) ReadResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { svc, err := client.GetDBAASServiceOpensearch(ctx, data.Service.ValueString()) if err != nil { @@ -200,11 +200,18 @@ func (data *OpensearchUserResourceModel) Read(ctx context.Context, client *exosc diagnostics.AddError("Client Error", "Unable to read user for the service") } -func (data *OpensearchUserResourceModel) Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *OpensearchUserResourceModel) UpdateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { // Nothing to do here as all fields of this resource are immutable; replaces will be required // automatically } +func (data *OpensearchUserResourceModel) WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + _, err := waitForDBAASService(ctx, client.GetDBAASServiceOpensearch, data.Service.ValueString(), func(t *exoscale.DBAASServiceOpensearch) string { return string(t.State) }) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Database service Opensearch %s", err.Error())) + } +} + func (data *OpensearchUserResourceModel) GetTimeouts() timeouts.Value { return data.Timeouts } diff --git a/pkg/resources/database/resource_user_pg.go b/pkg/resources/database/resource_user_pg.go index 2ec13adee..02a52f7d1 100644 --- a/pkg/resources/database/resource_user_pg.go +++ b/pkg/resources/database/resource_user_pg.go @@ -136,7 +136,7 @@ func (r *PGUserResource) ImportState(ctx context.Context, req resource.ImportSta } -func (data *PGUserResourceModel) Create(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *PGUserResourceModel) CreateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { createRequest := exoscale.CreateDBAASPostgresUserRequest{ Username: exoscale.DBAASUserUsername(data.Username.ValueString()), @@ -204,7 +204,7 @@ func (data *PGUserResourceModel) Delete(ctx context.Context, client *exoscale.Cl } -func (data *PGUserResourceModel) Read(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *PGUserResourceModel) ReadResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { svc, err := client.GetDBAASServicePG(ctx, data.Service.ValueString()) if err != nil { @@ -224,11 +224,18 @@ func (data *PGUserResourceModel) Read(ctx context.Context, client *exoscale.Clie diagnostics.AddError("Client Error", "Unable to read user for the service") } -func (data *PGUserResourceModel) Update(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { +func (data *PGUserResourceModel) UpdateResource(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { // Nothing to do here as all fields of this resource are immutable; replaces will be required // automatically } +func (data *PGUserResourceModel) WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { + _, err := waitForDBAASService(ctx, client.GetDBAASServicePG, data.Service.ValueString(), func(t *exoscale.DBAASServicePG) string { return string(t.State) }) + if err != nil { + diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Database service Opensearch %s", err.Error())) + } +} + func (data *PGUserResourceModel) GetTimeouts() timeouts.Value { return data.Timeouts } From d2cebaee7a2ece96332ba16bbdda8a612bed034c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Fri, 6 Dec 2024 19:31:53 +0100 Subject: [PATCH 03/15] Kafka tests --- pkg/provider/provider.go | 3 + pkg/resources/database/datasource_uri.go | 37 ++++ pkg/resources/database/datasource_uri_test.go | 12 +- pkg/resources/database/main_test.go | 14 +- .../database/resource_grafana_test.go | 2 +- pkg/resources/database/resource_kafka_test.go | 192 ++++++++++++++---- pkg/resources/database/resource_mysql_test.go | 2 +- .../database/resource_opensearch_test.go | 2 +- pkg/resources/database/resource_pg_test.go | 2 +- pkg/resources/database/resource_redis_test.go | 2 +- pkg/resources/database/resource_user_kafka.go | 2 +- pkg/resources/database/resource_user_mysql.go | 2 +- .../database/resource_user_opensearch.go | 2 +- pkg/resources/database/resource_user_pg.go | 2 +- .../testdata/resource_user_kafka.tmpl | 18 ++ 15 files changed, 236 insertions(+), 58 deletions(-) create mode 100644 pkg/resources/database/testdata/resource_user_kafka.tmpl diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index ca9809606..e6b7e49f0 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -221,6 +221,9 @@ func (p *ExoscaleProvider) Resources(ctx context.Context) []func() resource.Reso return []func() resource.Resource{ database.NewResource, database.NewMysqlUserResource, + database.NewKafkaUserResource, + database.NewOpensearchUserResource, + database.NewPGUserResource, iam.NewResourceOrgPolicy, iam.NewResourceRole, iam.NewResourceAPIKey, diff --git a/pkg/resources/database/datasource_uri.go b/pkg/resources/database/datasource_uri.go index 68e6d6ac3..0eaf6a166 100644 --- a/pkg/resources/database/datasource_uri.go +++ b/pkg/resources/database/datasource_uri.go @@ -214,6 +214,43 @@ polling: return service, nil } +// waitForDBAASServiceReadyForUsers polls the database service until it is ready to accept user creation +func waitForDBAASServiceReadyForUsers[T any]( + ctx context.Context, + getService func(context.Context, string) (*T, error), + serviceName string, + usersReady func(*T) bool, +) (*T, error) { + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + +polling: + for { + select { + case <-ticker.C: + service, err := getService(ctx, serviceName) + if err != nil { + return nil, fmt.Errorf("error polling service status: %w", err) + } + + usersReady := usersReady(service) + if usersReady { + break polling + } + + case <-ctx.Done(): + return nil, ctx.Err() + } + } + + // Get final state after breaking from polling loop + service, err := getService(ctx, serviceName) + if err != nil { + return nil, fmt.Errorf("error getting final service state: %w", err) + } + return service, nil +} + // Read defines how the data source updates Terraform's state to reflect the retrieved data. func (d *DataSourceURI) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { var data DataSourceURIModel diff --git a/pkg/resources/database/datasource_uri_test.go b/pkg/resources/database/datasource_uri_test.go index 6456a389c..fe9798f3f 100644 --- a/pkg/resources/database/datasource_uri_test.go +++ b/pkg/resources/database/datasource_uri_test.go @@ -64,7 +64,7 @@ func testDataSourceURI(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("pg", resourcePg.Name), + CheckDestroy: CheckServiceDestroy("pg", resourcePg.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { @@ -110,7 +110,7 @@ func testDataSourceURI(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("mysql", resourceMysql.Name), + CheckDestroy: CheckServiceDestroy("mysql", resourceMysql.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { @@ -157,7 +157,7 @@ func testDataSourceURI(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("redis", resourceRedis.Name), + CheckDestroy: CheckServiceDestroy("redis", resourceRedis.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { @@ -204,7 +204,7 @@ func testDataSourceURI(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("kafka", resourceKafka.Name), + CheckDestroy: CheckServiceDestroy("kafka", resourceKafka.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { @@ -252,7 +252,7 @@ func testDataSourceURI(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("opensearch", resourceOpensearch.Name), + CheckDestroy: CheckServiceDestroy("opensearch", resourceOpensearch.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { @@ -299,7 +299,7 @@ func testDataSourceURI(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("grafana", resourceGrafana.Name), + CheckDestroy: CheckServiceDestroy("grafana", resourceGrafana.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { diff --git a/pkg/resources/database/main_test.go b/pkg/resources/database/main_test.go index 7554d062e..c608c5a75 100644 --- a/pkg/resources/database/main_test.go +++ b/pkg/resources/database/main_test.go @@ -16,16 +16,16 @@ import ( ) func TestDatabase(t *testing.T) { - t.Run("ResourcePg", testResourcePg) - t.Run("ResourceMysql", testResourceMysql) - t.Run("ResourceRedis", testResourceRedis) + // t.Run("ResourcePg", testResourcePg) + // t.Run("ResourceMysql", testResourceMysql) + // t.Run("ResourceRedis", testResourceRedis) t.Run("ResourceKafka", testResourceKafka) - t.Run("ResourceOpensearch", testResourceOpensearch) - t.Run("ResourceGrafana", testResourceGrafana) - t.Run("DataSourceURI", testDataSourceURI) + // t.Run("ResourceOpensearch", testResourceOpensearch) + // t.Run("ResourceGrafana", testResourceGrafana) + // t.Run("DataSourceURI", testDataSourceURI) } -func CheckDestroy(dbType, name string) resource.TestCheckFunc { +func CheckServiceDestroy(dbType, name string) resource.TestCheckFunc { return func(_ *terraform.State) error { client, err := testutils.APIClient() if err != nil { diff --git a/pkg/resources/database/resource_grafana_test.go b/pkg/resources/database/resource_grafana_test.go index 16517f447..b9f25790e 100644 --- a/pkg/resources/database/resource_grafana_test.go +++ b/pkg/resources/database/resource_grafana_test.go @@ -78,7 +78,7 @@ func testResourceGrafana(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("grafana", dataBase.Name), + CheckDestroy: CheckServiceDestroy("grafana", dataBase.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { diff --git a/pkg/resources/database/resource_kafka_test.go b/pkg/resources/database/resource_kafka_test.go index 38469c0f4..53cf9c285 100644 --- a/pkg/resources/database/resource_kafka_test.go +++ b/pkg/resources/database/resource_kafka_test.go @@ -46,14 +46,33 @@ type TemplateModelKafka struct { Version string } +type TemplateModelKafkaUser struct { + ResourceName string + + Username string + Zone string + Service string + + Type string + Password string + + AccessKey string + AccessCert string + AccessCertExpiry string +} + func testResourceKafka(t *testing.T) { - tpl, err := template.ParseFiles("testdata/resource_kafka.tmpl") + serviceTpl, err := template.ParseFiles("testdata/resource_kafka.tmpl") + if err != nil { + t.Fatal(err) + } + userTpl, err := template.ParseFiles("testdata/resource_user_kafka.tmpl") if err != nil { t.Fatal(err) } - fullResourceName := "exoscale_database.test" - dataBase := TemplateModelKafka{ + serviceFullResourceName := "exoscale_database.test" + serviceDataBase := TemplateModelKafka{ ResourceName: "test", Name: acctest.RandomWithPrefix(testutils.Prefix), Plan: "business-4", @@ -62,32 +81,55 @@ func testResourceKafka(t *testing.T) { Version: "3.7", } - dataCreate := dataBase - dataCreate.MaintenanceDow = "monday" - dataCreate.MaintenanceTime = "01:23:00" - dataCreate.EnableCertAuth = true - dataCreate.IpFilter = []string{"1.2.3.4/32"} - dataCreate.KafkaSettings = strconv.Quote(`{"num_partitions":10}`) + userFullResourceName := "exoscale_database_kafka_user.test_user" + userDataBase := TemplateModelKafkaUser{ + ResourceName: "test_user", + Username: "foo", + Zone: serviceDataBase.Zone, + Service: fmt.Sprintf("%s.name", serviceFullResourceName), + } + + serviceDataCreate := serviceDataBase + serviceDataCreate.MaintenanceDow = "monday" + serviceDataCreate.MaintenanceTime = "01:23:00" + serviceDataCreate.EnableCertAuth = true + serviceDataCreate.IpFilter = []string{"1.2.3.4/32"} + serviceDataCreate.KafkaSettings = strconv.Quote(`{"num_partitions":10}`) buf := &bytes.Buffer{} - err = tpl.Execute(buf, &dataCreate) + err = serviceTpl.Execute(buf, &serviceDataCreate) + if err != nil { + t.Fatal(err) + } + + userDataCreate := userDataBase + err = userTpl.Execute(buf, &userDataCreate) if err != nil { t.Fatal(err) } - configCreate := buf.String() - dataUpdate := dataBase - dataUpdate.MaintenanceDow = "tuesday" - dataUpdate.MaintenanceTime = "02:34:00" - dataUpdate.EnableCertAuth = false - dataUpdate.EnableSASLAuth = true - dataUpdate.EnableKafkaREST = true - dataUpdate.EnableKafkaConnect = true - dataUpdate.IpFilter = nil - dataUpdate.KafkaSettings = strconv.Quote(`{"compression_type":"gzip","num_partitions":10}`) - dataUpdate.RestSettings = strconv.Quote(`{"consumer_request_max_bytes":100000}`) - dataUpdate.ConnectSettings = strconv.Quote(`{"session_timeout_ms":6000}`) + configCreate := buf.String() + fmt.Println(configCreate) + + serviceDataUpdate := serviceDataBase + serviceDataUpdate.MaintenanceDow = "tuesday" + serviceDataUpdate.MaintenanceTime = "02:34:00" + serviceDataUpdate.EnableCertAuth = false + serviceDataUpdate.EnableSASLAuth = true + serviceDataUpdate.EnableKafkaREST = true + serviceDataUpdate.EnableKafkaConnect = true + serviceDataUpdate.IpFilter = nil + serviceDataUpdate.KafkaSettings = strconv.Quote(`{"compression_type":"gzip","num_partitions":10}`) + serviceDataUpdate.RestSettings = strconv.Quote(`{"consumer_request_max_bytes":100000}`) + serviceDataUpdate.ConnectSettings = strconv.Quote(`{"session_timeout_ms":6000}`) buf = &bytes.Buffer{} - err = tpl.Execute(buf, &dataUpdate) + err = serviceTpl.Execute(buf, &serviceDataUpdate) + if err != nil { + t.Fatal(err) + } + + userDataUpdate := userDataBase + userDataUpdate.Username = "bar" + err = userTpl.Execute(buf, &userDataUpdate) if err != nil { t.Fatal(err) } @@ -95,22 +137,37 @@ func testResourceKafka(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("kafka", dataBase.Name), + CheckDestroy: CheckServiceDestroy("kafka", serviceDataBase.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { // Create Config: configCreate, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet(fullResourceName, "created_at"), - resource.TestCheckResourceAttrSet(fullResourceName, "disk_size"), - resource.TestCheckResourceAttrSet(fullResourceName, "node_cpus"), - resource.TestCheckResourceAttrSet(fullResourceName, "node_memory"), - resource.TestCheckResourceAttrSet(fullResourceName, "nodes"), - resource.TestCheckResourceAttrSet(fullResourceName, "ca_certificate"), - resource.TestCheckResourceAttrSet(fullResourceName, "updated_at"), + // Service + resource.TestCheckResourceAttrSet(serviceFullResourceName, "created_at"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "disk_size"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "node_cpus"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "node_memory"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "nodes"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "ca_certificate"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "updated_at"), func(s *terraform.State) error { - err := CheckExistsKafka(dataBase.Name, &dataCreate) + err := CheckExistsKafka(serviceDataBase.Name, &serviceDataCreate) + if err != nil { + return err + } + + return nil + }, + + // User + resource.TestCheckResourceAttrSet(userFullResourceName, "password"), + resource.TestCheckResourceAttrSet(userFullResourceName, "access_key"), + resource.TestCheckResourceAttrSet(userFullResourceName, "access_cert"), + resource.TestCheckResourceAttrSet(userFullResourceName, "access_cert_expiry"), + func(s *terraform.State) error { + err := CheckExistsKafkaUser(serviceDataBase.Name, userDataBase.Username, &userDataCreate) if err != nil { return err } @@ -123,22 +180,51 @@ func testResourceKafka(t *testing.T) { // Update Config: configUpdate, Check: resource.ComposeAggregateTestCheckFunc( + // Service + func(s *terraform.State) error { + err := CheckExistsKafka(serviceDataBase.Name, &serviceDataUpdate) + if err != nil { + return err + } + + return nil + }, + + // User func(s *terraform.State) error { - err := CheckExistsKafka(dataBase.Name, &dataUpdate) + // Check the old user was deleted + err := CheckExistsKafkaUser(serviceDataBase.Name, userDataBase.Username, &userDataUpdate) + if err == nil { + return fmt.Errorf("expected to not find user %s", userDataBase.Username) + } + + // Check the new user exists + err = CheckExistsKafkaUser(serviceDataBase.Name, userDataUpdate.Username, &userDataUpdate) if err != nil { return err } return nil + }, ), }, { // Import - ResourceName: fullResourceName, + ResourceName: serviceFullResourceName, ImportStateIdFunc: func() resource.ImportStateIdFunc { return func(*terraform.State) (string, error) { - return fmt.Sprintf("%s@%s", dataBase.Name, dataBase.Zone), nil + return fmt.Sprintf("%s@%s", serviceDataBase.Name, serviceDataBase.Zone), nil + } + }(), + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: userFullResourceName, + ImportStateIdFunc: func() resource.ImportStateIdFunc { + return func(*terraform.State) (string, error) { + return fmt.Sprintf("%s/%s@%s", serviceDataBase.Name, userDataUpdate.Username, userDataBase.Zone), nil } }(), ImportState: true, @@ -158,7 +244,7 @@ func CheckExistsKafka(name string, data *TemplateModelKafka) error { res, err := client.GetDbaasServiceKafkaWithResponse(ctx, oapi.DbaasServiceName(name)) if err != nil { - return err + return fmt.Errorf("aww") } if res.StatusCode() != http.StatusOK { return fmt.Errorf("API request error: unexpected status %s", res.Status()) @@ -283,3 +369,37 @@ func CheckExistsKafka(name string, data *TemplateModelKafka) error { return nil } + +func CheckExistsKafkaUser(service, username string, data *TemplateModelKafkaUser) error { + + client, err := testutils.APIClient() + if err != nil { + return err + } + + ctx := exoapi.WithEndpoint(context.Background(), exoapi.NewReqEndpoint(testutils.TestEnvironment(), testutils.TestZoneName)) + + res, err := client.GetDbaasServiceKafkaWithResponse(ctx, oapi.DbaasServiceName(service)) + if err != nil { + return err + } + if res.StatusCode() != http.StatusOK { + return fmt.Errorf("API request error: unexpected status %s", res.Status()) + } + svc := res.JSON200 + + serviceUsernames := make([]string, 0) + if svc.Users != nil { + for _, u := range *svc.Users { + if u.Username != nil { + serviceUsernames = append(serviceUsernames, *u.Username) + if *u.Username == username { + // Kafka only has immutable fields + return nil + } + } + } + } + + return fmt.Errorf("could not find user %s for service %s, found %v", username, service, serviceUsernames) +} diff --git a/pkg/resources/database/resource_mysql_test.go b/pkg/resources/database/resource_mysql_test.go index 375692bca..5fe9cad57 100644 --- a/pkg/resources/database/resource_mysql_test.go +++ b/pkg/resources/database/resource_mysql_test.go @@ -85,7 +85,7 @@ func testResourceMysql(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("mysql", dataBase.Name), + CheckDestroy: CheckServiceDestroy("mysql", dataBase.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { diff --git a/pkg/resources/database/resource_opensearch_test.go b/pkg/resources/database/resource_opensearch_test.go index 9c0171e20..e2b80f5c9 100644 --- a/pkg/resources/database/resource_opensearch_test.go +++ b/pkg/resources/database/resource_opensearch_test.go @@ -120,7 +120,7 @@ func testResourceOpensearch(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("opensearch", dataBase.Name), + CheckDestroy: CheckServiceDestroy("opensearch", dataBase.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { diff --git a/pkg/resources/database/resource_pg_test.go b/pkg/resources/database/resource_pg_test.go index ad11ce376..9c291741c 100644 --- a/pkg/resources/database/resource_pg_test.go +++ b/pkg/resources/database/resource_pg_test.go @@ -89,7 +89,7 @@ func testResourcePg(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("pg", dataBase.Name), + CheckDestroy: CheckServiceDestroy("pg", dataBase.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { diff --git a/pkg/resources/database/resource_redis_test.go b/pkg/resources/database/resource_redis_test.go index a0273826e..79c55c6bf 100644 --- a/pkg/resources/database/resource_redis_test.go +++ b/pkg/resources/database/resource_redis_test.go @@ -78,7 +78,7 @@ func testResourceRedis(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckDestroy("redis", dataBase.Name), + CheckDestroy: CheckServiceDestroy("redis", dataBase.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { diff --git a/pkg/resources/database/resource_user_kafka.go b/pkg/resources/database/resource_user_kafka.go index 926b38283..5477efd5f 100644 --- a/pkg/resources/database/resource_user_kafka.go +++ b/pkg/resources/database/resource_user_kafka.go @@ -231,7 +231,7 @@ func (data *KafkaUserResourceModel) UpdateResource(ctx context.Context, client * } func (data *KafkaUserResourceModel) WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { - _, err := waitForDBAASService(ctx, client.GetDBAASServiceKafka, data.Service.ValueString(), func(t *exoscale.DBAASServiceKafka) string { return string(t.State) }) + _, err := waitForDBAASServiceReadyForUsers(ctx, client.GetDBAASServiceKafka, data.Service.ValueString(), func(t *exoscale.DBAASServiceKafka) bool { return len(t.Users) > 0 }) if err != nil { diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Database service Kafka %s", err.Error())) } diff --git a/pkg/resources/database/resource_user_mysql.go b/pkg/resources/database/resource_user_mysql.go index 3c133fd50..acc3d8a3f 100644 --- a/pkg/resources/database/resource_user_mysql.go +++ b/pkg/resources/database/resource_user_mysql.go @@ -231,7 +231,7 @@ func (data *MysqlUserResourceModel) UpdateResource(ctx context.Context, client * } func (data *MysqlUserResourceModel) WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { - _, err := waitForDBAASService(ctx, client.GetDBAASServiceMysql, data.Service.ValueString(), func(t *exoscale.DBAASServiceMysql) string { return string(t.State) }) + _, err := waitForDBAASServiceReadyForUsers(ctx, client.GetDBAASServiceMysql, data.Service.ValueString(), func(t *exoscale.DBAASServiceMysql) bool { return len(t.Users) > 0 }) if err != nil { diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Database service MySQL %s", err.Error())) } diff --git a/pkg/resources/database/resource_user_opensearch.go b/pkg/resources/database/resource_user_opensearch.go index 5d63b35ff..ebd91307b 100644 --- a/pkg/resources/database/resource_user_opensearch.go +++ b/pkg/resources/database/resource_user_opensearch.go @@ -206,7 +206,7 @@ func (data *OpensearchUserResourceModel) UpdateResource(ctx context.Context, cli } func (data *OpensearchUserResourceModel) WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { - _, err := waitForDBAASService(ctx, client.GetDBAASServiceOpensearch, data.Service.ValueString(), func(t *exoscale.DBAASServiceOpensearch) string { return string(t.State) }) + _, err := waitForDBAASServiceReadyForUsers(ctx, client.GetDBAASServiceOpensearch, data.Service.ValueString(), func(t *exoscale.DBAASServiceOpensearch) bool { return len(t.Users) > 0 }) if err != nil { diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Database service Opensearch %s", err.Error())) } diff --git a/pkg/resources/database/resource_user_pg.go b/pkg/resources/database/resource_user_pg.go index 02a52f7d1..a1d6466da 100644 --- a/pkg/resources/database/resource_user_pg.go +++ b/pkg/resources/database/resource_user_pg.go @@ -230,7 +230,7 @@ func (data *PGUserResourceModel) UpdateResource(ctx context.Context, client *exo } func (data *PGUserResourceModel) WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { - _, err := waitForDBAASService(ctx, client.GetDBAASServicePG, data.Service.ValueString(), func(t *exoscale.DBAASServicePG) string { return string(t.State) }) + _, err := waitForDBAASServiceReadyForUsers(ctx, client.GetDBAASServicePG, data.Service.ValueString(), func(t *exoscale.DBAASServicePG) bool { return len(t.Users) > 0 }) if err != nil { diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Database service Opensearch %s", err.Error())) } diff --git a/pkg/resources/database/testdata/resource_user_kafka.tmpl b/pkg/resources/database/testdata/resource_user_kafka.tmpl new file mode 100644 index 000000000..431c4dc38 --- /dev/null +++ b/pkg/resources/database/testdata/resource_user_kafka.tmpl @@ -0,0 +1,18 @@ +resource "exoscale_database_kafka_user" {{ .ResourceName }} { + username = "{{ .Username }}" + service = {{ .Service }} + zone = "{{ .Zone }}" + + {{- if .AccessKey }} + access_key = "{{ .AccessKey }}" + {{- end }} + + {{- if .AccessCert }} + access_cert = "{{ .AccessCert }}" + {{- end }} + + {{- if .AccessCertExpiry }} + access_cert_expiry = "{{ .AccessCertExpiry }}" + {{- end }} + +} From cc8039075231f2fa527b0a2cc85f9be3e916cb24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Mon, 9 Dec 2024 17:28:29 +0100 Subject: [PATCH 04/15] Tests for other service types --- pkg/resources/database/main_test.go | 12 +- pkg/resources/database/resource_kafka_test.go | 16 +- pkg/resources/database/resource_mysql_test.go | 178 ++++++++++++++--- .../database/resource_opensearch_test.go | 180 ++++++++++++++---- pkg/resources/database/resource_pg_test.go | 177 +++++++++++++---- pkg/resources/database/resource_user.go | 5 + pkg/resources/database/resource_user_kafka.go | 2 + pkg/resources/database/resource_user_mysql.go | 2 + .../database/resource_user_opensearch.go | 2 + pkg/resources/database/resource_user_pg.go | 4 +- .../testdata/resource_user_mysql.tmpl | 9 + .../testdata/resource_user_opensearch.tmpl | 5 + .../database/testdata/resource_user_pg.tmpl | 5 + 13 files changed, 481 insertions(+), 116 deletions(-) create mode 100644 pkg/resources/database/testdata/resource_user_mysql.tmpl create mode 100644 pkg/resources/database/testdata/resource_user_opensearch.tmpl create mode 100644 pkg/resources/database/testdata/resource_user_pg.tmpl diff --git a/pkg/resources/database/main_test.go b/pkg/resources/database/main_test.go index c608c5a75..e2db06577 100644 --- a/pkg/resources/database/main_test.go +++ b/pkg/resources/database/main_test.go @@ -16,13 +16,13 @@ import ( ) func TestDatabase(t *testing.T) { - // t.Run("ResourcePg", testResourcePg) - // t.Run("ResourceMysql", testResourceMysql) - // t.Run("ResourceRedis", testResourceRedis) + t.Run("ResourcePg", testResourcePg) + t.Run("ResourceMysql", testResourceMysql) + t.Run("ResourceRedis", testResourceRedis) t.Run("ResourceKafka", testResourceKafka) - // t.Run("ResourceOpensearch", testResourceOpensearch) - // t.Run("ResourceGrafana", testResourceGrafana) - // t.Run("DataSourceURI", testDataSourceURI) + t.Run("ResourceOpensearch", testResourceOpensearch) + t.Run("ResourceGrafana", testResourceGrafana) + t.Run("DataSourceURI", testDataSourceURI) } func CheckServiceDestroy(dbType, name string) resource.TestCheckFunc { diff --git a/pkg/resources/database/resource_kafka_test.go b/pkg/resources/database/resource_kafka_test.go index 53cf9c285..449efe745 100644 --- a/pkg/resources/database/resource_kafka_test.go +++ b/pkg/resources/database/resource_kafka_test.go @@ -95,20 +95,19 @@ func testResourceKafka(t *testing.T) { serviceDataCreate.EnableCertAuth = true serviceDataCreate.IpFilter = []string{"1.2.3.4/32"} serviceDataCreate.KafkaSettings = strconv.Quote(`{"num_partitions":10}`) + + userDataCreate := userDataBase + buf := &bytes.Buffer{} err = serviceTpl.Execute(buf, &serviceDataCreate) if err != nil { t.Fatal(err) } - - userDataCreate := userDataBase err = userTpl.Execute(buf, &userDataCreate) if err != nil { t.Fatal(err) } - configCreate := buf.String() - fmt.Println(configCreate) serviceDataUpdate := serviceDataBase serviceDataUpdate.MaintenanceDow = "tuesday" @@ -121,14 +120,15 @@ func testResourceKafka(t *testing.T) { serviceDataUpdate.KafkaSettings = strconv.Quote(`{"compression_type":"gzip","num_partitions":10}`) serviceDataUpdate.RestSettings = strconv.Quote(`{"consumer_request_max_bytes":100000}`) serviceDataUpdate.ConnectSettings = strconv.Quote(`{"session_timeout_ms":6000}`) + + userDataUpdate := userDataBase + userDataUpdate.Username = "bar" + buf = &bytes.Buffer{} err = serviceTpl.Execute(buf, &serviceDataUpdate) if err != nil { t.Fatal(err) } - - userDataUpdate := userDataBase - userDataUpdate.Username = "bar" err = userTpl.Execute(buf, &userDataUpdate) if err != nil { t.Fatal(err) @@ -163,6 +163,7 @@ func testResourceKafka(t *testing.T) { // User resource.TestCheckResourceAttrSet(userFullResourceName, "password"), + resource.TestCheckResourceAttrSet(userFullResourceName, "type"), resource.TestCheckResourceAttrSet(userFullResourceName, "access_key"), resource.TestCheckResourceAttrSet(userFullResourceName, "access_cert"), resource.TestCheckResourceAttrSet(userFullResourceName, "access_cert_expiry"), @@ -394,7 +395,6 @@ func CheckExistsKafkaUser(service, username string, data *TemplateModelKafkaUser if u.Username != nil { serviceUsernames = append(serviceUsernames, *u.Username) if *u.Username == username { - // Kafka only has immutable fields return nil } } diff --git a/pkg/resources/database/resource_mysql_test.go b/pkg/resources/database/resource_mysql_test.go index 5fe9cad57..f77b0d350 100644 --- a/pkg/resources/database/resource_mysql_test.go +++ b/pkg/resources/database/resource_mysql_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "strconv" + "strings" "testing" "text/template" @@ -41,14 +42,31 @@ type TemplateModelMysql struct { Version string } +type TemplateModelMysqlUser struct { + ResourceName string + + Username string + Zone string + Service string + + Type string + Password string + + Authentication string +} + func testResourceMysql(t *testing.T) { - tpl, err := template.ParseFiles("testdata/resource_mysql.tmpl") + serviceTpl, err := template.ParseFiles("testdata/resource_mysql.tmpl") + if err != nil { + t.Fatal(err) + } + userTpl, err := template.ParseFiles("testdata/resource_user_mysql.tmpl") if err != nil { t.Fatal(err) } - fullResourceName := "exoscale_database.test" - dataBase := TemplateModelMysql{ + serviceFullResourceName := "exoscale_database.test" + serviceDataBase := TemplateModelMysql{ ResourceName: "test", Name: acctest.RandomWithPrefix(testutils.Prefix), Plan: "hobbyist-2", @@ -57,27 +75,51 @@ func testResourceMysql(t *testing.T) { Version: "8", } - dataCreate := dataBase - dataCreate.MaintenanceDow = "monday" - dataCreate.MaintenanceTime = "01:23:00" - dataCreate.BackupSchedule = "01:23" - dataCreate.IpFilter = []string{"1.2.3.4/32"} - dataCreate.MysqlSettings = strconv.Quote(`{"log_output":"INSIGHTS","long_query_time":1,"slow_query_log":true,"sql_mode":"ANSI,TRADITIONAL","sql_require_primary_key":true}`) + userFullResourceName := "exoscale_database_mysql_user.test_user" + userDataBase := TemplateModelMysqlUser{ + ResourceName: "test_user", + Username: "foo", + Zone: serviceDataBase.Zone, + Service: fmt.Sprintf("%s.name", serviceFullResourceName), + } + + serviceDataCreate := serviceDataBase + serviceDataCreate.MaintenanceDow = "monday" + serviceDataCreate.MaintenanceTime = "01:23:00" + serviceDataCreate.BackupSchedule = "01:23" + serviceDataCreate.IpFilter = []string{"1.2.3.4/32"} + serviceDataCreate.MysqlSettings = strconv.Quote(`{"log_output":"INSIGHTS","long_query_time":1,"slow_query_log":true,"sql_mode":"ANSI,TRADITIONAL","sql_require_primary_key":true}`) + + userDataCreate := userDataBase + userDataCreate.Authentication = "caching_sha2_password" + buf := &bytes.Buffer{} - err = tpl.Execute(buf, &dataCreate) + err = serviceTpl.Execute(buf, &serviceDataCreate) + if err != nil { + t.Fatal(err) + } + err = userTpl.Execute(buf, &userDataCreate) if err != nil { t.Fatal(err) } configCreate := buf.String() - dataUpdate := dataBase - dataUpdate.MaintenanceDow = "tuesday" - dataUpdate.MaintenanceTime = "02:34:00" - dataUpdate.BackupSchedule = "23:45" - dataUpdate.IpFilter = nil - dataUpdate.MysqlSettings = strconv.Quote(`{"log_output":"INSIGHTS","long_query_time":5,"slow_query_log":true,"sql_mode":"ANSI,TRADITIONAL","sql_require_primary_key":true}`) + serviceDataUpdate := serviceDataBase + serviceDataUpdate.MaintenanceDow = "tuesday" + serviceDataUpdate.MaintenanceTime = "02:34:00" + serviceDataUpdate.BackupSchedule = "23:45" + serviceDataUpdate.IpFilter = nil + serviceDataUpdate.MysqlSettings = strconv.Quote(`{"log_output":"INSIGHTS","long_query_time":5,"slow_query_log":true,"sql_mode":"ANSI,TRADITIONAL","sql_require_primary_key":true}`) + + userDataUpdate := userDataBase + userDataUpdate.Authentication = "mysql_native_password" + buf = &bytes.Buffer{} - err = tpl.Execute(buf, &dataUpdate) + err = serviceTpl.Execute(buf, &serviceDataUpdate) + if err != nil { + t.Fatal(err) + } + err = userTpl.Execute(buf, &userDataUpdate) if err != nil { t.Fatal(err) } @@ -85,22 +127,36 @@ func testResourceMysql(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckServiceDestroy("mysql", dataBase.Name), + CheckDestroy: CheckServiceDestroy("mysql", serviceDataBase.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { // Create Config: configCreate, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet(fullResourceName, "created_at"), - resource.TestCheckResourceAttrSet(fullResourceName, "disk_size"), - resource.TestCheckResourceAttrSet(fullResourceName, "node_cpus"), - resource.TestCheckResourceAttrSet(fullResourceName, "node_memory"), - resource.TestCheckResourceAttrSet(fullResourceName, "nodes"), - resource.TestCheckResourceAttrSet(fullResourceName, "ca_certificate"), - resource.TestCheckResourceAttrSet(fullResourceName, "updated_at"), + // Service + resource.TestCheckResourceAttrSet(serviceFullResourceName, "created_at"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "disk_size"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "node_cpus"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "node_memory"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "nodes"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "ca_certificate"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "updated_at"), func(s *terraform.State) error { - err := CheckExistsMysql(dataBase.Name, &dataCreate) + err := CheckExistsMysql(serviceDataBase.Name, &serviceDataCreate) + if err != nil { + return err + } + + return nil + }, + + // User + resource.TestCheckResourceAttrSet(userFullResourceName, "password"), + resource.TestCheckResourceAttrSet(userFullResourceName, "type"), + resource.TestCheckResourceAttrSet(userFullResourceName, "authentication"), + func(s *terraform.State) error { + err := CheckExistsMysqlUser(serviceDataBase.Name, userDataBase.Username, &userDataCreate) if err != nil { return err } @@ -113,22 +169,45 @@ func testResourceMysql(t *testing.T) { // Update Config: configUpdate, Check: resource.ComposeAggregateTestCheckFunc( + // Service func(s *terraform.State) error { - err := CheckExistsMysql(dataBase.Name, &dataUpdate) + err := CheckExistsMysql(serviceDataBase.Name, &serviceDataUpdate) if err != nil { return err } return nil }, + + // User + func(s *terraform.State) error { + // Check the new user exists + err = CheckExistsMysqlUser(serviceDataBase.Name, userDataUpdate.Username, &userDataUpdate) + if err != nil { + return err + } + + return nil + + }, ), }, { // Import - ResourceName: fullResourceName, + ResourceName: serviceFullResourceName, ImportStateIdFunc: func() resource.ImportStateIdFunc { return func(*terraform.State) (string, error) { - return fmt.Sprintf("%s@%s", dataBase.Name, dataBase.Zone), nil + return fmt.Sprintf("%s@%s", serviceDataBase.Name, serviceDataBase.Zone), nil + } + }(), + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: userFullResourceName, + ImportStateIdFunc: func() resource.ImportStateIdFunc { + return func(*terraform.State) (string, error) { + return fmt.Sprintf("%s/%s@%s", serviceDataBase.Name, userDataBase.Username, userDataBase.Zone), nil } }(), ImportState: true, @@ -197,9 +276,46 @@ func CheckExistsMysql(name string, data *TemplateModelMysql) error { } } - if data.Version != *service.Version { - return fmt.Errorf("mysql.version: expected %q, got %q", data.Version, *service.Version) + majVersion := strings.Split(*service.Version, ".")[0] + + if data.Version != majVersion { + return fmt.Errorf("mysql.version: expected %q, got %q", data.Version, majVersion) } return nil } + +func CheckExistsMysqlUser(service, username string, data *TemplateModelMysqlUser) error { + + client, err := testutils.APIClient() + if err != nil { + return err + } + + ctx := exoapi.WithEndpoint(context.Background(), exoapi.NewReqEndpoint(testutils.TestEnvironment(), testutils.TestZoneName)) + + res, err := client.GetDbaasServiceMysqlWithResponse(ctx, oapi.DbaasServiceName(service)) + if err != nil { + return err + } + if res.StatusCode() != http.StatusOK { + return fmt.Errorf("API request error: unexpected status %s", res.Status()) + } + svc := res.JSON200 + + serviceUsernames := make([]string, 0) + if svc.Users != nil { + for _, u := range *svc.Users { + if u.Username != nil { + serviceUsernames = append(serviceUsernames, *u.Username) + if *u.Username == username { + if *u.Authentication == data.Authentication { + return nil + } + } + } + } + } + + return fmt.Errorf("could not find user %s for service %s, found %v", username, service, serviceUsernames) +} diff --git a/pkg/resources/database/resource_opensearch_test.go b/pkg/resources/database/resource_opensearch_test.go index e2b80f5c9..49fa75874 100644 --- a/pkg/resources/database/resource_opensearch_test.go +++ b/pkg/resources/database/resource_opensearch_test.go @@ -63,14 +63,26 @@ type TemplateModelOpensearchDashboards struct { RequestTimeout int64 } +type TemplateModelOpensearchUser struct { + ResourceName string + + Username string + Service string + Zone string +} + func testResourceOpensearch(t *testing.T) { - tpl, err := template.ParseFiles("testdata/resource_opensearch.tmpl") + serviceTpl, err := template.ParseFiles("testdata/resource_opensearch.tmpl") + if err != nil { + t.Fatal(err) + } + userTpl, err := template.ParseFiles("testdata/resource_user_opensearch.tmpl") if err != nil { t.Fatal(err) } - fullResourceName := "exoscale_database.test" - dataBase := TemplateModelOpensearch{ + serviceFullResourceName := "exoscale_database.test" + serviceDataBase := TemplateModelOpensearch{ ResourceName: "test", Name: acctest.RandomWithPrefix(testutils.Prefix), Plan: "hobbyist-2", @@ -79,40 +91,63 @@ func testResourceOpensearch(t *testing.T) { Version: "1", } - dataCreate := dataBase - dataCreate.MaintenanceDow = "monday" - dataCreate.MaintenanceTime = "01:23:00" - dataCreate.IndexPatterns = []TemplateModelOpensearchIndexPattern{ + userFullResourceName := "exoscale_database_opensearch_user.test_user" + userDataBase := TemplateModelOpensearchUser{ + ResourceName: "test_user", + Username: "foo", + Zone: serviceDataBase.Zone, + Service: fmt.Sprintf("%s.name", serviceFullResourceName), + } + + serviceDataCreate := serviceDataBase + serviceDataCreate.MaintenanceDow = "monday" + serviceDataCreate.MaintenanceTime = "01:23:00" + serviceDataCreate.IndexPatterns = []TemplateModelOpensearchIndexPattern{ {2, "log.?", "alphabetical"}, {12, "internet.*", "creation_date"}, } - dataCreate.IndexTemplate = &TemplateModelOpensearchIndexTemplate{5, 4, 3} - dataCreate.Dashboards = &TemplateModelOpensearchDashboards{true, 129, 30001} - dataCreate.KeepIndexRefreshInterval = true - dataCreate.IpFilter = []string{"0.0.0.0/0"} - dataCreate.MaxIndexCount = "4" + serviceDataCreate.IndexTemplate = &TemplateModelOpensearchIndexTemplate{5, 4, 3} + serviceDataCreate.Dashboards = &TemplateModelOpensearchDashboards{true, 129, 30001} + serviceDataCreate.KeepIndexRefreshInterval = true + serviceDataCreate.IpFilter = []string{"0.0.0.0/0"} + serviceDataCreate.MaxIndexCount = "4" + + userDataCreate := userDataBase + buf := &bytes.Buffer{} - err = tpl.Execute(buf, &dataCreate) + err = serviceTpl.Execute(buf, &serviceDataCreate) + if err != nil { + t.Fatal(err) + } + err = userTpl.Execute(buf, &userDataCreate) if err != nil { t.Fatal(err) } configCreate := buf.String() - dataUpdate := dataBase - dataUpdate.MaintenanceDow = "tuesday" - dataUpdate.MaintenanceTime = "02:34:00" - dataUpdate.IndexPatterns = []TemplateModelOpensearchIndexPattern{ + serviceDataUpdate := serviceDataBase + serviceDataUpdate.MaintenanceDow = "tuesday" + serviceDataUpdate.MaintenanceTime = "02:34:00" + serviceDataUpdate.IndexPatterns = []TemplateModelOpensearchIndexPattern{ {4, "log.?", "alphabetical"}, {12, "internet.*", "creation_date"}, } - dataUpdate.IndexTemplate = &TemplateModelOpensearchIndexTemplate{5, 4, 3} - dataUpdate.Dashboards = &TemplateModelOpensearchDashboards{true, 132, 30006} - dataUpdate.KeepIndexRefreshInterval = true - dataUpdate.MaxIndexCount = "0" - dataUpdate.IpFilter = []string{"1.1.1.1/32"} - dataUpdate.IpFilter = nil + serviceDataUpdate.IndexTemplate = &TemplateModelOpensearchIndexTemplate{5, 4, 3} + serviceDataUpdate.Dashboards = &TemplateModelOpensearchDashboards{true, 132, 30006} + serviceDataUpdate.KeepIndexRefreshInterval = true + serviceDataUpdate.MaxIndexCount = "0" + serviceDataUpdate.IpFilter = []string{"1.1.1.1/32"} + serviceDataUpdate.IpFilter = nil + + userDataUpdate := userDataBase + userDataUpdate.Username = "bar" + buf = &bytes.Buffer{} - err = tpl.Execute(buf, &dataUpdate) + err = serviceTpl.Execute(buf, &serviceDataUpdate) + if err != nil { + t.Fatal(err) + } + err = userTpl.Execute(buf, &userDataUpdate) if err != nil { t.Fatal(err) } @@ -120,22 +155,34 @@ func testResourceOpensearch(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckServiceDestroy("opensearch", dataBase.Name), + CheckDestroy: CheckServiceDestroy("opensearch", serviceDataBase.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { // Create Config: configCreate, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet(fullResourceName, "created_at"), - resource.TestCheckResourceAttrSet(fullResourceName, "disk_size"), - resource.TestCheckResourceAttrSet(fullResourceName, "node_cpus"), - resource.TestCheckResourceAttrSet(fullResourceName, "node_memory"), - resource.TestCheckResourceAttrSet(fullResourceName, "nodes"), - resource.TestCheckResourceAttrSet(fullResourceName, "ca_certificate"), - resource.TestCheckResourceAttrSet(fullResourceName, "updated_at"), + // Service + resource.TestCheckResourceAttrSet(serviceFullResourceName, "created_at"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "disk_size"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "node_cpus"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "node_memory"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "nodes"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "ca_certificate"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "updated_at"), + func(s *terraform.State) error { + err := CheckExistsOpensearch(serviceDataBase.Name, &serviceDataCreate) + if err != nil { + return err + } + + return nil + }, + // User + resource.TestCheckResourceAttrSet(userFullResourceName, "password"), + resource.TestCheckResourceAttrSet(userFullResourceName, "type"), func(s *terraform.State) error { - err := CheckExistsOpensearch(dataBase.Name, &dataCreate) + err := CheckExistsOpensearchUser(serviceDataBase.Name, userDataBase.Username, &userDataCreate) if err != nil { return err } @@ -148,8 +195,26 @@ func testResourceOpensearch(t *testing.T) { // Update Config: configUpdate, Check: resource.ComposeAggregateTestCheckFunc( + // Service func(s *terraform.State) error { - err := CheckExistsOpensearch(dataBase.Name, &dataUpdate) + err := CheckExistsOpensearch(serviceDataBase.Name, &serviceDataUpdate) + if err != nil { + return err + } + + return nil + }, + + // User + func(s *terraform.State) error { + // Check the old user was deleted + err := CheckExistsOpensearchUser(serviceDataBase.Name, userDataBase.Username, &userDataUpdate) + if err == nil { + return fmt.Errorf("expected to not find user %s", userDataBase.Username) + } + + // Check the new user exists + err = CheckExistsOpensearchUser(serviceDataBase.Name, userDataUpdate.Username, &userDataUpdate) if err != nil { return err } @@ -160,10 +225,20 @@ func testResourceOpensearch(t *testing.T) { }, { // Import - ResourceName: fullResourceName, + ResourceName: serviceFullResourceName, + ImportStateIdFunc: func() resource.ImportStateIdFunc { + return func(*terraform.State) (string, error) { + return fmt.Sprintf("%s@%s", serviceDataBase.Name, serviceDataBase.Zone), nil + } + }(), + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: userFullResourceName, ImportStateIdFunc: func() resource.ImportStateIdFunc { return func(*terraform.State) (string, error) { - return fmt.Sprintf("%s@%s", dataBase.Name, dataBase.Zone), nil + return fmt.Sprintf("%s/%s@%s", serviceDataBase.Name, userDataUpdate.Username, userDataBase.Zone), nil } }(), ImportState: true, @@ -270,3 +345,36 @@ func CheckExistsOpensearch(name string, data *TemplateModelOpensearch) error { return nil } + +func CheckExistsOpensearchUser(service, username string, data *TemplateModelOpensearchUser) error { + + client, err := testutils.APIClient() + if err != nil { + return err + } + + ctx := exoapi.WithEndpoint(context.Background(), exoapi.NewReqEndpoint(testutils.TestEnvironment(), testutils.TestZoneName)) + + res, err := client.GetDbaasServiceOpensearchWithResponse(ctx, oapi.DbaasServiceName(service)) + if err != nil { + return err + } + if res.StatusCode() != http.StatusOK { + return fmt.Errorf("API request error: unexpected status %s", res.Status()) + } + svc := res.JSON200 + + serviceUsernames := make([]string, 0) + if svc.Users != nil { + for _, u := range *svc.Users { + if u.Username != nil { + serviceUsernames = append(serviceUsernames, *u.Username) + if *u.Username == username { + return nil + } + } + } + } + + return fmt.Errorf("could not find user %s for service %s, found %v", username, service, serviceUsernames) +} diff --git a/pkg/resources/database/resource_pg_test.go b/pkg/resources/database/resource_pg_test.go index 9c291741c..db0f363f8 100644 --- a/pkg/resources/database/resource_pg_test.go +++ b/pkg/resources/database/resource_pg_test.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "strconv" + "strings" "testing" "text/template" @@ -42,14 +43,26 @@ type TemplateModelPg struct { Version string } +type TemplateModelPgUser struct { + ResourceName string + + Username string + Service string + Zone string +} + func testResourcePg(t *testing.T) { - tpl, err := template.ParseFiles("testdata/resource_pg.tmpl") + serviceTpl, err := template.ParseFiles("testdata/resource_pg.tmpl") + if err != nil { + t.Fatal(err) + } + userTpl, err := template.ParseFiles("testdata/resource_user_pg.tmpl") if err != nil { t.Fatal(err) } - fullResourceName := "exoscale_database.test" - dataBase := TemplateModelPg{ + serviceFullResourceName := "exoscale_database.test" + serviceDataBase := TemplateModelPg{ ResourceName: "test", Name: acctest.RandomWithPrefix(testutils.Prefix), Plan: "hobbyist-2", @@ -58,30 +71,53 @@ func testResourcePg(t *testing.T) { Version: "13", } - dataCreate := dataBase - dataCreate.MaintenanceDow = "monday" - dataCreate.MaintenanceTime = "01:23:00" - dataCreate.BackupSchedule = "01:23" - dataCreate.IpFilter = []string{"1.2.3.4/32"} - dataCreate.PgSettings = strconv.Quote(`{"timezone":"Europe/Zurich"}`) - dataCreate.PgbouncerSettings = strconv.Quote(`{"min_pool_size":10}`) + userFullResourceName := "exoscale_database_pg_user.test_user" + userDataBase := TemplateModelPgUser{ + ResourceName: "test_user", + Username: "foo", + Zone: serviceDataBase.Zone, + Service: fmt.Sprintf("%s.name", serviceFullResourceName), + } + + serviceDataCreate := serviceDataBase + serviceDataCreate.MaintenanceDow = "monday" + serviceDataCreate.MaintenanceTime = "01:23:00" + serviceDataCreate.BackupSchedule = "01:23" + serviceDataCreate.IpFilter = []string{"1.2.3.4/32"} + serviceDataCreate.PgSettings = strconv.Quote(`{"timezone":"Europe/Zurich"}`) + serviceDataCreate.PgbouncerSettings = strconv.Quote(`{"min_pool_size":10}`) + + userDataCreate := userDataBase + buf := &bytes.Buffer{} - err = tpl.Execute(buf, &dataCreate) + err = serviceTpl.Execute(buf, &serviceDataCreate) + if err != nil { + t.Fatal(err) + } + err = userTpl.Execute(buf, &userDataCreate) if err != nil { t.Fatal(err) } configCreate := buf.String() - dataUpdate := dataBase - dataUpdate.MaintenanceDow = "tuesday" - dataUpdate.MaintenanceTime = "02:34:00" - dataUpdate.BackupSchedule = "23:45" - dataUpdate.IpFilter = nil - dataUpdate.PgSettings = strconv.Quote(`{"max_worker_processes":10,"timezone":"Europe/Zurich"}`) - dataUpdate.PgbouncerSettings = strconv.Quote(`{"autodb_pool_size":5,"min_pool_size":10}`) - dataUpdate.PglookoutSettings = strconv.Quote(`{"max_failover_replication_time_lag":30}`) + serviceDataUpdate := serviceDataBase + serviceDataUpdate.MaintenanceDow = "tuesday" + serviceDataUpdate.MaintenanceTime = "02:34:00" + serviceDataUpdate.BackupSchedule = "23:45" + serviceDataUpdate.IpFilter = nil + serviceDataUpdate.PgSettings = strconv.Quote(`{"max_worker_processes":10,"timezone":"Europe/Zurich"}`) + serviceDataUpdate.PgbouncerSettings = strconv.Quote(`{"autodb_pool_size":5,"min_pool_size":10}`) + serviceDataUpdate.PglookoutSettings = strconv.Quote(`{"max_failover_replication_time_lag":30}`) + + userDataUpdate := userDataBase + userDataUpdate.Username = "bar" + buf = &bytes.Buffer{} - err = tpl.Execute(buf, &dataUpdate) + err = serviceTpl.Execute(buf, &serviceDataUpdate) + if err != nil { + t.Fatal(err) + } + err = userTpl.Execute(buf, &userDataUpdate) if err != nil { t.Fatal(err) } @@ -89,22 +125,34 @@ func testResourcePg(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testutils.AccPreCheck(t) }, - CheckDestroy: CheckServiceDestroy("pg", dataBase.Name), + CheckDestroy: CheckServiceDestroy("pg", serviceDataBase.Name), ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { // Create Config: configCreate, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet(fullResourceName, "created_at"), - resource.TestCheckResourceAttrSet(fullResourceName, "disk_size"), - resource.TestCheckResourceAttrSet(fullResourceName, "node_cpus"), - resource.TestCheckResourceAttrSet(fullResourceName, "node_memory"), - resource.TestCheckResourceAttrSet(fullResourceName, "nodes"), - resource.TestCheckResourceAttrSet(fullResourceName, "ca_certificate"), - resource.TestCheckResourceAttrSet(fullResourceName, "updated_at"), + // Service + resource.TestCheckResourceAttrSet(serviceFullResourceName, "created_at"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "disk_size"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "node_cpus"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "node_memory"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "nodes"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "ca_certificate"), + resource.TestCheckResourceAttrSet(serviceFullResourceName, "updated_at"), func(s *terraform.State) error { - err := CheckExistsPg(dataBase.Name, &dataCreate) + err := CheckExistsPg(serviceDataBase.Name, &serviceDataCreate) + if err != nil { + return err + } + + return nil + }, + // User + resource.TestCheckResourceAttrSet(userFullResourceName, "password"), + resource.TestCheckResourceAttrSet(userFullResourceName, "type"), + func(s *terraform.State) error { + err := CheckExistsPgUser(serviceDataBase.Name, userDataBase.Username, &userDataCreate) if err != nil { return err } @@ -117,8 +165,26 @@ func testResourcePg(t *testing.T) { // Update Config: configUpdate, Check: resource.ComposeAggregateTestCheckFunc( + // Service + func(s *terraform.State) error { + err := CheckExistsPg(serviceDataBase.Name, &serviceDataUpdate) + if err != nil { + return err + } + + return nil + }, + + // User func(s *terraform.State) error { - err := CheckExistsPg(dataBase.Name, &dataUpdate) + // Check the old user was deleted + err := CheckExistsPgUser(serviceDataBase.Name, userDataBase.Username, &userDataUpdate) + if err == nil { + return fmt.Errorf("expected to not find user %s", userDataBase.Username) + } + + // Check the new user exists + err = CheckExistsPgUser(serviceDataBase.Name, userDataUpdate.Username, &userDataUpdate) if err != nil { return err } @@ -129,16 +195,26 @@ func testResourcePg(t *testing.T) { }, { // Import - ResourceName: fullResourceName, + ResourceName: serviceFullResourceName, ImportStateIdFunc: func() resource.ImportStateIdFunc { return func(*terraform.State) (string, error) { - return fmt.Sprintf("%s@%s", dataBase.Name, dataBase.Zone), nil + return fmt.Sprintf("%s@%s", serviceDataBase.Name, serviceDataBase.Zone), nil } }(), ImportState: true, // NOTE: ImportStateVerify doesn't work when there are optional attributes. //ImportStateVerify: true }, + { + ResourceName: userFullResourceName, + ImportStateIdFunc: func() resource.ImportStateIdFunc { + return func(*terraform.State) (string, error) { + return fmt.Sprintf("%s/%s@%s", serviceDataBase.Name, userDataUpdate.Username, userDataBase.Zone), nil + } + }(), + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -184,11 +260,44 @@ func CheckExistsPg(name string, data *TemplateModelPg) error { return fmt.Errorf("pg.maintenance_time: expected %q, got %q", data.MaintenanceTime, service.Maintenance.Time) } - if data.Version != *service.Version { - return fmt.Errorf("pg.version: expected %q, got %q", data.Version, *service.Version) + serviceMajVersion := strings.Split(*service.Version, ".")[0] + + if data.Version != serviceMajVersion { + return fmt.Errorf("pg.version: expected %q, got %q", data.Version, serviceMajVersion) } // NOTE: Due to default values setup by Aiven, we won't validate settings. return nil } + +func CheckExistsPgUser(service, username string, data *TemplateModelPgUser) error { + + client, err := testutils.APIClient() + if err != nil { + return err + } + + ctx := exoapi.WithEndpoint(context.Background(), exoapi.NewReqEndpoint(testutils.TestEnvironment(), testutils.TestZoneName)) + + res, err := client.GetDbaasServicePgWithResponse(ctx, oapi.DbaasServiceName(service)) + if err != nil { + return err + } + if res.StatusCode() != http.StatusOK { + return fmt.Errorf("API request error: unexpected status %s", res.Status()) + } + svc := res.JSON200 + + serviceUsernames := make([]string, 0) + if svc.Users != nil { + for _, u := range *svc.Users { + serviceUsernames = append(serviceUsernames, u.Username) + if u.Username == username { + return nil + } + } + } + + return fmt.Errorf("could not find user %s for service %s, found %v", username, service, serviceUsernames) +} diff --git a/pkg/resources/database/resource_user.go b/pkg/resources/database/resource_user.go index 13e0fa4ce..72b3c2328 100644 --- a/pkg/resources/database/resource_user.go +++ b/pkg/resources/database/resource_user.go @@ -32,6 +32,7 @@ type UserResourceModel struct { Username types.String `tfsdk:"username"` Password types.String `tfsdk:"password"` Zone types.String `tfsdk:"zone"` + Type types.String `tfsdk:"type"` Timeouts timeouts.Value `tfsdk:"timeouts"` } @@ -78,6 +79,10 @@ var commonAttributes = map[string]schema.Attribute{ Computed: true, Sensitive: true, }, + "type": schema.StringAttribute{ + Description: "The type of the service user.", + Computed: true, + }, } func buildUserAttributes(newAttributes map[string]schema.Attribute) map[string]schema.Attribute { diff --git a/pkg/resources/database/resource_user_kafka.go b/pkg/resources/database/resource_user_kafka.go index 5477efd5f..554e954ce 100644 --- a/pkg/resources/database/resource_user_kafka.go +++ b/pkg/resources/database/resource_user_kafka.go @@ -174,6 +174,7 @@ func (data *KafkaUserResourceModel) CreateResource(ctx context.Context, client * for _, user := range svc.Users { if user.Username == data.Username.ValueString() { data.Password = basetypes.NewStringValue(user.Password) + data.Type = basetypes.NewStringValue(user.Type) data.AccessCert = basetypes.NewStringValue(user.AccessCert) data.AccessKey = basetypes.NewStringValue(user.AccessKey) data.AccessCertExpiry = basetypes.NewStringValue(user.AccessCertExpiry.String()) @@ -216,6 +217,7 @@ func (data *KafkaUserResourceModel) ReadResource(ctx context.Context, client *ex for _, user := range svc.Users { if user.Username == data.Username.ValueString() { data.Password = basetypes.NewStringValue(user.Password) + data.Type = basetypes.NewStringValue(user.Type) data.AccessCert = basetypes.NewStringValue(user.AccessCert) data.AccessKey = basetypes.NewStringValue(user.AccessKey) data.AccessCertExpiry = basetypes.NewStringValue(user.AccessCertExpiry.String()) diff --git a/pkg/resources/database/resource_user_mysql.go b/pkg/resources/database/resource_user_mysql.go index acc3d8a3f..3359fd668 100644 --- a/pkg/resources/database/resource_user_mysql.go +++ b/pkg/resources/database/resource_user_mysql.go @@ -178,6 +178,7 @@ func (data *MysqlUserResourceModel) CreateResource(ctx context.Context, client * for _, user := range svc.Users { if user.Username == data.Username.ValueString() { data.Password = basetypes.NewStringValue(user.Password) + data.Type = basetypes.NewStringValue(user.Type) data.Authentication = basetypes.NewStringValue(user.Authentication) return } @@ -218,6 +219,7 @@ func (data *MysqlUserResourceModel) ReadResource(ctx context.Context, client *ex for _, user := range svc.Users { if user.Username == data.Username.ValueString() { data.Password = basetypes.NewStringValue(user.Password) + data.Type = basetypes.NewStringValue(user.Type) data.Authentication = basetypes.NewStringValue(user.Authentication) return } diff --git a/pkg/resources/database/resource_user_opensearch.go b/pkg/resources/database/resource_user_opensearch.go index ebd91307b..ba590a26f 100644 --- a/pkg/resources/database/resource_user_opensearch.go +++ b/pkg/resources/database/resource_user_opensearch.go @@ -155,6 +155,7 @@ func (data *OpensearchUserResourceModel) CreateResource(ctx context.Context, cli for _, user := range svc.Users { if user.Username == data.Username.ValueString() { data.Password = basetypes.NewStringValue(user.Password) + data.Type = basetypes.NewStringValue(user.Type) return } } @@ -194,6 +195,7 @@ func (data *OpensearchUserResourceModel) ReadResource(ctx context.Context, clien for _, user := range svc.Users { if user.Username == data.Username.ValueString() { data.Password = basetypes.NewStringValue(user.Password) + data.Type = basetypes.NewStringValue(user.Type) return } } diff --git a/pkg/resources/database/resource_user_pg.go b/pkg/resources/database/resource_user_pg.go index a1d6466da..20df3de5f 100644 --- a/pkg/resources/database/resource_user_pg.go +++ b/pkg/resources/database/resource_user_pg.go @@ -31,7 +31,7 @@ type PGUserResource struct { type PGUserResourceModel struct { UserResourceModel - AllowReplication types.Bool `tfsdk:"replication"` + AllowReplication types.Bool `tfsdk:"allow_replication"` } func (r *PGUserResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { @@ -173,6 +173,7 @@ func (data *PGUserResourceModel) CreateResource(ctx context.Context, client *exo for _, user := range svc.Users { if user.Username == data.Username.ValueString() { data.Password = basetypes.NewStringValue(user.Password) + data.Type = basetypes.NewStringValue(user.Type) if user.AllowReplication != nil { data.AllowReplication = basetypes.NewBoolValue(*user.AllowReplication) } @@ -215,6 +216,7 @@ func (data *PGUserResourceModel) ReadResource(ctx context.Context, client *exosc for _, user := range svc.Users { if user.Username == data.Username.ValueString() { data.Password = basetypes.NewStringValue(user.Password) + data.Type = basetypes.NewStringValue(user.Type) if user.AllowReplication != nil { data.AllowReplication = basetypes.NewBoolValue(*user.AllowReplication) } diff --git a/pkg/resources/database/testdata/resource_user_mysql.tmpl b/pkg/resources/database/testdata/resource_user_mysql.tmpl new file mode 100644 index 000000000..809aa612f --- /dev/null +++ b/pkg/resources/database/testdata/resource_user_mysql.tmpl @@ -0,0 +1,9 @@ +resource "exoscale_database_mysql_user" {{ .ResourceName }} { + username = "{{ .Username }}" + service = {{ .Service }} + zone = "{{ .Zone }}" + + {{- if .Authentication }} + authentication = "{{ .Authentication }}" + {{- end }} +} diff --git a/pkg/resources/database/testdata/resource_user_opensearch.tmpl b/pkg/resources/database/testdata/resource_user_opensearch.tmpl new file mode 100644 index 000000000..3f737cb05 --- /dev/null +++ b/pkg/resources/database/testdata/resource_user_opensearch.tmpl @@ -0,0 +1,5 @@ +resource "exoscale_database_opensearch_user" {{ .ResourceName }} { + username = "{{ .Username }}" + service = {{ .Service }} + zone = "{{ .Zone }}" +} diff --git a/pkg/resources/database/testdata/resource_user_pg.tmpl b/pkg/resources/database/testdata/resource_user_pg.tmpl new file mode 100644 index 000000000..4de2cd600 --- /dev/null +++ b/pkg/resources/database/testdata/resource_user_pg.tmpl @@ -0,0 +1,5 @@ +resource "exoscale_database_pg_user" {{ .ResourceName }} { + username = "{{ .Username }}" + service = {{ .Service }} + zone = "{{ .Zone }}" +} From 698f8f364df807afa57a80358c5a54caf37bc3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Tue, 10 Dec 2024 14:43:03 +0100 Subject: [PATCH 05/15] Doc --- docs/resources/database_kafka_user.md | 49 ++++++++++++++++++++++ docs/resources/database_mysql_user.md | 47 +++++++++++++++++++++ docs/resources/database_opensearch_user.md | 46 ++++++++++++++++++++ docs/resources/database_pg_user.md | 47 +++++++++++++++++++++ 4 files changed, 189 insertions(+) create mode 100644 docs/resources/database_kafka_user.md create mode 100644 docs/resources/database_mysql_user.md create mode 100644 docs/resources/database_opensearch_user.md create mode 100644 docs/resources/database_pg_user.md diff --git a/docs/resources/database_kafka_user.md b/docs/resources/database_kafka_user.md new file mode 100644 index 000000000..a088762de --- /dev/null +++ b/docs/resources/database_kafka_user.md @@ -0,0 +1,49 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "exoscale_database_kafka_user Resource - terraform-provider-exoscale" +subcategory: "" +description: |- + Manage service users for a Kafka Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. +--- + +# exoscale_database_kafka_user (Resource) + +Manage service users for a Kafka Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). + + + + +## Schema + +### Required + +- `service` (String) ❗ The name of the database service. +- `username` (String) ❗ The name of the user for this service. +- `zone` (String) ❗ The Exoscale [Zone](https://www.exoscale.com/datacenters/) name. + +### Optional + +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `access_cert` (String, Sensitive) Access certificate for the user. +- `access_cert_expiry` (String) Access certificate expiry date. +- `access_key` (String, Sensitive) Access certificate key for the user. +- `id` (String) The ID of this resource, computed as SERVICENAME/USERNAME +- `password` (String, Sensitive) The password of the service user. +- `type` (String) The type of the service user. + + +### 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. +- `read` (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). Read operations occur during any refresh or planning operation when refresh is enabled. +- `update` (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). + +-> The symbol ❗ in an attribute indicates that modifying it, will force the creation of a new resource. + + diff --git a/docs/resources/database_mysql_user.md b/docs/resources/database_mysql_user.md new file mode 100644 index 000000000..769117fce --- /dev/null +++ b/docs/resources/database_mysql_user.md @@ -0,0 +1,47 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "exoscale_database_mysql_user Resource - terraform-provider-exoscale" +subcategory: "" +description: |- + Manage service users for MySQL Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. +--- + +# exoscale_database_mysql_user (Resource) + +Manage service users for MySQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). + + + + +## Schema + +### Required + +- `service` (String) ❗ The name of the database service. +- `username` (String) ❗ The name of the user for this service. +- `zone` (String) ❗ The Exoscale [Zone](https://www.exoscale.com/datacenters/) name. + +### Optional + +- `authentication` (String) Authentication details. The possible values are `null`, `caching_sha2_password` and `mysql_native_password`. +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `id` (String) The ID of this resource, computed as SERVICENAME/USERNAME +- `password` (String, Sensitive) The password of the service user. +- `type` (String) The type of the service user. + + +### 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. +- `read` (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). Read operations occur during any refresh or planning operation when refresh is enabled. +- `update` (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). + +-> The symbol ❗ in an attribute indicates that modifying it, will force the creation of a new resource. + + diff --git a/docs/resources/database_opensearch_user.md b/docs/resources/database_opensearch_user.md new file mode 100644 index 000000000..74e3b6414 --- /dev/null +++ b/docs/resources/database_opensearch_user.md @@ -0,0 +1,46 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "exoscale_database_opensearch_user Resource - terraform-provider-exoscale" +subcategory: "" +description: |- + Manage service users for an Opensearch Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. +--- + +# exoscale_database_opensearch_user (Resource) + +Manage service users for an Opensearch Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). + + + + +## Schema + +### Required + +- `service` (String) ❗ The name of the database service. +- `username` (String) ❗ The name of the user for this service. +- `zone` (String) ❗ The Exoscale [Zone](https://www.exoscale.com/datacenters/) name. + +### Optional + +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `id` (String) The ID of this resource, computed as SERVICENAME/USERNAME +- `password` (String, Sensitive) The password of the service user. +- `type` (String) The type of the service user. + + +### 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. +- `read` (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). Read operations occur during any refresh or planning operation when refresh is enabled. +- `update` (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). + +-> The symbol ❗ in an attribute indicates that modifying it, will force the creation of a new resource. + + diff --git a/docs/resources/database_pg_user.md b/docs/resources/database_pg_user.md new file mode 100644 index 000000000..be10d61be --- /dev/null +++ b/docs/resources/database_pg_user.md @@ -0,0 +1,47 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "exoscale_database_pg_user Resource - terraform-provider-exoscale" +subcategory: "" +description: |- + Manage service users for a PostgreSQL Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. +--- + +# exoscale_database_pg_user (Resource) + +Manage service users for a PostgreSQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). + + + + +## Schema + +### Required + +- `service` (String) ❗ The name of the database service. +- `username` (String) ❗ The name of the user for this service. +- `zone` (String) ❗ The Exoscale [Zone](https://www.exoscale.com/datacenters/) name. + +### Optional + +- `allow_replication` (Boolean) Allows replication +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `id` (String) The ID of this resource, computed as SERVICENAME/USERNAME +- `password` (String, Sensitive) The password of the service user. +- `type` (String) The type of the service user. + + +### 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. +- `read` (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). Read operations occur during any refresh or planning operation when refresh is enabled. +- `update` (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). + +-> The symbol ❗ in an attribute indicates that modifying it, will force the creation of a new resource. + + From 3ac03e047ae1d5fbe142c2be3e600f16c60d4c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Tue, 10 Dec 2024 15:17:19 +0100 Subject: [PATCH 06/15] Update doc --- docs/resources/database_kafka_user.md | 2 +- docs/resources/database_mysql_user.md | 4 ++-- docs/resources/database_opensearch_user.md | 2 +- docs/resources/database_pg_user.md | 6 +++--- pkg/resources/database/resource_user.go | 2 +- pkg/resources/database/resource_user_mysql.go | 2 +- pkg/resources/database/resource_user_pg.go | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/resources/database_kafka_user.md b/docs/resources/database_kafka_user.md index a088762de..9d011088f 100644 --- a/docs/resources/database_kafka_user.md +++ b/docs/resources/database_kafka_user.md @@ -30,7 +30,7 @@ Manage service users for a Kafka Exoscale [Database Services (DBaaS)](https://co - `access_cert` (String, Sensitive) Access certificate for the user. - `access_cert_expiry` (String) Access certificate expiry date. - `access_key` (String, Sensitive) Access certificate key for the user. -- `id` (String) The ID of this resource, computed as SERVICENAME/USERNAME +- `id` (String) The ID of this resource, computed as service/username - `password` (String, Sensitive) The password of the service user. - `type` (String) The type of the service user. diff --git a/docs/resources/database_mysql_user.md b/docs/resources/database_mysql_user.md index 769117fce..b03e01ac7 100644 --- a/docs/resources/database_mysql_user.md +++ b/docs/resources/database_mysql_user.md @@ -23,12 +23,12 @@ Manage service users for MySQL Exoscale [Database Services (DBaaS)](https://comm ### Optional -- `authentication` (String) Authentication details. The possible values are `null`, `caching_sha2_password` and `mysql_native_password`. +- `authentication` (String) ❗ Authentication details. The possible values are `null`, `caching_sha2_password` and `mysql_native_password`. - `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) ### Read-Only -- `id` (String) The ID of this resource, computed as SERVICENAME/USERNAME +- `id` (String) The ID of this resource, computed as service/username - `password` (String, Sensitive) The password of the service user. - `type` (String) The type of the service user. diff --git a/docs/resources/database_opensearch_user.md b/docs/resources/database_opensearch_user.md index 74e3b6414..95dbaffec 100644 --- a/docs/resources/database_opensearch_user.md +++ b/docs/resources/database_opensearch_user.md @@ -27,7 +27,7 @@ Manage service users for an Opensearch Exoscale [Database Services (DBaaS)](http ### Read-Only -- `id` (String) The ID of this resource, computed as SERVICENAME/USERNAME +- `id` (String) The ID of this resource, computed as service/username - `password` (String, Sensitive) The password of the service user. - `type` (String) The type of the service user. diff --git a/docs/resources/database_pg_user.md b/docs/resources/database_pg_user.md index be10d61be..b42caff88 100644 --- a/docs/resources/database_pg_user.md +++ b/docs/resources/database_pg_user.md @@ -3,12 +3,12 @@ page_title: "exoscale_database_pg_user Resource - terraform-provider-exoscale" subcategory: "" description: |- - Manage service users for a PostgreSQL Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. + ❗ Manage service users for a PostgreSQL Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. --- # exoscale_database_pg_user (Resource) -Manage service users for a PostgreSQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). +❗ Manage service users for a PostgreSQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). @@ -28,7 +28,7 @@ Manage service users for a PostgreSQL Exoscale [Database Services (DBaaS)](https ### Read-Only -- `id` (String) The ID of this resource, computed as SERVICENAME/USERNAME +- `id` (String) The ID of this resource, computed as service/username - `password` (String, Sensitive) The password of the service user. - `type` (String) The type of the service user. diff --git a/pkg/resources/database/resource_user.go b/pkg/resources/database/resource_user.go index 72b3c2328..458c452ec 100644 --- a/pkg/resources/database/resource_user.go +++ b/pkg/resources/database/resource_user.go @@ -68,7 +68,7 @@ var commonAttributes = map[string]schema.Attribute{ // Computed attributes "id": schema.StringAttribute{ - MarkdownDescription: "The ID of this resource, computed as SERVICENAME/USERNAME", + MarkdownDescription: "The ID of this resource, computed as service/username", Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), diff --git a/pkg/resources/database/resource_user_mysql.go b/pkg/resources/database/resource_user_mysql.go index 3359fd668..064bf8bc6 100644 --- a/pkg/resources/database/resource_user_mysql.go +++ b/pkg/resources/database/resource_user_mysql.go @@ -53,7 +53,7 @@ func (r *MysqlUserResource) Schema(ctx context.Context, req resource.SchemaReque MarkdownDescription: "Manage service users for MySQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/).", Attributes: buildUserAttributes(map[string]schema.Attribute{ "authentication": schema.StringAttribute{ - MarkdownDescription: "Authentication details. The possible values are `null`, `caching_sha2_password` and `mysql_native_password`.", + MarkdownDescription: "❗ Authentication details. The possible values are `null`, `caching_sha2_password` and `mysql_native_password`.", Optional: true, Computed: true, Validators: []validator.String{ diff --git a/pkg/resources/database/resource_user_pg.go b/pkg/resources/database/resource_user_pg.go index 20df3de5f..711925858 100644 --- a/pkg/resources/database/resource_user_pg.go +++ b/pkg/resources/database/resource_user_pg.go @@ -48,7 +48,7 @@ func (r *PGUserResource) Metadata(ctx context.Context, req resource.MetadataRequ func (r *PGUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ // This description is used by the documentation generator and the language server. - MarkdownDescription: "Manage service users for a PostgreSQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/).", + MarkdownDescription: "❗ Manage service users for a PostgreSQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/).", Attributes: buildUserAttributes(map[string]schema.Attribute{ "allow_replication": schema.BoolAttribute{ MarkdownDescription: "Allows replication", From 56c3299adf8fafb5671285a93a35775430ed78a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Wed, 11 Dec 2024 13:37:05 +0100 Subject: [PATCH 07/15] vendoring --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index 571153344..f94441e1d 100644 --- a/go.sum +++ b/go.sum @@ -175,8 +175,6 @@ github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7 github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A= github.com/hashicorp/terraform-plugin-docs v0.16.0 h1:UmxFr3AScl6Wged84jndJIfFccGyBZn52KtMNsS12dI= github.com/hashicorp/terraform-plugin-docs v0.16.0/go.mod h1:M3ZrlKBJAbPMtNOPwHicGi1c+hZUh7/g0ifT/z7TVfA= -github.com/hashicorp/terraform-plugin-framework v1.11.0 h1:M7+9zBArexHFXDx/pKTxjE6n/2UCXY6b8FIq9ZYhwfE= -github.com/hashicorp/terraform-plugin-framework v1.11.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM= github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ532Bh2BobF4H+yBiYJ/scw= github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU= github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0 h1:SJXL5FfJJm17554Kpt9jFXngdM6fXbnUnZ6iT2IeiYA= From ec24098994ef6e78af5bed97fd9e39543816476d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Wed, 11 Dec 2024 14:32:18 +0100 Subject: [PATCH 08/15] Fix test --- pkg/resources/database/resource_mysql_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/resources/database/resource_mysql_test.go b/pkg/resources/database/resource_mysql_test.go index f77b0d350..30bcb4f7b 100644 --- a/pkg/resources/database/resource_mysql_test.go +++ b/pkg/resources/database/resource_mysql_test.go @@ -200,8 +200,9 @@ func testResourceMysql(t *testing.T) { return fmt.Sprintf("%s@%s", serviceDataBase.Name, serviceDataBase.Zone), nil } }(), - ImportState: true, - ImportStateVerify: true, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: strings.Fields("updated_at"), }, { ResourceName: userFullResourceName, From 20967715734bd003b4a5e3e8d19cc7c75cb51107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Wed, 18 Dec 2024 16:39:57 +0100 Subject: [PATCH 09/15] Rename resource to exoscale_dbaas_DBTYPE_user --- pkg/resources/database/resource_kafka_test.go | 2 +- pkg/resources/database/resource_mysql_test.go | 2 +- pkg/resources/database/resource_opensearch_test.go | 2 +- pkg/resources/database/resource_pg_test.go | 2 +- pkg/resources/database/resource_user_kafka.go | 2 +- pkg/resources/database/resource_user_mysql.go | 2 +- pkg/resources/database/resource_user_opensearch.go | 2 +- pkg/resources/database/resource_user_pg.go | 2 +- pkg/resources/database/testdata/resource_user_kafka.tmpl | 2 +- pkg/resources/database/testdata/resource_user_mysql.tmpl | 2 +- pkg/resources/database/testdata/resource_user_opensearch.tmpl | 2 +- pkg/resources/database/testdata/resource_user_pg.tmpl | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/resources/database/resource_kafka_test.go b/pkg/resources/database/resource_kafka_test.go index 449efe745..a5ad98003 100644 --- a/pkg/resources/database/resource_kafka_test.go +++ b/pkg/resources/database/resource_kafka_test.go @@ -81,7 +81,7 @@ func testResourceKafka(t *testing.T) { Version: "3.7", } - userFullResourceName := "exoscale_database_kafka_user.test_user" + userFullResourceName := "exoscale_dbaas_kafka_user.test_user" userDataBase := TemplateModelKafkaUser{ ResourceName: "test_user", Username: "foo", diff --git a/pkg/resources/database/resource_mysql_test.go b/pkg/resources/database/resource_mysql_test.go index 30bcb4f7b..61651690d 100644 --- a/pkg/resources/database/resource_mysql_test.go +++ b/pkg/resources/database/resource_mysql_test.go @@ -75,7 +75,7 @@ func testResourceMysql(t *testing.T) { Version: "8", } - userFullResourceName := "exoscale_database_mysql_user.test_user" + userFullResourceName := "exoscale_dbaas_mysql_user.test_user" userDataBase := TemplateModelMysqlUser{ ResourceName: "test_user", Username: "foo", diff --git a/pkg/resources/database/resource_opensearch_test.go b/pkg/resources/database/resource_opensearch_test.go index 49fa75874..43f1bb242 100644 --- a/pkg/resources/database/resource_opensearch_test.go +++ b/pkg/resources/database/resource_opensearch_test.go @@ -91,7 +91,7 @@ func testResourceOpensearch(t *testing.T) { Version: "1", } - userFullResourceName := "exoscale_database_opensearch_user.test_user" + userFullResourceName := "exoscale_dbaas_opensearch_user.test_user" userDataBase := TemplateModelOpensearchUser{ ResourceName: "test_user", Username: "foo", diff --git a/pkg/resources/database/resource_pg_test.go b/pkg/resources/database/resource_pg_test.go index db0f363f8..304085f06 100644 --- a/pkg/resources/database/resource_pg_test.go +++ b/pkg/resources/database/resource_pg_test.go @@ -71,7 +71,7 @@ func testResourcePg(t *testing.T) { Version: "13", } - userFullResourceName := "exoscale_database_pg_user.test_user" + userFullResourceName := "exoscale_dbaas_pg_user.test_user" userDataBase := TemplateModelPgUser{ ResourceName: "test_user", Username: "foo", diff --git a/pkg/resources/database/resource_user_kafka.go b/pkg/resources/database/resource_user_kafka.go index 554e954ce..1a35b6fab 100644 --- a/pkg/resources/database/resource_user_kafka.go +++ b/pkg/resources/database/resource_user_kafka.go @@ -42,7 +42,7 @@ func (r *KafkaUserResource) Configure(ctx context.Context, req resource.Configur } func (r *KafkaUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_database_kafka_user" + resp.TypeName = req.ProviderTypeName + "_dbaas_kafka_user" } func (r *KafkaUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { diff --git a/pkg/resources/database/resource_user_mysql.go b/pkg/resources/database/resource_user_mysql.go index 064bf8bc6..8d5784150 100644 --- a/pkg/resources/database/resource_user_mysql.go +++ b/pkg/resources/database/resource_user_mysql.go @@ -44,7 +44,7 @@ func (r *MysqlUserResource) Configure(ctx context.Context, req resource.Configur } func (r *MysqlUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_database_mysql_user" + resp.TypeName = req.ProviderTypeName + "_dbaas_mysql_user" } func (r *MysqlUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { diff --git a/pkg/resources/database/resource_user_opensearch.go b/pkg/resources/database/resource_user_opensearch.go index ba590a26f..e218c3993 100644 --- a/pkg/resources/database/resource_user_opensearch.go +++ b/pkg/resources/database/resource_user_opensearch.go @@ -39,7 +39,7 @@ func (r *OpensearchUserResource) Configure(ctx context.Context, req resource.Con } func (r *OpensearchUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_database_opensearch_user" + resp.TypeName = req.ProviderTypeName + "_dbaas_opensearch_user" } func (r *OpensearchUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { diff --git a/pkg/resources/database/resource_user_pg.go b/pkg/resources/database/resource_user_pg.go index 711925858..9a781d1ac 100644 --- a/pkg/resources/database/resource_user_pg.go +++ b/pkg/resources/database/resource_user_pg.go @@ -42,7 +42,7 @@ func (r *PGUserResource) Configure(ctx context.Context, req resource.ConfigureRe } func (r *PGUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_database_pg_user" + resp.TypeName = req.ProviderTypeName + "_dbaas_pg_user" } func (r *PGUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { diff --git a/pkg/resources/database/testdata/resource_user_kafka.tmpl b/pkg/resources/database/testdata/resource_user_kafka.tmpl index 431c4dc38..82ec25a2c 100644 --- a/pkg/resources/database/testdata/resource_user_kafka.tmpl +++ b/pkg/resources/database/testdata/resource_user_kafka.tmpl @@ -1,4 +1,4 @@ -resource "exoscale_database_kafka_user" {{ .ResourceName }} { +resource "exoscale_dbaas_kafka_user" {{ .ResourceName }} { username = "{{ .Username }}" service = {{ .Service }} zone = "{{ .Zone }}" diff --git a/pkg/resources/database/testdata/resource_user_mysql.tmpl b/pkg/resources/database/testdata/resource_user_mysql.tmpl index 809aa612f..2d4127fe3 100644 --- a/pkg/resources/database/testdata/resource_user_mysql.tmpl +++ b/pkg/resources/database/testdata/resource_user_mysql.tmpl @@ -1,4 +1,4 @@ -resource "exoscale_database_mysql_user" {{ .ResourceName }} { +resource "exoscale_dbaas_mysql_user" {{ .ResourceName }} { username = "{{ .Username }}" service = {{ .Service }} zone = "{{ .Zone }}" diff --git a/pkg/resources/database/testdata/resource_user_opensearch.tmpl b/pkg/resources/database/testdata/resource_user_opensearch.tmpl index 3f737cb05..bfd8d6541 100644 --- a/pkg/resources/database/testdata/resource_user_opensearch.tmpl +++ b/pkg/resources/database/testdata/resource_user_opensearch.tmpl @@ -1,4 +1,4 @@ -resource "exoscale_database_opensearch_user" {{ .ResourceName }} { +resource "exoscale_dbaas_opensearch_user" {{ .ResourceName }} { username = "{{ .Username }}" service = {{ .Service }} zone = "{{ .Zone }}" diff --git a/pkg/resources/database/testdata/resource_user_pg.tmpl b/pkg/resources/database/testdata/resource_user_pg.tmpl index 4de2cd600..cc470502d 100644 --- a/pkg/resources/database/testdata/resource_user_pg.tmpl +++ b/pkg/resources/database/testdata/resource_user_pg.tmpl @@ -1,4 +1,4 @@ -resource "exoscale_database_pg_user" {{ .ResourceName }} { +resource "exoscale_dbaas_pg_user" {{ .ResourceName }} { username = "{{ .Username }}" service = {{ .Service }} zone = "{{ .Zone }}" From 3e95ccebf9b1dc083e85a1de8079b3fe52f9bfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Wed, 18 Dec 2024 16:40:35 +0100 Subject: [PATCH 10/15] doc --- .../resources/{database_kafka_user.md => dbaas_kafka_user.md} | 4 ++-- .../resources/{database_mysql_user.md => dbaas_mysql_user.md} | 4 ++-- .../{database_opensearch_user.md => dbaas_opensearch_user.md} | 4 ++-- docs/resources/{database_pg_user.md => dbaas_pg_user.md} | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename docs/resources/{database_kafka_user.md => dbaas_kafka_user.md} (95%) rename docs/resources/{database_mysql_user.md => dbaas_mysql_user.md} (95%) rename docs/resources/{database_opensearch_user.md => dbaas_opensearch_user.md} (94%) rename docs/resources/{database_pg_user.md => dbaas_pg_user.md} (95%) diff --git a/docs/resources/database_kafka_user.md b/docs/resources/dbaas_kafka_user.md similarity index 95% rename from docs/resources/database_kafka_user.md rename to docs/resources/dbaas_kafka_user.md index 9d011088f..0865d36f9 100644 --- a/docs/resources/database_kafka_user.md +++ b/docs/resources/dbaas_kafka_user.md @@ -1,12 +1,12 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "exoscale_database_kafka_user Resource - terraform-provider-exoscale" +page_title: "exoscale_dbaas_kafka_user Resource - terraform-provider-exoscale" subcategory: "" description: |- Manage service users for a Kafka Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. --- -# exoscale_database_kafka_user (Resource) +# exoscale_dbaas_kafka_user (Resource) Manage service users for a Kafka Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). diff --git a/docs/resources/database_mysql_user.md b/docs/resources/dbaas_mysql_user.md similarity index 95% rename from docs/resources/database_mysql_user.md rename to docs/resources/dbaas_mysql_user.md index b03e01ac7..413f5891f 100644 --- a/docs/resources/database_mysql_user.md +++ b/docs/resources/dbaas_mysql_user.md @@ -1,12 +1,12 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "exoscale_database_mysql_user Resource - terraform-provider-exoscale" +page_title: "exoscale_dbaas_mysql_user Resource - terraform-provider-exoscale" subcategory: "" description: |- Manage service users for MySQL Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. --- -# exoscale_database_mysql_user (Resource) +# exoscale_dbaas_mysql_user (Resource) Manage service users for MySQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). diff --git a/docs/resources/database_opensearch_user.md b/docs/resources/dbaas_opensearch_user.md similarity index 94% rename from docs/resources/database_opensearch_user.md rename to docs/resources/dbaas_opensearch_user.md index 95dbaffec..3b9d9b271 100644 --- a/docs/resources/database_opensearch_user.md +++ b/docs/resources/dbaas_opensearch_user.md @@ -1,12 +1,12 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "exoscale_database_opensearch_user Resource - terraform-provider-exoscale" +page_title: "exoscale_dbaas_opensearch_user Resource - terraform-provider-exoscale" subcategory: "" description: |- Manage service users for an Opensearch Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. --- -# exoscale_database_opensearch_user (Resource) +# exoscale_dbaas_opensearch_user (Resource) Manage service users for an Opensearch Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). diff --git a/docs/resources/database_pg_user.md b/docs/resources/dbaas_pg_user.md similarity index 95% rename from docs/resources/database_pg_user.md rename to docs/resources/dbaas_pg_user.md index b42caff88..8d2f10705 100644 --- a/docs/resources/database_pg_user.md +++ b/docs/resources/dbaas_pg_user.md @@ -1,12 +1,12 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "exoscale_database_pg_user Resource - terraform-provider-exoscale" +page_title: "exoscale_dbaas_pg_user Resource - terraform-provider-exoscale" subcategory: "" description: |- ❗ Manage service users for a PostgreSQL Exoscale Database Services (DBaaS) https://community.exoscale.com/documentation/dbaas/. --- -# exoscale_database_pg_user (Resource) +# exoscale_dbaas_pg_user (Resource) ❗ Manage service users for a PostgreSQL Exoscale [Database Services (DBaaS)](https://community.exoscale.com/documentation/dbaas/). From 223407fde3dfb67d8ff88214848579026f8e5576 Mon Sep 17 00:00:00 2001 From: tgrondier <5384645+tgrondier@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:27:52 +0100 Subject: [PATCH 11/15] Update pkg/resources/database/resource_kafka_test.go Co-authored-by: Philipp Sauter <46172817+sauterp@users.noreply.github.com> --- pkg/resources/database/resource_kafka_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/resources/database/resource_kafka_test.go b/pkg/resources/database/resource_kafka_test.go index a5ad98003..f45698870 100644 --- a/pkg/resources/database/resource_kafka_test.go +++ b/pkg/resources/database/resource_kafka_test.go @@ -245,7 +245,7 @@ func CheckExistsKafka(name string, data *TemplateModelKafka) error { res, err := client.GetDbaasServiceKafkaWithResponse(ctx, oapi.DbaasServiceName(name)) if err != nil { - return fmt.Errorf("aww") + return err } if res.StatusCode() != http.StatusOK { return fmt.Errorf("API request error: unexpected status %s", res.Status()) From e694c458a6231bbfc8b090626f6e7d1ed5b1ed07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Fri, 20 Dec 2024 16:30:38 +0100 Subject: [PATCH 12/15] Fixup unchecked error --- pkg/resources/database/resource_user.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/resources/database/resource_user.go b/pkg/resources/database/resource_user.go index 458c452ec..f3ab33e3e 100644 --- a/pkg/resources/database/resource_user.go +++ b/pkg/resources/database/resource_user.go @@ -245,8 +245,11 @@ func UserCreate[T ResourceModelInterface](ctx context.Context, req resource.Crea } data.WaitForService(ctx, client, &resp.Diagnostics) - data.CreateResource(ctx, client, &diags) + if resp.Diagnostics.HasError() { + return + } + data.CreateResource(ctx, client, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } From e4f3e14626ed2aa561fcec0fa5b1de896613311d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Mon, 23 Dec 2024 12:58:18 +0100 Subject: [PATCH 13/15] Ignore state when importing --- pkg/resources/database/resource_grafana_test.go | 6 ++++-- pkg/resources/database/resource_kafka_test.go | 6 ++++-- pkg/resources/database/resource_mysql_test.go | 2 +- pkg/resources/database/resource_opensearch_test.go | 6 ++++-- pkg/resources/database/resource_redis_test.go | 6 ++++-- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/pkg/resources/database/resource_grafana_test.go b/pkg/resources/database/resource_grafana_test.go index b9f25790e..21b0d8f94 100644 --- a/pkg/resources/database/resource_grafana_test.go +++ b/pkg/resources/database/resource_grafana_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "strconv" + "strings" "testing" "text/template" @@ -124,8 +125,9 @@ func testResourceGrafana(t *testing.T) { return fmt.Sprintf("%s@%s", dataBase.Name, dataBase.Zone), nil } }(), - ImportState: true, - ImportStateVerify: true, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: strings.Fields("updated_at state"), }, }, }) diff --git a/pkg/resources/database/resource_kafka_test.go b/pkg/resources/database/resource_kafka_test.go index f45698870..1252d3f1f 100644 --- a/pkg/resources/database/resource_kafka_test.go +++ b/pkg/resources/database/resource_kafka_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "strconv" + "strings" "testing" "text/template" @@ -218,8 +219,9 @@ func testResourceKafka(t *testing.T) { return fmt.Sprintf("%s@%s", serviceDataBase.Name, serviceDataBase.Zone), nil } }(), - ImportState: true, - ImportStateVerify: true, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: strings.Fields("updated_at state"), }, { ResourceName: userFullResourceName, diff --git a/pkg/resources/database/resource_mysql_test.go b/pkg/resources/database/resource_mysql_test.go index 61651690d..8a2891064 100644 --- a/pkg/resources/database/resource_mysql_test.go +++ b/pkg/resources/database/resource_mysql_test.go @@ -202,7 +202,7 @@ func testResourceMysql(t *testing.T) { }(), ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: strings.Fields("updated_at"), + ImportStateVerifyIgnore: strings.Fields("updated_at state"), }, { ResourceName: userFullResourceName, diff --git a/pkg/resources/database/resource_opensearch_test.go b/pkg/resources/database/resource_opensearch_test.go index 43f1bb242..789473bb4 100644 --- a/pkg/resources/database/resource_opensearch_test.go +++ b/pkg/resources/database/resource_opensearch_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "strconv" + "strings" "testing" "text/template" @@ -231,8 +232,9 @@ func testResourceOpensearch(t *testing.T) { return fmt.Sprintf("%s@%s", serviceDataBase.Name, serviceDataBase.Zone), nil } }(), - ImportState: true, - ImportStateVerify: true, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: strings.Fields("updated_at state"), }, { ResourceName: userFullResourceName, diff --git a/pkg/resources/database/resource_redis_test.go b/pkg/resources/database/resource_redis_test.go index 79c55c6bf..8d6219708 100644 --- a/pkg/resources/database/resource_redis_test.go +++ b/pkg/resources/database/resource_redis_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "strconv" + "strings" "testing" "text/template" @@ -124,8 +125,9 @@ func testResourceRedis(t *testing.T) { return fmt.Sprintf("%s@%s", dataBase.Name, dataBase.Zone), nil } }(), - ImportState: true, - ImportStateVerify: true, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: strings.Fields("updated_at state"), }, }, }) From a0088dd81b717e1bd69085a9a158a01bf2a6f257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Mon, 23 Dec 2024 13:46:57 +0100 Subject: [PATCH 14/15] mysql user: wait for db RUNNING before allowing user creation --- pkg/resources/database/resource_user_mysql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/resources/database/resource_user_mysql.go b/pkg/resources/database/resource_user_mysql.go index 8d5784150..883347533 100644 --- a/pkg/resources/database/resource_user_mysql.go +++ b/pkg/resources/database/resource_user_mysql.go @@ -233,7 +233,7 @@ func (data *MysqlUserResourceModel) UpdateResource(ctx context.Context, client * } func (data *MysqlUserResourceModel) WaitForService(ctx context.Context, client *exoscale.Client, diagnostics *diag.Diagnostics) { - _, err := waitForDBAASServiceReadyForUsers(ctx, client.GetDBAASServiceMysql, data.Service.ValueString(), func(t *exoscale.DBAASServiceMysql) bool { return len(t.Users) > 0 }) + _, err := waitForDBAASServiceReadyForUsers(ctx, client.GetDBAASServiceMysql, data.Service.ValueString(), func(t *exoscale.DBAASServiceMysql) bool { return t.State == exoscale.EnumServiceStateRunning }) if err != nil { diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Database service MySQL %s", err.Error())) } From 9ce0c14aa3b62f9334e80efe230082e514318186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9a=20Grondier?= Date: Tue, 24 Dec 2024 15:47:41 +0100 Subject: [PATCH 15/15] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54aebc07b..d6e13f189 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ FEATURES: - InstancePool: min-available support #406 +- dbaas: support for users #401 BUG FIXES: