From 9cc1728b76f9270554e4f4df67329056ccbbae6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9sz=C3=A1ros=20Gergely?= Date: Fri, 6 Oct 2023 13:31:31 +0200 Subject: [PATCH] CDPCP-9880 - GCP Datalake create/delete/read --- docs/resources/datalake_gcp_datalake.md | 154 +++++++++ provider/provider.go | 1 + resources/datalake/converter_gcp.go | 216 +++++++++++++ resources/datalake/model_gcp_datalake.go | 86 +++++ resources/datalake/resource_gcp_datalake.go | 174 ++++++++++ resources/datalake/schema_gcp_datalake.go | 340 ++++++++++++++++++++ 6 files changed, 971 insertions(+) create mode 100644 docs/resources/datalake_gcp_datalake.md create mode 100644 resources/datalake/converter_gcp.go create mode 100644 resources/datalake/model_gcp_datalake.go create mode 100644 resources/datalake/resource_gcp_datalake.go create mode 100644 resources/datalake/schema_gcp_datalake.go diff --git a/docs/resources/datalake_gcp_datalake.md b/docs/resources/datalake_gcp_datalake.md new file mode 100644 index 00000000..f0606172 --- /dev/null +++ b/docs/resources/datalake_gcp_datalake.md @@ -0,0 +1,154 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "cdp_datalake_gcp_datalake Resource - terraform-provider-cdp" +subcategory: "" +description: |- + A Data Lake is a service which provides a protective ring around the data stored in a cloud object store, including authentication, authorization, and governance support. +--- + +# cdp_datalake_gcp_datalake (Resource) + +A Data Lake is a service which provides a protective ring around the data stored in a cloud object store, including authentication, authorization, and governance support. + + + + +## Schema + +### Required + +- `datalake_name` (String) +- `environment_name` (String) +- `instance_profile` (String) +- `storage_location_base` (String) + +### Optional + +- `custom_instance_groups` (Attributes Set) (see [below for nested schema](#nestedatt--custom_instance_groups)) +- `enable_ranger_raz` (Boolean) +- `image` (Attributes) (see [below for nested schema](#nestedatt--image)) +- `java_version` (Number) +- `multi_az` (Boolean) +- `polling_options` (Attributes) Polling related configuration options that could specify various values that will be used during CDP resource creation. (see [below for nested schema](#nestedatt--polling_options)) +- `recipes` (Attributes Set) (see [below for nested schema](#nestedatt--recipes)) +- `runtime` (String) +- `scale` (String) +- `tags` (Map of String) + +### Read-Only + +- `certificate_expiration_state` (String) +- `cloudera_manager` (Attributes) (see [below for nested schema](#nestedatt--cloudera_manager)) +- `creation_date` (String) +- `crn` (String) +- `endpoints` (Attributes Set) (see [below for nested schema](#nestedatt--endpoints)) +- `environment_crn` (String) +- `id` (String) The ID of this resource. +- `instance_groups` (Attributes Set) (see [below for nested schema](#nestedatt--instance_groups)) +- `product_versions` (Attributes Set) (see [below for nested schema](#nestedatt--product_versions)) +- `status` (String) +- `status_reason` (String) + + +### Nested Schema for `custom_instance_groups` + +Required: + +- `name` (String) + +Optional: + +- `instance_type` (String) + + + +### Nested Schema for `image` + +Required: + +- `id` (String) + +Optional: + +- `catalog` (String) + + + +### Nested Schema for `polling_options` + +Optional: + +- `polling_timeout` (Number) Timeout value in minutes that specifies for how long should the polling go for resource creation/deletion. + + + +### Nested Schema for `recipes` + +Required: + +- `instance_group_name` (String) +- `recipe_names` (Attributes Set) (see [below for nested schema](#nestedatt--recipes--recipe_names)) + + +### Nested Schema for `recipes.recipe_names` + + + + +### Nested Schema for `cloudera_manager` + +Read-Only: + +- `cloudera_manager_repository_url` (String) +- `cloudera_manager_server_url` (String) +- `version` (String) + + + +### Nested Schema for `endpoints` + +Read-Only: + +- `display_name` (String) +- `knox_service` (String) +- `mode` (String) +- `open` (Boolean) +- `service_name` (String) +- `service_url` (String) + + + +### Nested Schema for `instance_groups` + +Read-Only: + +- `instances` (Attributes Set) (see [below for nested schema](#nestedatt--instance_groups--instances)) +- `name` (String) + + +### Nested Schema for `instance_groups.instances` + +Read-Only: + +- `discovery_fqdn` (String) +- `id` (String) +- `instance_group` (String) +- `instance_status` (String) +- `instance_type_val` (String) +- `private_ip` (String) +- `public_ip` (String) +- `ssh_port` (Number) +- `state` (String) +- `status_reason` (String) + + + + +### Nested Schema for `product_versions` + +Read-Only: + +- `name` (String) +- `version` (String) + + diff --git a/provider/provider.go b/provider/provider.go index 0b94a31c..0ca28f3c 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -229,6 +229,7 @@ func (p *CdpProvider) Resources(_ context.Context) []func() resource.Resource { environments.NewGcpCredentialResource, datalake.NewAwsDatalakeResource, datalake.NewAzureDatalakeResource, + datalake.NewGcpDatalakeResource, iam.NewGroupResource, datahub.NewAwsDatahubResource, datahub.NewAzureDatahubResource, diff --git a/resources/datalake/converter_gcp.go b/resources/datalake/converter_gcp.go new file mode 100644 index 00000000..b182d8b8 --- /dev/null +++ b/resources/datalake/converter_gcp.go @@ -0,0 +1,216 @@ +// Copyright 2023 Cloudera. All Rights Reserved. +// +// This file is licensed under the Apache License Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. +// +// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +// OF ANY KIND, either express or implied. Refer to the License for the specific +// permissions and limitations governing your use of the file. + +package datalake + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/types" + + datalakemodels "github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/datalake/models" + "github.com/cloudera/terraform-provider-cdp/utils" +) + +func datalakeDetailsToGcpDatalakeResourceModel(ctx context.Context, resp *datalakemodels.DatalakeDetails, model *gcpDatalakeResourceModel, pollingOptions *utils.PollingOptions, diags *diag.Diagnostics) { + model.ID = types.StringPointerValue(resp.Crn) + model.InstanceProfile = types.StringValue(resp.GcpConfiguration.ServiceAccountEmail) + if resp.ClouderaManager != nil { + var cmDiags diag.Diagnostics + model.ClouderaManager, cmDiags = types.ObjectValueFrom(ctx, map[string]attr.Type{ + "cloudera_manager_repository_url": types.StringType, + "cloudera_manager_server_url": types.StringType, + "version": types.StringType, + }, &clouderaManagerDetails{ + ClouderaManagerRepositoryURL: types.StringPointerValue(resp.ClouderaManager.ClouderaManagerRepositoryURL), + ClouderaManagerServerURL: types.StringValue(resp.ClouderaManager.ClouderaManagerServerURL), + Version: types.StringPointerValue(resp.ClouderaManager.Version), + }) + diags.Append(cmDiags...) + } + model.CreationDate = types.StringValue(resp.CreationDate.String()) + model.Crn = types.StringPointerValue(resp.Crn) + model.DatalakeName = types.StringPointerValue(resp.DatalakeName) + model.EnableRangerRaz = types.BoolValue(resp.EnableRangerRaz) + model.PollingOptions = pollingOptions + endpoints := make([]*endpoint, len(resp.Endpoints.Endpoints)) + for i, v := range resp.Endpoints.Endpoints { + endpoints[i] = &endpoint{ + DisplayName: types.StringPointerValue(v.DisplayName), + KnoxService: types.StringPointerValue(v.KnoxService), + Mode: types.StringPointerValue(v.Mode), + Open: types.BoolPointerValue(v.Open), + ServiceName: types.StringPointerValue(v.ServiceName), + ServiceURL: types.StringPointerValue(v.ServiceURL), + } + } + var epDiags diag.Diagnostics + model.Endpoints, epDiags = types.SetValueFrom(ctx, types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "display_name": types.StringType, + "knox_service": types.StringType, + "mode": types.StringType, + "open": types.BoolType, + "service_name": types.StringType, + "service_url": types.StringType, + }, + }, endpoints) + diags.Append(epDiags...) + model.EnvironmentCrn = types.StringValue(resp.EnvironmentCrn) + instanceGroups := make([]*instanceGroup, len(resp.InstanceGroups)) + for i, v := range resp.InstanceGroups { + instanceGroups[i] = &instanceGroup{ + Name: types.StringPointerValue(v.Name), + } + + instances := make([]*instance, len(v.Instances)) + for j, ins := range v.Instances { + instances[j] = &instance{ + DiscoveryFQDN: types.StringValue(ins.DiscoveryFQDN), + ID: types.StringPointerValue(ins.ID), + InstanceGroup: types.StringValue(ins.InstanceGroup), + InstanceStatus: types.StringValue(string(ins.InstanceStatus)), + InstanceTypeVal: types.StringValue(string(ins.InstanceTypeVal)), + PrivateIP: types.StringValue(ins.PrivateIP), + PublicIP: types.StringValue(ins.PublicIP), + SSHPort: types.Int64Value(int64(ins.SSHPort)), + State: types.StringPointerValue(ins.State), + StatusReason: types.StringValue(ins.StatusReason), + } + } + var instDiags diag.Diagnostics + instanceGroups[i].Instances, instDiags = types.SetValueFrom(ctx, types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "discovery_fqdn": types.StringType, + "id": types.StringType, + "instance_group": types.StringType, + "instance_status": types.StringType, + "instance_type_val": types.StringType, + "private_ip": types.StringType, + "public_ip": types.StringType, + "ssh_port": types.Int64Type, + "state": types.StringType, + "status_reason": types.StringType, + }, + }, instances) + diags.Append(instDiags...) + } + var igDiags diag.Diagnostics + model.InstanceGroups, igDiags = types.SetValueFrom(ctx, types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "instances": types.SetType{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "discovery_fqdn": types.StringType, + "id": types.StringType, + "instance_group": types.StringType, + "instance_status": types.StringType, + "instance_type_val": types.StringType, + "private_ip": types.StringType, + "public_ip": types.StringType, + "ssh_port": types.Int64Type, + "state": types.StringType, + "status_reason": types.StringType, + }, + }, + }, + "name": types.StringType, + }, + }, instanceGroups) + diags.Append(igDiags...) + productVersions := make([]*productVersion, len(resp.ProductVersions)) + for i, v := range resp.ProductVersions { + productVersions[i] = &productVersion{ + Name: types.StringPointerValue(v.Name), + Version: types.StringPointerValue(v.Version), + } + } + var pvDiags diag.Diagnostics + model.ProductVersions, pvDiags = types.SetValueFrom(ctx, types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "version": types.StringType, + }, + }, productVersions) + diags.Append(pvDiags...) + model.Scale = types.StringValue(string(resp.Shape)) + model.Status = types.StringValue(resp.Status) + model.StatusReason = types.StringValue(resp.StatusReason) + if model.CertificateExpirationState.IsUnknown() { + model.CertificateExpirationState = types.StringNull() + } +} + +func toGcpDatalakeRequest(ctx context.Context, model *gcpDatalakeResourceModel) *datalakemodels.CreateGCPDatalakeRequest { + req := &datalakemodels.CreateGCPDatalakeRequest{} + if model.CloudProviderConfiguration != nil { + req.CloudProviderConfiguration = &datalakemodels.GCPConfigurationRequest{ + ServiceAccountEmail: model.CloudProviderConfiguration.ServiceAccountEmail.ValueStringPointer(), + StorageLocation: model.CloudProviderConfiguration.StorageLocation.ValueStringPointer(), + } + } + req.CustomInstanceGroups = make([]*datalakemodels.SdxInstanceGroupRequest, len(model.CustomInstanceGroups)) + for i, v := range model.CustomInstanceGroups { + req.CustomInstanceGroups[i] = &datalakemodels.SdxInstanceGroupRequest{ + InstanceType: v.InstanceType.ValueString(), + Name: v.Name.ValueStringPointer(), + } + } + req.DatalakeName = model.DatalakeName.ValueStringPointer() + req.EnableRangerRaz = model.EnableRangerRaz.ValueBool() + req.EnvironmentName = model.EnvironmentName.ValueStringPointer() + if model.Image != nil { + req.Image = &datalakemodels.ImageRequest{ + CatalogName: model.Image.CatalogName.ValueStringPointer(), + ID: model.Image.ID.ValueStringPointer(), + } + } + req.JavaVersion = int32(model.JavaVersion.ValueInt64()) + req.Recipes = make([]*datalakemodels.InstanceGroupRecipeRequest, len(model.Recipes)) + for i, v := range model.Recipes { + req.Recipes[i] = &datalakemodels.InstanceGroupRecipeRequest{ + InstanceGroupName: v.InstanceGroupName.ValueStringPointer(), + RecipeNames: utils.FromSetValueToStringList(v.RecipeNames), + } + } + req.Runtime = model.Runtime.ValueString() + req.Scale = datalakemodels.DatalakeScaleType(model.Scale.ValueString()) + if !model.Tags.IsNull() { + req.Tags = make([]*datalakemodels.DatalakeResourceGCPTagRequest, len(model.Tags.Elements())) + i := 0 + for k, v := range model.Tags.Elements() { + val, diag := v.(basetypes.StringValuable).ToStringValue(ctx) + if !diag.HasError() { + req.Tags[i] = &datalakemodels.DatalakeResourceGCPTagRequest{ + Key: &k, + Value: val.ValueStringPointer(), + } + } + i++ + } + } + return req +} + +func toGcpDatalakeResourceModel(resp *datalakemodels.CreateGCPDatalakeResponse, model *gcpDatalakeResourceModel) { + model.ID = types.StringPointerValue(resp.Datalake.DatalakeName) + model.CertificateExpirationState = types.StringValue(resp.Datalake.CertificateExpirationState) + model.CreationDate = types.StringValue(resp.Datalake.CreationDate.String()) + model.Crn = types.StringPointerValue(resp.Datalake.Crn) + model.DatalakeName = types.StringPointerValue(resp.Datalake.DatalakeName) + model.EnableRangerRaz = types.BoolValue(resp.Datalake.EnableRangerRaz) + model.EnvironmentCrn = types.StringValue(resp.Datalake.EnvironmentCrn) + model.MultiAz = types.BoolValue(resp.Datalake.MultiAz) + model.Status = types.StringValue(resp.Datalake.Status) + model.StatusReason = types.StringValue(resp.Datalake.StatusReason) +} diff --git a/resources/datalake/model_gcp_datalake.go b/resources/datalake/model_gcp_datalake.go new file mode 100644 index 00000000..9527482f --- /dev/null +++ b/resources/datalake/model_gcp_datalake.go @@ -0,0 +1,86 @@ +// Copyright 2023 Cloudera. All Rights Reserved. +// +// This file is licensed under the Apache License Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. +// +// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +// OF ANY KIND, either express or implied. Refer to the License for the specific +// permissions and limitations governing your use of the file. + +package datalake + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/cloudera/terraform-provider-cdp/utils" +) + +type gcpDatalakeResourceModel struct { + ID types.String `tfsdk:"id"` + + PollingOptions *utils.PollingOptions `tfsdk:"polling_options"` + + InstanceProfile types.String `tfsdk:"instance_profile"` + + CloudProviderConfiguration *gcpConfiguration `tfsdk:"cloud_provider_configuration"` + + CertificateExpirationState types.String `tfsdk:"certificate_expiration_state"` + + ClouderaManager types.Object `tfsdk:"cloudera_manager"` + + CreationDate types.String `tfsdk:"creation_date"` + + Crn types.String `tfsdk:"crn"` + + CustomInstanceGroups []*gcpDatalakeInstanceGroup `tfsdk:"custom_instance_groups"` + + DatalakeName types.String `tfsdk:"datalake_name"` + + EnableRangerRaz types.Bool `tfsdk:"enable_ranger_raz"` + + Endpoints types.Set `tfsdk:"endpoints"` + + EnvironmentCrn types.String `tfsdk:"environment_crn"` + + EnvironmentName types.String `tfsdk:"environment_name"` + + Image *gcpDatalakeImage `tfsdk:"image"` + + InstanceGroups types.Set `tfsdk:"instance_groups"` + + ProductVersions types.Set `tfsdk:"product_versions"` + + JavaVersion types.Int64 `tfsdk:"java_version"` + + MultiAz types.Bool `tfsdk:"multi_az"` + + Recipes []*instanceGroupRecipe `tfsdk:"recipes"` + + Runtime types.String `tfsdk:"runtime"` + + Scale types.String `tfsdk:"scale"` + + Status types.String `tfsdk:"status"` + + StatusReason types.String `tfsdk:"status_reason"` + + Tags types.Map `tfsdk:"tags"` +} + +type gcpConfiguration struct { + ServiceAccountEmail types.String `tfsdk:"service_account_email"` + StorageLocation types.String `tfsdk:"storage_location"` +} + +type gcpDatalakeInstanceGroup struct { + InstanceType types.String `tfsdk:"instance_type"` + + Name types.String `tfsdk:"name"` +} + +type gcpDatalakeImage struct { + CatalogName types.String `tfsdk:"catalog_name"` + + ID types.String `tfsdk:"id"` +} diff --git a/resources/datalake/resource_gcp_datalake.go b/resources/datalake/resource_gcp_datalake.go new file mode 100644 index 00000000..a982453a --- /dev/null +++ b/resources/datalake/resource_gcp_datalake.go @@ -0,0 +1,174 @@ +// Copyright 2023 Cloudera. All Rights Reserved. +// +// This file is licensed under the Apache License Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. +// +// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +// OF ANY KIND, either express or implied. Refer to the License for the specific +// permissions and limitations governing your use of the file. + +package datalake + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/cdp" + "github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/datalake/client/operations" + datalakemodels "github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/datalake/models" + "github.com/cloudera/terraform-provider-cdp/utils" +) + +var ( + _ resource.Resource = &gcpDatalakeResource{} +) + +type gcpDatalakeResource struct { + client *cdp.Client +} + +func NewGcpDatalakeResource() resource.Resource { + return &gcpDatalakeResource{} +} + +func (r *gcpDatalakeResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_datalake_gcp_datalake" +} + +func (r *gcpDatalakeResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = gcpDatalakeResourceSchema +} + +func (r *gcpDatalakeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + r.client = utils.GetCdpClientForResource(req, resp) +} + +func (r *gcpDatalakeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var state gcpDatalakeResourceModel + diags := req.Plan.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + tflog.Error(ctx, "Got error while trying to set plan") + return + } + + client := r.client.Datalake + + params := operations.NewCreateGCPDatalakeParamsWithContext(ctx) + params.WithInput(toGcpDatalakeRequest(ctx, &state)) + responseOk, err := client.Operations.CreateGCPDatalake(params) + if err != nil { + utils.AddDatalakeDiagnosticsError(err, &resp.Diagnostics, "create GCP Datalake") + return + } + + datalakeResp := responseOk.Payload + toGcpDatalakeResourceModel(datalakeResp, &state) + + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + if err := waitForDatalakeToBeRunning(ctx, state.DatalakeName.ValueString(), time.Hour, r.client.Datalake, state.PollingOptions); err != nil { + utils.AddDatalakeDiagnosticsError(err, &resp.Diagnostics, "create GCP Datalake") + return + } + + descParams := operations.NewDescribeDatalakeParamsWithContext(ctx) + descParams.WithInput(&datalakemodels.DescribeDatalakeRequest{DatalakeName: state.DatalakeName.ValueStringPointer()}) + descResponseOk, err := client.Operations.DescribeDatalake(descParams) + if err != nil { + utils.AddDatalakeDiagnosticsError(err, &resp.Diagnostics, "create GCP Datalake") + return + } + + descDlResp := descResponseOk.Payload + datalakeDetailsToGcpDatalakeResourceModel(ctx, descDlResp.Datalake, &state, state.PollingOptions, &resp.Diagnostics) + + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *gcpDatalakeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state gcpDatalakeResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + client := r.client.Datalake + + params := operations.NewDescribeDatalakeParamsWithContext(ctx) + params.WithInput(&datalakemodels.DescribeDatalakeRequest{DatalakeName: state.DatalakeName.ValueStringPointer()}) + responseOk, err := client.Operations.DescribeDatalake(params) + if err != nil { + if dlErr, ok := err.(*operations.DescribeDatalakeDefault); ok { + if cdp.IsDatalakeError(dlErr.GetPayload(), "NOT_FOUND", "") { + resp.Diagnostics.AddWarning("Resource not found on provider", "Data lake not found, removing from state.") + tflog.Warn(ctx, "Data lake not found, removing from state", map[string]interface{}{ + "id": state.ID.ValueString(), + }) + resp.State.RemoveResource(ctx) + return + } + } + utils.AddDatalakeDiagnosticsError(err, &resp.Diagnostics, "read GCP Datalake") + return + } + + datalakeResp := responseOk.Payload + datalakeDetailsToGcpDatalakeResourceModel(ctx, datalakeResp.Datalake, &state, state.PollingOptions, &resp.Diagnostics) + + diags = resp.State.Set(ctx, state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *gcpDatalakeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { +} + +func (r *gcpDatalakeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state gcpDatalakeResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + client := r.client.Datalake + params := operations.NewDeleteDatalakeParamsWithContext(ctx) + params.WithInput(&datalakemodels.DeleteDatalakeRequest{ + DatalakeName: state.DatalakeName.ValueStringPointer(), + Force: false, + }) + _, err := client.Operations.DeleteDatalake(params) + if err != nil { + if dlErr, ok := err.(*operations.DescribeDatalakeDefault); ok { + if cdp.IsDatalakeError(dlErr.GetPayload(), "NOT_FOUND", "") { + tflog.Info(ctx, "Data lake already deleted", map[string]interface{}{ + "id": state.ID.ValueString(), + }) + return + } + } + utils.AddDatalakeDiagnosticsError(err, &resp.Diagnostics, "delete GCP Datalake") + return + } + + if err := waitForDatalakeToBeDeleted(ctx, state.DatalakeName.ValueString(), time.Hour, r.client.Datalake, state.PollingOptions); err != nil { + utils.AddDatalakeDiagnosticsError(err, &resp.Diagnostics, "delete GCP Datalake") + return + } +} diff --git a/resources/datalake/schema_gcp_datalake.go b/resources/datalake/schema_gcp_datalake.go new file mode 100644 index 00000000..c86f540f --- /dev/null +++ b/resources/datalake/schema_gcp_datalake.go @@ -0,0 +1,340 @@ +// Copyright 2023 Cloudera. All Rights Reserved. +// +// This file is licensed under the Apache License Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. +// +// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +// OF ANY KIND, either express or implied. Refer to the License for the specific +// permissions and limitations governing your use of the file. + +package datalake + +import ( + "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/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var gcpDatalakeResourceSchema = schema.Schema{ + MarkdownDescription: "A Data Lake is a service which provides a protective ring around the data stored in a cloud object store, including authentication, authorization, and governance support.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "polling_options": schema.SingleNestedAttribute{ + MarkdownDescription: "Polling related configuration options that could specify various values that will be used during CDP resource creation.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "polling_timeout": schema.Int64Attribute{ + MarkdownDescription: "Timeout value in minutes that specifies for how long should the polling go for resource creation/deletion.", + Default: int64default.StaticInt64(60), + Computed: true, + Optional: true, + }, + }, + }, + "instance_profile": schema.StringAttribute{ + Required: true, + }, + "storage_location_base": schema.StringAttribute{ + Required: true, + }, + "certificate_expiration_state": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "cloudera_manager": schema.SingleNestedAttribute{ + Computed: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + Attributes: map[string]schema.Attribute{ + "cloudera_manager_repository_url": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "cloudera_manager_server_url": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "version": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + }, + "creation_date": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "crn": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "custom_instance_groups": schema.SetNestedAttribute{ + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "instance_type": schema.StringAttribute{ + Optional: true, + }, + "name": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + "datalake_name": schema.StringAttribute{ + Required: true, + }, + "enable_ranger_raz": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + "environment_crn": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "endpoints": schema.SetNestedAttribute{ + Computed: true, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "display_name": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "knox_service": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "mode": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "open": schema.BoolAttribute{ + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + "service_name": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "service_url": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + }, + }, + "environment_name": schema.StringAttribute{ + Required: true, + }, + "image": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "catalog": schema.StringAttribute{ + Optional: true, + }, + "id": schema.StringAttribute{ + Required: true, + }, + }, + }, + "instance_groups": schema.SetNestedAttribute{ + Computed: true, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "instances": schema.SetNestedAttribute{ + Computed: true, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "discovery_fqdn": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "instance_group": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "instance_status": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "instance_type_val": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "private_ip": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "public_ip": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "ssh_port": schema.Int64Attribute{ + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + "state": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "status_reason": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + }, + }, + "name": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + }, + }, + "java_version": schema.Int64Attribute{ + Optional: true, + }, + "multi_az": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + "product_versions": schema.SetNestedAttribute{ + Computed: true, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "version": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + }, + }, + "recipes": schema.SetNestedAttribute{ + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "instance_group_name": schema.StringAttribute{ + Required: true, + }, + "recipe_names": schema.SetNestedAttribute{ + Required: true, + }, + }, + }, + }, + "runtime": schema.StringAttribute{ + Optional: true, + }, + "scale": schema.StringAttribute{ + Computed: true, + Optional: true, + }, + "status": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "status_reason": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "tags": schema.MapAttribute{ + Optional: true, + ElementType: types.StringType, + }, + }, +}