From 53dbcbe91c5b5cb2a53efcf03fe97f8b561c6035 Mon Sep 17 00:00:00 2001 From: Naeel Date: Sat, 18 Jul 2026 08:44:07 +0300 Subject: [PATCH] =?UTF-8?q?chore:=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=2073=20=D1=81=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20Go-=D1=84=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB=D0=B0=20=D0=B8=D0=B7=20git?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources_gen/100_openwhisk_resource.go | 382 ---------- .../109_zones_v2_record_resource.go | 362 --------- .../resources_gen/109_zones_v2_resource.go | 484 ------------ .../resources_gen/110_dnszone_resource.go | 495 ------------- .../resources_gen/111_dnsrecord_resource.go | 546 -------------- .../resources_gen/112_tenant_resource.go | 372 ---------- .../resources_gen/113_vc_complex_resource.go | 563 -------------- .../115_mariadb_database_resource.go | 343 --------- .../resources_gen/115_mariadb_resource.go | 447 ----------- .../115_mariadb_user_resource.go | 367 --------- .../resources_gen/116_kafka_resource.go | 389 ---------- .../resources_gen/116_kafka_topic_resource.go | 364 --------- .../resources_gen/116_kafka_user_resource.go | 373 ---------- .../resources_gen/119_akhq_resource.go | 554 -------------- .../120_clickhouse_database_resource.go | 343 --------- .../resources_gen/120_clickhouse_resource.go | 487 ------------ .../120_clickhouse_user_resource.go | 359 --------- .../internal/resources_gen/12_s3_resource.go | 421 ----------- .../resources_gen/12_s3_sub_user_resource.go | 358 --------- .../resources_gen/13_s3bucket_resource.go | 478 ------------ .../148_vc_mgmt_sthutrval_cluster_resource.go | 419 ----------- .../resources_gen/149_valo_tenant_resource.go | 431 ----------- .../150_k8s_sthutrval_cluster_resource.go | 429 ----------- ...150_k8s_sthutrval_cluster_user_resource.go | 343 --------- .../151_k8s_openbao_kv_resource.go | 343 --------- .../151_k8s_openbao_policy_resource.go | 355 --------- .../resources_gen/151_k8s_openbao_resource.go | 468 ------------ .../151_k8s_openbao_secrets_resource.go | 368 --------- .../resources_gen/153_nifi_resource.go | 474 ------------ .../resources_gen/163_llm_ai_resource.go | 345 --------- .../163_llm_ai_secrets_resource.go | 401 ---------- .../resources_gen/163_llm_ai_team_resource.go | 365 --------- .../resources_gen/175_k8s_go_resource.go | 422 ----------- .../resources_gen/19_vc_org_resource.go | 391 ---------- .../resources_gen/19_vc_org_user_resource.go | 343 --------- .../resources_gen/1_dummy_resource.go | 582 --------------- .../resources_gen/20_vc_org_saas_resource.go | 348 --------- .../resources_gen/21_vc_vdc_resource.go | 573 -------------- .../resources_gen/22_vc_nsxt_resource.go | 614 --------------- .../resources_gen/23_vc_vm_resource.go | 581 --------------- .../resources_gen/24_vc_nat_resource.go | 322 -------- .../resources_gen/25_vcexternalip_resource.go | 604 --------------- .../resources_gen/26_vapp_resource.go | 571 -------------- .../resources_gen/27_vc_vm_v2_resource.go | 350 --------- .../resources_gen/28_vc_vm_v3_resource.go | 697 ------------------ .../resources_gen/29_vc_vdc_group_resource.go | 449 ----------- .../29_vc_vdc_group_vdc_resource.go | 326 -------- .../resources_gen/2_template_resource.go | 397 ---------- .../resources_gen/32_vmpostgre_resource.go | 514 ------------- .../50_nextcloud_backup_resource.go | 344 --------- .../resources_gen/50_nextcloud_resource.go | 414 ----------- .../resources_gen/81_superset_resource.go | 447 ----------- .../resources_gen/82_harbor_resource.go | 482 ------------ .../resources_gen/86_k8s_velero_resource.go | 401 ---------- .../resources_gen/87_k8svalkey_resource.go | 423 ----------- .../87_k8svalkey_user_resource.go | 358 --------- .../88_k8s_ziti_controller_resource.go | 431 ----------- .../resources_gen/89_flask_resource.go | 449 ----------- .../90_postgres_backup_resource.go | 326 -------- .../90_postgres_database_resource.go | 358 --------- .../resources_gen/90_postgres_resource.go | 499 ------------- .../90_postgres_user_resource.go | 367 --------- .../resources_gen/91_redis_resource.go | 393 ---------- .../resources_gen/92_mongodb_resource.go | 389 ---------- .../resources_gen/92_mongodb_user_resource.go | 364 --------- .../resources_gen/93_rabbitmq_resource.go | 474 ------------ .../resources_gen/94_lucee_resource.go | 449 ----------- .../resources_gen/95_nodejs_resource.go | 449 ----------- .../resources_gen/96_pgadmin_resource.go | 515 ------------- .../resources_gen/97_nodered_resource.go | 431 ----------- .../resources_gen/98_http_resource.go | 509 ------------- .../resources_gen/99_gitea_resource.go | 579 --------------- provider/internal/resources_gen/registry.go | 81 -- 73 files changed, 31244 deletions(-) delete mode 100644 provider/internal/resources_gen/100_openwhisk_resource.go delete mode 100644 provider/internal/resources_gen/109_zones_v2_record_resource.go delete mode 100644 provider/internal/resources_gen/109_zones_v2_resource.go delete mode 100644 provider/internal/resources_gen/110_dnszone_resource.go delete mode 100644 provider/internal/resources_gen/111_dnsrecord_resource.go delete mode 100644 provider/internal/resources_gen/112_tenant_resource.go delete mode 100644 provider/internal/resources_gen/113_vc_complex_resource.go delete mode 100644 provider/internal/resources_gen/115_mariadb_database_resource.go delete mode 100644 provider/internal/resources_gen/115_mariadb_resource.go delete mode 100644 provider/internal/resources_gen/115_mariadb_user_resource.go delete mode 100644 provider/internal/resources_gen/116_kafka_resource.go delete mode 100644 provider/internal/resources_gen/116_kafka_topic_resource.go delete mode 100644 provider/internal/resources_gen/116_kafka_user_resource.go delete mode 100644 provider/internal/resources_gen/119_akhq_resource.go delete mode 100644 provider/internal/resources_gen/120_clickhouse_database_resource.go delete mode 100644 provider/internal/resources_gen/120_clickhouse_resource.go delete mode 100644 provider/internal/resources_gen/120_clickhouse_user_resource.go delete mode 100644 provider/internal/resources_gen/12_s3_resource.go delete mode 100644 provider/internal/resources_gen/12_s3_sub_user_resource.go delete mode 100644 provider/internal/resources_gen/13_s3bucket_resource.go delete mode 100644 provider/internal/resources_gen/148_vc_mgmt_sthutrval_cluster_resource.go delete mode 100644 provider/internal/resources_gen/149_valo_tenant_resource.go delete mode 100644 provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go delete mode 100644 provider/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go delete mode 100644 provider/internal/resources_gen/151_k8s_openbao_kv_resource.go delete mode 100644 provider/internal/resources_gen/151_k8s_openbao_policy_resource.go delete mode 100644 provider/internal/resources_gen/151_k8s_openbao_resource.go delete mode 100644 provider/internal/resources_gen/151_k8s_openbao_secrets_resource.go delete mode 100644 provider/internal/resources_gen/153_nifi_resource.go delete mode 100644 provider/internal/resources_gen/163_llm_ai_resource.go delete mode 100644 provider/internal/resources_gen/163_llm_ai_secrets_resource.go delete mode 100644 provider/internal/resources_gen/163_llm_ai_team_resource.go delete mode 100644 provider/internal/resources_gen/175_k8s_go_resource.go delete mode 100644 provider/internal/resources_gen/19_vc_org_resource.go delete mode 100644 provider/internal/resources_gen/19_vc_org_user_resource.go delete mode 100644 provider/internal/resources_gen/1_dummy_resource.go delete mode 100644 provider/internal/resources_gen/20_vc_org_saas_resource.go delete mode 100644 provider/internal/resources_gen/21_vc_vdc_resource.go delete mode 100644 provider/internal/resources_gen/22_vc_nsxt_resource.go delete mode 100644 provider/internal/resources_gen/23_vc_vm_resource.go delete mode 100644 provider/internal/resources_gen/24_vc_nat_resource.go delete mode 100644 provider/internal/resources_gen/25_vcexternalip_resource.go delete mode 100644 provider/internal/resources_gen/26_vapp_resource.go delete mode 100644 provider/internal/resources_gen/27_vc_vm_v2_resource.go delete mode 100644 provider/internal/resources_gen/28_vc_vm_v3_resource.go delete mode 100644 provider/internal/resources_gen/29_vc_vdc_group_resource.go delete mode 100644 provider/internal/resources_gen/29_vc_vdc_group_vdc_resource.go delete mode 100644 provider/internal/resources_gen/2_template_resource.go delete mode 100644 provider/internal/resources_gen/32_vmpostgre_resource.go delete mode 100644 provider/internal/resources_gen/50_nextcloud_backup_resource.go delete mode 100644 provider/internal/resources_gen/50_nextcloud_resource.go delete mode 100644 provider/internal/resources_gen/81_superset_resource.go delete mode 100644 provider/internal/resources_gen/82_harbor_resource.go delete mode 100644 provider/internal/resources_gen/86_k8s_velero_resource.go delete mode 100644 provider/internal/resources_gen/87_k8svalkey_resource.go delete mode 100644 provider/internal/resources_gen/87_k8svalkey_user_resource.go delete mode 100644 provider/internal/resources_gen/88_k8s_ziti_controller_resource.go delete mode 100644 provider/internal/resources_gen/89_flask_resource.go delete mode 100644 provider/internal/resources_gen/90_postgres_backup_resource.go delete mode 100644 provider/internal/resources_gen/90_postgres_database_resource.go delete mode 100644 provider/internal/resources_gen/90_postgres_resource.go delete mode 100644 provider/internal/resources_gen/90_postgres_user_resource.go delete mode 100644 provider/internal/resources_gen/91_redis_resource.go delete mode 100644 provider/internal/resources_gen/92_mongodb_resource.go delete mode 100644 provider/internal/resources_gen/92_mongodb_user_resource.go delete mode 100644 provider/internal/resources_gen/93_rabbitmq_resource.go delete mode 100644 provider/internal/resources_gen/94_lucee_resource.go delete mode 100644 provider/internal/resources_gen/95_nodejs_resource.go delete mode 100644 provider/internal/resources_gen/96_pgadmin_resource.go delete mode 100644 provider/internal/resources_gen/97_nodered_resource.go delete mode 100644 provider/internal/resources_gen/98_http_resource.go delete mode 100644 provider/internal/resources_gen/99_gitea_resource.go delete mode 100644 provider/internal/resources_gen/registry.go diff --git a/provider/internal/resources_gen/100_openwhisk_resource.go b/provider/internal/resources_gen/100_openwhisk_resource.go deleted file mode 100644 index c4cbf39..0000000 --- a/provider/internal/resources_gen/100_openwhisk_resource.go +++ /dev/null @@ -1,382 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: openwhisk -// Service ID: 100 - -var _ resource.Resource = &OpenwhiskResource{} -var _ resource.ResourceWithModifyPlan = &OpenwhiskResource{} -var _ resource.ResourceWithImportState = &OpenwhiskResource{} - -type OpenwhiskResource struct { - client *core.UniversalClient -} - -type OpenwhiskModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - Username types.String `tfsdk:"username"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewOpenwhiskResource() resource.Resource { - return &OpenwhiskResource{} -} - -func (r *OpenwhiskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_openwhisk" -} - -func (r *OpenwhiskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Имя пользователя Openwhisk Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *OpenwhiskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *OpenwhiskModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Username.IsNull() && !plan.Username.IsUnknown() && !state.Username.IsNull() && !state.Username.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Username.ValueString(), state.Username.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить username", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - if config.Username.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - if config.Username.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 64: resources_core.FormatString(config.Username), - 281: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 100, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *OpenwhiskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if data.Username.IsNull() || data.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 64: resources_core.FormatString(data.Username), - 281: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 100, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "username", Field: "Username", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *OpenwhiskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 100, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "username", Field: "Username", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *OpenwhiskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan OpenwhiskModel - var state OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 100, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "username", Field: "Username", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *OpenwhiskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *OpenwhiskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *OpenwhiskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/109_zones_v2_record_resource.go b/provider/internal/resources_gen/109_zones_v2_record_resource.go deleted file mode 100644 index 9cc2073..0000000 --- a/provider/internal/resources_gen/109_zones_v2_record_resource.go +++ /dev/null @@ -1,362 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: zones_v2 -// Subresource: record - -var _ resource.Resource = &ZonesV2RecordResource{} - -// ZonesV2RecordResource manages a subresource via operations. -type ZonesV2RecordResource struct { - client *core.UniversalClient -} - -type ZonesV2RecordModel struct { - ID types.String `tfsdk:"id"` - ZonesV2ID types.String `tfsdk:"zones_v2_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - RecordInput types.String `tfsdk:"record_input"` - RecordName types.String `tfsdk:"record_name"` - RecordTTL types.Int64 `tfsdk:"record_t_t_l"` - RecordType types.String `tfsdk:"record_type"` -} - -func NewZonesV2RecordResource() resource.Resource { - return &ZonesV2RecordResource{} -} - -func (r *ZonesV2RecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_zones_v2_record" -} - -func (r *ZonesV2RecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "zones_v2_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "record_input": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции"}, - "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны в конце проставляется автоматически Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена"}, - "record_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("A")}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ZonesV2RecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ZonesV2RecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.RecordName.IsNull() || plan.RecordName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.ZonesV2ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - } - identityCodes := []string{ - "recordName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("record", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - "recordTTL": resources_core.FormatInt64(plan.RecordTTL), - "recordType": resources_core.FormatString(plan.RecordType), - "recordInput": resources_core.FormatString(plan.RecordInput), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_record", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_record", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ZonesV2RecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2RecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ZonesV2RecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_record" == "" { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ZonesV2ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - "recordTTL": resources_core.FormatInt64(plan.RecordTTL), - "recordType": resources_core.FormatString(plan.RecordType), - "recordInput": resources_core.FormatString(plan.RecordInput), - }) - - idParams := map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_record", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ZonesV2RecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_record" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ZonesV2ID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "recordName": resources_core.FormatString(state.RecordName), - } - identityCodes := []string{ - "recordName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("record", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(state.RecordName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_record", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ZonesV2RecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/109_zones_v2_resource.go b/provider/internal/resources_gen/109_zones_v2_resource.go deleted file mode 100644 index f07aab8..0000000 --- a/provider/internal/resources_gen/109_zones_v2_resource.go +++ /dev/null @@ -1,484 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: zones_v2 -// Service ID: 109 - -var _ resource.Resource = &ZonesV2Resource{} -var _ resource.ResourceWithModifyPlan = &ZonesV2Resource{} -var _ resource.ResourceWithImportState = &ZonesV2Resource{} - -type ZonesV2Resource struct { - client *core.UniversalClient -} - -type ZonesV2Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Records types.String `tfsdk:"records"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ZoneExpire types.Int64 `tfsdk:"zone_expire"` - ZoneKind types.String `tfsdk:"zone_kind"` - ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` - ZoneName types.String `tfsdk:"zone_name"` - ZoneRefresh types.Int64 `tfsdk:"zone_refresh"` - ZoneRetry types.Int64 `tfsdk:"zone_retry"` - ZoneTTL types.Int64 `tfsdk:"zone_t_t_l"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewZonesV2Resource() resource.Resource { - return &ZonesV2Resource{} -} - -func (r *ZonesV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_zones_v2" -} - -func (r *ZonesV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "records": schema.StringAttribute{Required: true}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("powerdns"), MarkdownDescription: "Не изменяется после создания."}, - "zone_expire": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600000), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, - "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, - "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, - "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, - "zone_refresh": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, - "zone_retry": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7200), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, - "zone_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ZonesV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ZonesV2Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneExpire.IsNull() && !plan.ZoneExpire.IsUnknown() && !state.ZoneExpire.IsNull() && !state.ZoneExpire.IsUnknown() { - if plan.ZoneExpire.ValueInt64() != state.ZoneExpire.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_expire", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneKind.ValueString(), state.ZoneKind.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneMastersForSlave.ValueString(), state.ZoneMastersForSlave.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneName.ValueString(), state.ZoneName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneRefresh.IsNull() && !plan.ZoneRefresh.IsUnknown() && !state.ZoneRefresh.IsNull() && !state.ZoneRefresh.IsUnknown() { - if plan.ZoneRefresh.ValueInt64() != state.ZoneRefresh.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_refresh", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneRetry.IsNull() && !plan.ZoneRetry.IsUnknown() && !state.ZoneRetry.IsNull() && !state.ZoneRetry.IsUnknown() { - if plan.ZoneRetry.ValueInt64() != state.ZoneRetry.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_retry", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTTL.IsNull() && !plan.ZoneTTL.IsUnknown() && !state.ZoneTTL.IsNull() && !state.ZoneTTL.IsUnknown() { - if plan.ZoneTTL.ValueInt64() != state.ZoneTTL.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_t_t_l", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Records.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "records is required.") - return - } - if config.Records.IsUnknown() { - return - } - if config.ZoneName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - if config.ZoneName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 761: resources_core.FormatString(config.ZoneName), - 762: resources_core.FormatString(config.ZoneKind), - 763: resources_core.FormatString(config.ZoneMastersForSlave), - 764: resources_core.FormatInt64(config.ZoneRefresh), - 765: resources_core.FormatInt64(config.ZoneRetry), - 766: resources_core.FormatInt64(config.ZoneExpire), - 767: resources_core.FormatInt64(config.ZoneTTL), - 768: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 109, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ZonesV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Records.IsNull() || data.Records.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "records is required.") - return - } - if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 761: resources_core.FormatString(data.ZoneName), - 762: resources_core.FormatString(data.ZoneKind), - 763: resources_core.FormatString(data.ZoneMastersForSlave), - 764: resources_core.FormatInt64(data.ZoneRefresh), - 765: resources_core.FormatInt64(data.ZoneRetry), - 766: resources_core.FormatInt64(data.ZoneExpire), - 767: resources_core.FormatInt64(data.ZoneTTL), - 768: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 109, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 109, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ZonesV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ZonesV2Model - var state ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.Records.IsNull() != state.Records.IsNull() || plan.Records.IsUnknown() != state.Records.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Records.IsNull() && !plan.Records.IsUnknown() && !state.Records.IsNull() && !state.Records.IsUnknown() { - if !strings.EqualFold(plan.Records.ValueString(), state.Records.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 769: resources_core.FormatString(plan.Records), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 109, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ZonesV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ZonesV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/110_dnszone_resource.go b/provider/internal/resources_gen/110_dnszone_resource.go deleted file mode 100644 index fab197b..0000000 --- a/provider/internal/resources_gen/110_dnszone_resource.go +++ /dev/null @@ -1,495 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dnszone -// Service ID: 110 - -var _ resource.Resource = &DnszoneResource{} -var _ resource.ResourceWithModifyPlan = &DnszoneResource{} -var _ resource.ResourceWithImportState = &DnszoneResource{} - -type DnszoneResource struct { - client *core.UniversalClient -} - -type DnszoneModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ZoneEmail types.String `tfsdk:"zone_email"` - ZoneKind types.String `tfsdk:"zone_kind"` - ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` - ZoneName types.String `tfsdk:"zone_name"` - ZoneTimeToExpire types.String `tfsdk:"zone_time_to_expire"` - ZoneTimeToLive types.String `tfsdk:"zone_time_to_live"` - ZoneTimeToRefresh types.String `tfsdk:"zone_time_to_refresh"` - ZoneTimeToRetry types.String `tfsdk:"zone_time_to_retry"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDnszoneResource() resource.Resource { - return &DnszoneResource{} -} - -func (r *DnszoneResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dnszone" -} - -func (r *DnszoneResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("powerdns"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "zone_email": schema.StringAttribute{Required: true, MarkdownDescription: "Контактный e-mail Не изменяется после создания."}, - "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, - "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, - "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, - "zone_time_to_expire": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3600000"), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, - "zone_time_to_live": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, - "zone_time_to_refresh": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, - "zone_time_to_retry": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("7200"), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DnszoneResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DnszoneModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneEmail.IsNull() && !plan.ZoneEmail.IsUnknown() && !state.ZoneEmail.IsNull() && !state.ZoneEmail.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneEmail.ValueString(), state.ZoneEmail.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_email", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneKind.ValueString(), state.ZoneKind.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneMastersForSlave.ValueString(), state.ZoneMastersForSlave.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneName.ValueString(), state.ZoneName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToExpire.IsNull() && !plan.ZoneTimeToExpire.IsUnknown() && !state.ZoneTimeToExpire.IsNull() && !state.ZoneTimeToExpire.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToExpire.ValueString(), state.ZoneTimeToExpire.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_expire", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToLive.IsNull() && !plan.ZoneTimeToLive.IsUnknown() && !state.ZoneTimeToLive.IsNull() && !state.ZoneTimeToLive.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToLive.ValueString(), state.ZoneTimeToLive.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_live", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToRefresh.IsNull() && !plan.ZoneTimeToRefresh.IsUnknown() && !state.ZoneTimeToRefresh.IsNull() && !state.ZoneTimeToRefresh.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToRefresh.ValueString(), state.ZoneTimeToRefresh.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_refresh", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToRetry.IsNull() && !plan.ZoneTimeToRetry.IsUnknown() && !state.ZoneTimeToRetry.IsNull() && !state.ZoneTimeToRetry.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToRetry.ValueString(), state.ZoneTimeToRetry.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_retry", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ZoneEmail.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") - return - } - if config.ZoneEmail.IsUnknown() { - return - } - if config.ZoneName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - if config.ZoneName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 66: resources_core.FormatString(config.ZoneEmail), - 67: resources_core.FormatString(config.ZoneName), - 68: resources_core.FormatString(config.ZoneKind), - 69: resources_core.FormatString(config.ZoneMastersForSlave), - 70: resources_core.FormatString(config.ZoneTimeToRefresh), - 71: resources_core.FormatString(config.ZoneTimeToRetry), - 72: resources_core.FormatString(config.ZoneTimeToExpire), - 73: resources_core.FormatString(config.ZoneTimeToLive), - 284: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 110, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DnszoneResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ZoneEmail.IsNull() || data.ZoneEmail.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") - return - } - if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 66: resources_core.FormatString(data.ZoneEmail), - 67: resources_core.FormatString(data.ZoneName), - 68: resources_core.FormatString(data.ZoneKind), - 69: resources_core.FormatString(data.ZoneMastersForSlave), - 70: resources_core.FormatString(data.ZoneTimeToRefresh), - 71: resources_core.FormatString(data.ZoneTimeToRetry), - 72: resources_core.FormatString(data.ZoneTimeToExpire), - 73: resources_core.FormatString(data.ZoneTimeToLive), - 284: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 110, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnszoneResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 110, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DnszoneResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DnszoneModel - var state DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 110, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnszoneResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DnszoneResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DnszoneResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/111_dnsrecord_resource.go b/provider/internal/resources_gen/111_dnsrecord_resource.go deleted file mode 100644 index b28d2d8..0000000 --- a/provider/internal/resources_gen/111_dnsrecord_resource.go +++ /dev/null @@ -1,546 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dnsrecord -// Service ID: 111 - -var _ resource.Resource = &DnsrecordResource{} -var _ resource.ResourceWithModifyPlan = &DnsrecordResource{} -var _ resource.ResourceWithImportState = &DnsrecordResource{} - -type DnsrecordResource struct { - client *core.UniversalClient -} - -type DnsrecordModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - RecordInput types.String `tfsdk:"record_input"` - RecordName types.String `tfsdk:"record_name"` - RecordTTL types.Int64 `tfsdk:"record_t_t_l"` - RecordType types.String `tfsdk:"record_type"` - ZoneUid types.String `tfsdk:"zone_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDnsrecordResource() resource.Resource { - return &DnsrecordResource{} -} - -func (r *DnsrecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dnsrecord" -} - -func (r *DnsrecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "record_input": schema.StringAttribute{Required: true, MarkdownDescription: "Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP"}, - "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя записи. Имя зоны в конце проставляется автоматически Не изменяется после создания."}, - "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена Не изменяется после создания."}, - "record_type": schema.StringAttribute{Required: true, MarkdownDescription: "Тип DNS-записи Не изменяется после создания."}, - "zone_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID экземпляра услуги DNS-зоны Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DnsrecordResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DnsrecordModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.RecordName.IsNull() && !plan.RecordName.IsUnknown() && !state.RecordName.IsNull() && !state.RecordName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RecordName.ValueString(), state.RecordName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить record_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RecordTTL.IsNull() && !plan.RecordTTL.IsUnknown() && !state.RecordTTL.IsNull() && !state.RecordTTL.IsUnknown() { - if plan.RecordTTL.ValueInt64() != state.RecordTTL.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить record_t_t_l", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RecordType.IsNull() && !plan.RecordType.IsUnknown() && !state.RecordType.IsNull() && !state.RecordType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RecordType.ValueString(), state.RecordType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить record_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneUid.ValueString(), state.ZoneUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.RecordInput.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if config.RecordInput.IsUnknown() { - return - } - if config.RecordName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - if config.RecordName.IsUnknown() { - return - } - if config.RecordType.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") - return - } - if config.RecordType.IsUnknown() { - return - } - if config.ZoneUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") - return - } - if config.ZoneUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.ZoneUid.IsNull() && !config.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, config.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != config.ZoneUid.ValueString() { - config.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - params := map[int]string{ - 75: resources_core.FormatString(config.ZoneUid), - 76: resources_core.FormatString(config.RecordType), - 77: resources_core.FormatString(config.RecordName), - 78: resources_core.FormatString(config.RecordInput), - 450: resources_core.FormatInt64(config.RecordTTL), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 111, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DnsrecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.RecordInput.IsNull() || data.RecordInput.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if data.RecordName.IsNull() || data.RecordName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - if data.RecordType.IsNull() || data.RecordType.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") - return - } - if data.ZoneUid.IsNull() || data.ZoneUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalZoneUid := data.ZoneUid - if !data.ZoneUid.IsNull() && !data.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, data.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedZoneUid != "" && resolvedZoneUid != data.ZoneUid.ValueString() { - data.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 75: resources_core.FormatString(data.ZoneUid), - 76: resources_core.FormatString(data.RecordType), - 77: resources_core.FormatString(data.RecordName), - 78: resources_core.FormatString(data.RecordInput), - 450: resources_core.FormatInt64(data.RecordTTL), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 111, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, state.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { - state.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalZoneUid.IsNull() && !originalZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - if strings.EqualFold(state.ZoneUid.ValueString(), originalZoneUid.ValueString()) { - state.ZoneUid = originalZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnsrecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 111, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, newState.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != newState.ZoneUid.ValueString() { - newState.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() && !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { - if strings.EqualFold(newState.ZoneUid.ValueString(), state.ZoneUid.ValueString()) { - newState.ZoneUid = state.ZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DnsrecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DnsrecordModel - var state DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.RecordInput.IsNull() != state.RecordInput.IsNull() || plan.RecordInput.IsUnknown() != state.RecordInput.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.RecordInput.IsNull() && !plan.RecordInput.IsUnknown() && !state.RecordInput.IsNull() && !state.RecordInput.IsUnknown() { - if !strings.EqualFold(plan.RecordInput.ValueString(), state.RecordInput.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 256: resources_core.FormatString(plan.RecordInput), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 111, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, state.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { - state.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - if strings.EqualFold(state.ZoneUid.ValueString(), plan.ZoneUid.ValueString()) { - state.ZoneUid = plan.ZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnsrecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DnsrecordResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DnsrecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/112_tenant_resource.go b/provider/internal/resources_gen/112_tenant_resource.go deleted file mode 100644 index 57ca92c..0000000 --- a/provider/internal/resources_gen/112_tenant_resource.go +++ /dev/null @@ -1,372 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: tenant -// Service ID: 112 - -var _ resource.Resource = &TenantResource{} -var _ resource.ResourceWithModifyPlan = &TenantResource{} -var _ resource.ResourceWithImportState = &TenantResource{} - -type TenantResource struct { - client *core.UniversalClient -} - -type TenantModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - GrafanaOrgName types.String `tfsdk:"grafana_org_name"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewTenantResource() resource.Resource { - return &TenantResource{} -} - -func (r *TenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_tenant" -} - -func (r *TenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "grafana_org_name": schema.StringAttribute{Required: true, MarkdownDescription: "WZ-Код клиента Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("grafana.ngcloud.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *TenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *TenantModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.GrafanaOrgName.IsNull() && !plan.GrafanaOrgName.IsUnknown() && !state.GrafanaOrgName.IsNull() && !state.GrafanaOrgName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.GrafanaOrgName.ValueString(), state.GrafanaOrgName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить grafana_org_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.GrafanaOrgName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") - return - } - if config.GrafanaOrgName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 239: resources_core.FormatString(config.GrafanaOrgName), - 240: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 112, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *TenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.GrafanaOrgName.IsNull() || data.GrafanaOrgName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 239: resources_core.FormatString(data.GrafanaOrgName), - 240: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 112, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 112, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *TenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan TenantModel - var state TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 112, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *TenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *TenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/113_vc_complex_resource.go b/provider/internal/resources_gen/113_vc_complex_resource.go deleted file mode 100644 index f8eace6..0000000 --- a/provider/internal/resources_gen/113_vc_complex_resource.go +++ /dev/null @@ -1,563 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_complex -// Service ID: 113 - -var _ resource.Resource = &VcComplexResource{} -var _ resource.ResourceWithModifyPlan = &VcComplexResource{} -var _ resource.ResourceWithImportState = &VcComplexResource{} - -type VcComplexResource struct { - client *core.UniversalClient -} - -type VcComplexModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - CpuAllocated types.String `tfsdk:"cpu_allocated"` - CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - MemAllocated types.Int64 `tfsdk:"mem_allocated"` - MemGuaranteed types.Int64 `tfsdk:"mem_guaranteed"` - NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` - NeedExternalAddressSNAT types.Bool `tfsdk:"need_external_address_s_n_a_t"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SegroupName types.String `tfsdk:"segroup_name"` - StorageConfig types.String `tfsdk:"storage_config"` - VIPCount types.Int64 `tfsdk:"v_i_p_count"` - VappName types.String `tfsdk:"vapp_name"` - VdcNetworkPool types.String `tfsdk:"vdc_network_pool"` - VdcProviderGateway types.String `tfsdk:"vdc_provider_gateway"` - VirtualServicesCount types.String `tfsdk:"virtual_services_count"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcComplexResource() resource.Resource { - return &VcComplexResource{} -} - -func (r *VcComplexResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_complex" -} - -func (r *VcComplexResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "cpu_allocated": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "шт Не изменяется после создания."}, - "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, - "ip_space_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("internet-ipv4-v1"), MarkdownDescription: "Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT Не изменяется после создания."}, - "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "gb Не изменяется после создания."}, - "mem_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, - "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает AVI в Cloud Director Не изменяется после создания."}, - "need_external_address_s_n_a_t": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP на Edge. Используется для SNAT всего трафика Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "segroup_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("SEGROUP-SANDBOX-CL1-SHARED-01"), MarkdownDescription: "Наименование segroup, в котором будут выделяться пулы на AVI Не изменяется после создания."}, - "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{\"name\": \"\", \"size\": 1024}]"), MarkdownDescription: "Описывается как массив объектов, где описывается имя политики и её размер Не изменяется после создания.", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "v_i_p_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "шт Не изменяется после создания."}, - "vapp_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя vApp Не изменяется после создания."}, - "vdc_network_pool": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nsxt-sandbox-geneve-np"), MarkdownDescription: "Не изменяется после создания."}, - "vdc_provider_gateway": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox-v1cl1-pvdc"), MarkdownDescription: "Взять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List<String>) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String Не изменяется после создания."}, - "virtual_services_count": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcComplexResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcComplexModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.CpuAllocated.ValueString(), state.CpuAllocated.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить cpu_allocated", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { - if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { - if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить mem_allocated", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MemGuaranteed.IsNull() && !plan.MemGuaranteed.IsUnknown() && !state.MemGuaranteed.IsNull() && !state.MemGuaranteed.IsUnknown() { - if plan.MemGuaranteed.ValueInt64() != state.MemGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить mem_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { - if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить need_enable_a_v_i", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NeedExternalAddressSNAT.IsNull() && !plan.NeedExternalAddressSNAT.IsUnknown() && !state.NeedExternalAddressSNAT.IsNull() && !state.NeedExternalAddressSNAT.IsUnknown() { - if plan.NeedExternalAddressSNAT.ValueBool() != state.NeedExternalAddressSNAT.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить need_external_address_s_n_a_t", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.SegroupName.IsNull() && !plan.SegroupName.IsUnknown() && !state.SegroupName.IsNull() && !state.SegroupName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.SegroupName.ValueString(), state.SegroupName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить segroup_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.StorageConfig.ValueString(), state.StorageConfig.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить storage_config", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VIPCount.IsNull() && !plan.VIPCount.IsUnknown() && !state.VIPCount.IsNull() && !state.VIPCount.IsUnknown() { - if plan.VIPCount.ValueInt64() != state.VIPCount.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить v_i_p_count", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappName.ValueString(), state.VappName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcNetworkPool.IsNull() && !plan.VdcNetworkPool.IsUnknown() && !state.VdcNetworkPool.IsNull() && !state.VdcNetworkPool.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcNetworkPool.ValueString(), state.VdcNetworkPool.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_network_pool", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcProviderGateway.IsNull() && !plan.VdcProviderGateway.IsUnknown() && !state.VdcProviderGateway.IsNull() && !state.VdcProviderGateway.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcProviderGateway.ValueString(), state.VdcProviderGateway.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_provider_gateway", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VirtualServicesCount.ValueString(), state.VirtualServicesCount.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить virtual_services_count", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VappName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") - return - } - if config.VappName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 332: resources_core.FormatString(config.ResourceRealm), - 334: resources_core.FormatString(config.VappName), - 358: resources_core.FormatString(config.VdcProviderGateway), - 359: resources_core.FormatString(config.VirtualServicesCount), - 360: resources_core.FormatString(config.IpSpaceName), - 399: resources_core.FormatString(config.StorageConfig), - 400: resources_core.FormatString(config.VdcNetworkPool), - 401: resources_core.FormatInt64(config.CpuGuaranteed), - 402: resources_core.FormatInt64(config.MemGuaranteed), - 403: resources_core.FormatString(config.SegroupName), - 404: resources_core.FormatBool(config.NeedExternalAddressSNAT), - 405: resources_core.FormatBool(config.NeedEnableAVI), - 566: resources_core.FormatString(config.CpuAllocated), - 567: resources_core.FormatInt64(config.MemAllocated), - 568: resources_core.FormatInt64(config.VIPCount), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 113, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcComplexResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VappName.IsNull() || data.VappName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 332: resources_core.FormatString(data.ResourceRealm), - 334: resources_core.FormatString(data.VappName), - 358: resources_core.FormatString(data.VdcProviderGateway), - 359: resources_core.FormatString(data.VirtualServicesCount), - 360: resources_core.FormatString(data.IpSpaceName), - 399: resources_core.FormatString(data.StorageConfig), - 400: resources_core.FormatString(data.VdcNetworkPool), - 401: resources_core.FormatInt64(data.CpuGuaranteed), - 402: resources_core.FormatInt64(data.MemGuaranteed), - 403: resources_core.FormatString(data.SegroupName), - 404: resources_core.FormatBool(data.NeedExternalAddressSNAT), - 405: resources_core.FormatBool(data.NeedEnableAVI), - 566: resources_core.FormatString(data.CpuAllocated), - 567: resources_core.FormatInt64(data.MemAllocated), - 568: resources_core.FormatInt64(data.VIPCount), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 113, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcComplexResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 113, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcComplexResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcComplexModel - var state VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 113, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcComplexResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcComplexResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcComplexResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/115_mariadb_database_resource.go b/provider/internal/resources_gen/115_mariadb_database_resource.go deleted file mode 100644 index cd6dd52..0000000 --- a/provider/internal/resources_gen/115_mariadb_database_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Subresource: database - -var _ resource.Resource = &MariadbDatabaseResource{} - -// MariadbDatabaseResource manages a subresource via operations. -type MariadbDatabaseResource struct { - client *core.UniversalClient -} - -type MariadbDatabaseModel struct { - ID types.String `tfsdk:"id"` - MariadbID types.String `tfsdk:"mariadb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` -} - -func NewMariadbDatabaseResource() resource.Resource { - return &MariadbDatabaseResource{} -} - -func (r *MariadbDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb_database" -} - -func (r *MariadbDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mariadb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("db-dxinlvxx"), MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MariadbDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MariadbDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/115_mariadb_resource.go b/provider/internal/resources_gen/115_mariadb_resource.go deleted file mode 100644 index 6185299..0000000 --- a/provider/internal/resources_gen/115_mariadb_resource.go +++ /dev/null @@ -1,447 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Service ID: 115 - -var _ resource.Resource = &MariadbResource{} -var _ resource.ResourceWithModifyPlan = &MariadbResource{} -var _ resource.ResourceWithImportState = &MariadbResource{} - -type MariadbResource struct { - client *core.UniversalClient -} - -// MariadbAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type MariadbAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// MariadbAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type MariadbAutoscaleConfigurationModel struct { - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` -} - -// MariadbBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type MariadbBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// MariadbClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type MariadbClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// MariadbMariadbConfigurationModel — вложенная модель для map-fixed параметра mariadbConfiguration. -type MariadbMariadbConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -// MariadbStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type MariadbStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type MariadbModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *MariadbAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *MariadbAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *MariadbBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *MariadbClusterConfigurationModel `tfsdk:"cluster_configuration"` - MariadbConfiguration *MariadbMariadbConfigurationModel `tfsdk:"mariadb_configuration"` - StartupConfiguration *MariadbStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewMariadbResource() resource.Resource { - return &MariadbResource{} -} - -func (r *MariadbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb" -} - -func (r *MariadbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(720)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать"}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - }}, - "mariadb_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("9.4.0")}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *MariadbModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 877: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 878: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 879: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": config.AccessConfiguration.MasterAccessList.ValueString()}), - 880: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.MariadbConfiguration.Version.ValueString())}), - 881: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 882: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 115, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *MariadbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 877: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 878: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 879: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": data.AccessConfiguration.MasterAccessList.ValueString()}), - 880: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.MariadbConfiguration.Version.ValueString())}), - 881: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 882: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 115, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 115, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *MariadbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbModel - var state MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 883: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 884: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": plan.AccessConfiguration.MasterAccessList.ValueString()}), - 885: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.MariadbConfiguration.Version.ValueString())}), - 886: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 887: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 115, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *MariadbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *MariadbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/115_mariadb_user_resource.go b/provider/internal/resources_gen/115_mariadb_user_resource.go deleted file mode 100644 index 5a2f790..0000000 --- a/provider/internal/resources_gen/115_mariadb_user_resource.go +++ /dev/null @@ -1,367 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Subresource: user - -var _ resource.Resource = &MariadbUserResource{} - -// MariadbUserResource manages a subresource via operations. -type MariadbUserResource struct { - client *core.UniversalClient -} - -type MariadbUserModel struct { - ID types.String `tfsdk:"id"` - MariadbID types.String `tfsdk:"mariadb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - DbName types.String `tfsdk:"db_name"` - Password types.String `tfsdk:"password"` - Username types.String `tfsdk:"username"` -} - -func NewMariadbUserResource() resource.Resource { - return &MariadbUserResource{} -} - -func (r *MariadbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb_user" -} - -func (r *MariadbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mariadb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("%"), MarkdownDescription: "Пример: `%` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Необходимо указать массив существующих баз Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "password": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("mhs3keq0"), MarkdownDescription: "Секрет будет записан в хранилище секретов и не будет отображаться публично Не изменяется после создания. Изменение требует пересоздания.", Sensitive: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("userz2hsgoyo"), MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MariadbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "password": resources_core.FormatString(plan.Password), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "password", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "password": resources_core.FormatString(plan.Password), - "dbName": resources_core.FormatString(plan.DbName), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "password": resources_core.FormatString(plan.Password), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(state.AccessHosts), - "dbName": resources_core.FormatString(state.DbName), - "password": resources_core.FormatString(state.Password), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "password", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MariadbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/116_kafka_resource.go b/provider/internal/resources_gen/116_kafka_resource.go deleted file mode 100644 index 108ee87..0000000 --- a/provider/internal/resources_gen/116_kafka_resource.go +++ /dev/null @@ -1,389 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Service ID: 116 - -var _ resource.Resource = &KafkaResource{} -var _ resource.ResourceWithModifyPlan = &KafkaResource{} -var _ resource.ResourceWithImportState = &KafkaResource{} - -type KafkaResource struct { - client *core.UniversalClient -} - -// KafkaAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type KafkaAccessConfigurationModel struct { - AllocationExternal types.Bool `tfsdk:"allocation_external" json:"allocationExternal"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// KafkaClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type KafkaClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// KafkaStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type KafkaStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type KafkaModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *KafkaAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *KafkaClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *KafkaStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewKafkaResource() resource.Resource { - return &KafkaResource{} -} - -func (r *KafkaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka" -} - -func (r *KafkaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "allocation_external": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048)}, - "replicas": schema.Int64Attribute{Required: true}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *KafkaModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1084: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1085: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1086: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", config.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": config.AccessConfiguration.MasterAccessList.ValueString()}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 116, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *KafkaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1084: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1085: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1086: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", data.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": data.AccessConfiguration.MasterAccessList.ValueString()}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 116, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 116, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *KafkaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaModel - var state KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1087: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 1088: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", plan.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": plan.AccessConfiguration.MasterAccessList.ValueString()}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 116, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *KafkaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *KafkaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/116_kafka_topic_resource.go b/provider/internal/resources_gen/116_kafka_topic_resource.go deleted file mode 100644 index 3f289b3..0000000 --- a/provider/internal/resources_gen/116_kafka_topic_resource.go +++ /dev/null @@ -1,364 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Subresource: topic - -var _ resource.Resource = &KafkaTopicResource{} - -// KafkaTopicResource manages a subresource via operations. -type KafkaTopicResource struct { - client *core.UniversalClient -} - -type KafkaTopicModel struct { - ID types.String `tfsdk:"id"` - KafkaID types.String `tfsdk:"kafka_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - NameTopic types.String `tfsdk:"name_topic"` - Partitions types.Int64 `tfsdk:"partitions"` - Replicas types.Int64 `tfsdk:"replicas"` -} - -func NewKafkaTopicResource() resource.Resource { - return &KafkaTopicResource{} -} - -func (r *KafkaTopicResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka_topic" -} - -func (r *KafkaTopicResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "kafka_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций можно увеличить, но уменьшить их нельзя."}, - "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество реплик не может привышать количество реплик брокера"}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaTopicResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan KafkaTopicModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if plan.Partitions.IsNull() || plan.Partitions.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") - return - } - if plan.Replicas.IsNull() || plan.Replicas.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - } - identityCodes := []string{ - "nameTopic", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "partitions": resources_core.FormatInt64(plan.Partitions), - "replicas": resources_core.FormatInt64(plan.Replicas), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaTopicResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaTopicResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaTopicModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_topic" == "" { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "partitions": resources_core.FormatInt64(plan.Partitions), - "replicas": resources_core.FormatInt64(plan.Replicas), - }) - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_topic", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaTopicResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_topic" == "" { - return - } - - instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(state.NameTopic), - } - identityCodes := []string{ - "nameTopic", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(state.NameTopic), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_topic", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *KafkaTopicResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/116_kafka_user_resource.go b/provider/internal/resources_gen/116_kafka_user_resource.go deleted file mode 100644 index 52ea73c..0000000 --- a/provider/internal/resources_gen/116_kafka_user_resource.go +++ /dev/null @@ -1,373 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Subresource: user - -var _ resource.Resource = &KafkaUserResource{} - -// KafkaUserResource manages a subresource via operations. -type KafkaUserResource struct { - client *core.UniversalClient -} - -type KafkaUserModel struct { - ID types.String `tfsdk:"id"` - KafkaID types.String `tfsdk:"kafka_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - Group types.String `tfsdk:"group"` - NameTopic types.String `tfsdk:"name_topic"` - Operations types.String `tfsdk:"operations"` - Username types.String `tfsdk:"username"` -} - -func NewKafkaUserResource() resource.Resource { - return &KafkaUserResource{} -} - -func (r *KafkaUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka_user" -} - -func (r *KafkaUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "kafka_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("*"), MarkdownDescription: "Пример: `192.168.1.0/24` или `*`"}, - "group": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("*"), MarkdownDescription: "Пример: `order-consumers` или `*`"}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`"}, - "operations": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `Create, Describe, Read, Write`"}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `username-admin` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan KafkaUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if plan.Operations.IsNull() || plan.Operations.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "operations is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "nameTopic": resources_core.FormatString(plan.NameTopic), - "operations": resources_core.FormatString(plan.Operations), - "group": resources_core.FormatString(plan.Group), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_user" == "" { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "operations": resources_core.FormatString(plan.Operations), - "group": resources_core.FormatString(plan.Group), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "username": resources_core.FormatString(plan.Username), - }) - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *KafkaUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/119_akhq_resource.go b/provider/internal/resources_gen/119_akhq_resource.go deleted file mode 100644 index 101c30a..0000000 --- a/provider/internal/resources_gen/119_akhq_resource.go +++ /dev/null @@ -1,554 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: akhq -// Service ID: 119 - -var _ resource.Resource = &AkhqResource{} -var _ resource.ResourceWithModifyPlan = &AkhqResource{} -var _ resource.ResourceWithImportState = &AkhqResource{} - -type AkhqResource struct { - client *core.UniversalClient -} - -type AkhqModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - KafkaUid types.String `tfsdk:"kafka_uid"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewAkhqResource() resource.Resource { - return &AkhqResource{} -} - -func (r *AkhqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_akhq" -} - -func (r *AkhqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "kafka_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Выбрать кластер из списка"}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `1000` Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `1024` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *AkhqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *AkhqModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Domain.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if config.Domain.IsUnknown() { - return - } - if config.KafkaUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if config.KafkaUid.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.KafkaUid.IsNull() && !config.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, config.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != config.KafkaUid.ValueString() { - config.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - params := map[int]string{ - 550: resources_core.FormatString(config.ResourceRealm), - 551: resources_core.FormatInt64(config.ResourceMemory), - 552: resources_core.FormatInt64(config.ResourceCPU), - 553: resources_core.FormatString(config.Domain), - 554: resources_core.FormatInt64(config.ResourceInstances), - 555: resources_core.FormatString(config.KafkaUid), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 119, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *AkhqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Domain.IsNull() || data.Domain.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if data.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalKafkaUid := data.KafkaUid - if !data.KafkaUid.IsNull() && !data.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, data.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedKafkaUid != "" && resolvedKafkaUid != data.KafkaUid.ValueString() { - data.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 550: resources_core.FormatString(data.ResourceRealm), - 551: resources_core.FormatInt64(data.ResourceMemory), - 552: resources_core.FormatInt64(data.ResourceCPU), - 553: resources_core.FormatString(data.Domain), - 554: resources_core.FormatInt64(data.ResourceInstances), - 555: resources_core.FormatString(data.KafkaUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 119, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalKafkaUid.IsNull() && !originalKafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), originalKafkaUid.ValueString()) { - state.KafkaUid = originalKafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *AkhqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 119, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, newState.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != newState.KafkaUid.ValueString() { - newState.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() && !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - if strings.EqualFold(newState.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - newState.KafkaUid = state.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *AkhqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan AkhqModel - var state AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.KafkaUid.IsNull() != state.KafkaUid.IsNull() || plan.KafkaUid.IsUnknown() != state.KafkaUid.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if !strings.EqualFold(plan.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 565: resources_core.FormatString(plan.KafkaUid), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 119, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), plan.KafkaUid.ValueString()) { - state.KafkaUid = plan.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *AkhqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *AkhqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *AkhqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/120_clickhouse_database_resource.go b/provider/internal/resources_gen/120_clickhouse_database_resource.go deleted file mode 100644 index cc5c42e..0000000 --- a/provider/internal/resources_gen/120_clickhouse_database_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Subresource: database - -var _ resource.Resource = &ClickhouseDatabaseResource{} - -// ClickhouseDatabaseResource manages a subresource via operations. -type ClickhouseDatabaseResource struct { - client *core.UniversalClient -} - -type ClickhouseDatabaseModel struct { - ID types.String `tfsdk:"id"` - ClickhouseID types.String `tfsdk:"clickhouse_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` -} - -func NewClickhouseDatabaseResource() resource.Resource { - return &ClickhouseDatabaseResource{} -} - -func (r *ClickhouseDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse_database" -} - -func (r *ClickhouseDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "clickhouse_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("db01"), MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ClickhouseDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ClickhouseDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/120_clickhouse_resource.go b/provider/internal/resources_gen/120_clickhouse_resource.go deleted file mode 100644 index 27781cb..0000000 --- a/provider/internal/resources_gen/120_clickhouse_resource.go +++ /dev/null @@ -1,487 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Service ID: 120 - -var _ resource.Resource = &ClickhouseResource{} -var _ resource.ResourceWithModifyPlan = &ClickhouseResource{} -var _ resource.ResourceWithImportState = &ClickhouseResource{} - -type ClickhouseResource struct { - client *core.UniversalClient -} - -// ClickhouseAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type ClickhouseAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// ClickhouseAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type ClickhouseAutoscaleConfigurationModel struct { - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` -} - -// ClickhouseBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type ClickhouseBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// ClickhouseClickhouseConfigurationModel — вложенная модель для map-fixed параметра clickhouseConfiguration. -type ClickhouseClickhouseConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -// ClickhouseClickhouseKeeperConfigurationModel — вложенная модель для map-fixed параметра clickhouseKeeperConfiguration. -type ClickhouseClickhouseKeeperConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// ClickhouseClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type ClickhouseClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Shards types.Int64 `tfsdk:"shards" json:"shards"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// ClickhouseStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type ClickhouseStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type ClickhouseModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *ClickhouseAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *ClickhouseAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *ClickhouseBackupConfigurationModel `tfsdk:"backup_configuration"` - ClickhouseConfiguration *ClickhouseClickhouseConfigurationModel `tfsdk:"clickhouse_configuration"` - ClickhouseFilesConfiguration types.String `tfsdk:"clickhouse_files_configuration"` - ClickhouseKeeperConfiguration *ClickhouseClickhouseKeeperConfigurationModel `tfsdk:"clickhouse_keeper_configuration"` - ClusterConfiguration *ClickhouseClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *ClickhouseStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewClickhouseResource() resource.Resource { - return &ClickhouseResource{} -} - -func (r *ClickhouseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse" -} - -func (r *ClickhouseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "clickhouse_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("25.10")}, - }}, - "clickhouse_files_configuration": schema.StringAttribute{Optional: true, MarkdownDescription: "Формат:
- path: somethings3.xml
xmlConfig: '<clickhouse>......</clickhouse>'"}, - "clickhouse_keeper_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "shards": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда"}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ClickhouseModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 837: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 838: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", config.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 839: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": config.AccessConfiguration.MasterAccessList.ValueString()}), - 840: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 841: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.ClickhouseConfiguration.Version.ValueString())}), - 842: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 843: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64())}), - 851: resources_core.FormatString(config.ClickhouseFilesConfiguration), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 120, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ClickhouseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 837: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 838: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", data.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 839: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": data.AccessConfiguration.MasterAccessList.ValueString()}), - 840: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 841: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.ClickhouseConfiguration.Version.ValueString())}), - 842: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 843: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64())}), - 851: resources_core.FormatString(data.ClickhouseFilesConfiguration), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 120, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 120, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ClickhouseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseModel - var state ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ClickhouseFilesConfiguration.IsNull() != state.ClickhouseFilesConfiguration.IsNull() || plan.ClickhouseFilesConfiguration.IsUnknown() != state.ClickhouseFilesConfiguration.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ClickhouseFilesConfiguration.IsNull() && !plan.ClickhouseFilesConfiguration.IsUnknown() && !state.ClickhouseFilesConfiguration.IsNull() && !state.ClickhouseFilesConfiguration.IsUnknown() { - if !strings.EqualFold(plan.ClickhouseFilesConfiguration.ValueString(), state.ClickhouseFilesConfiguration.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 844: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", plan.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 845: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Memory.ValueInt64())}), - 846: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": plan.AccessConfiguration.MasterAccessList.ValueString()}), - 847: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.ClickhouseConfiguration.Version.ValueString())}), - 848: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 849: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - 850: resources_core.FormatString(plan.ClickhouseFilesConfiguration), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 120, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ClickhouseResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ClickhouseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/120_clickhouse_user_resource.go b/provider/internal/resources_gen/120_clickhouse_user_resource.go deleted file mode 100644 index 8dc28f7..0000000 --- a/provider/internal/resources_gen/120_clickhouse_user_resource.go +++ /dev/null @@ -1,359 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Subresource: user - -var _ resource.Resource = &ClickhouseUserResource{} - -// ClickhouseUserResource manages a subresource via operations. -type ClickhouseUserResource struct { - client *core.UniversalClient -} - -type ClickhouseUserModel struct { - ID types.String `tfsdk:"id"` - ClickhouseID types.String `tfsdk:"clickhouse_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - DbName types.String `tfsdk:"db_name"` - Username types.String `tfsdk:"username"` -} - -func NewClickhouseUserResource() resource.Resource { - return &ClickhouseUserResource{} -} - -func (r *ClickhouseUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse_user" -} - -func (r *ClickhouseUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "clickhouse_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("::/0"), MarkdownDescription: "Пример: `::/0` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myusername"), MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ClickhouseUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(state.AccessHosts), - "dbName": resources_core.FormatString(state.DbName), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ClickhouseUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/12_s3_resource.go b/provider/internal/resources_gen/12_s3_resource.go deleted file mode 100644 index f4db5ce..0000000 --- a/provider/internal/resources_gen/12_s3_resource.go +++ /dev/null @@ -1,421 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3 -// Service ID: 12 - -var _ resource.Resource = &S3Resource{} -var _ resource.ResourceWithModifyPlan = &S3Resource{} -var _ resource.ResourceWithImportState = &S3Resource{} - -type S3Resource struct { - client *core.UniversalClient -} - -type S3Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DisplayName types.String `tfsdk:"display_name"` - MaxBucketsPerUser types.Int64 `tfsdk:"max_buckets_per_user"` - MaxObjectsPerBucket types.Int64 `tfsdk:"max_objects_per_bucket"` - MaxSizeGbPerUser types.Int64 `tfsdk:"max_size_gb_per_user"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewS3Resource() resource.Resource { - return &S3Resource{} -} - -func (r *S3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3" -} - -func (r *S3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "display_name": schema.StringAttribute{Required: true, MarkdownDescription: "Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента Не изменяется после создания."}, - "max_buckets_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на пользователя. Количество бакетов пользователя. Передаём руками"}, - "max_objects_per_bucket": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на бакет. Кол-во объектов. Передаём руками"}, - "max_size_gb_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "maxSizeGb. Передаём руками"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ceph.tst.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *S3Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.DisplayName.IsNull() && !plan.DisplayName.IsUnknown() && !state.DisplayName.IsNull() && !state.DisplayName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.DisplayName.ValueString(), state.DisplayName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить display_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.DisplayName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") - return - } - if config.DisplayName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 163: resources_core.FormatString(config.ResourceRealm), - 320: resources_core.FormatString(config.DisplayName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 12, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *S3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.DisplayName.IsNull() || data.DisplayName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 163: resources_core.FormatString(data.ResourceRealm), - 320: resources_core.FormatString(data.DisplayName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 12, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 12, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *S3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3Model - var state S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.MaxSizeGbPerUser.IsNull() != state.MaxSizeGbPerUser.IsNull() || plan.MaxSizeGbPerUser.IsUnknown() != state.MaxSizeGbPerUser.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxSizeGbPerUser.IsNull() && !plan.MaxSizeGbPerUser.IsUnknown() && !state.MaxSizeGbPerUser.IsNull() && !state.MaxSizeGbPerUser.IsUnknown() { - if plan.MaxSizeGbPerUser.ValueInt64() != state.MaxSizeGbPerUser.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MaxObjectsPerBucket.IsNull() != state.MaxObjectsPerBucket.IsNull() || plan.MaxObjectsPerBucket.IsUnknown() != state.MaxObjectsPerBucket.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxObjectsPerBucket.IsNull() && !plan.MaxObjectsPerBucket.IsUnknown() && !state.MaxObjectsPerBucket.IsNull() && !state.MaxObjectsPerBucket.IsUnknown() { - if plan.MaxObjectsPerBucket.ValueInt64() != state.MaxObjectsPerBucket.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MaxBucketsPerUser.IsNull() != state.MaxBucketsPerUser.IsNull() || plan.MaxBucketsPerUser.IsUnknown() != state.MaxBucketsPerUser.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxBucketsPerUser.IsNull() && !plan.MaxBucketsPerUser.IsUnknown() && !state.MaxBucketsPerUser.IsNull() && !state.MaxBucketsPerUser.IsUnknown() { - if plan.MaxBucketsPerUser.ValueInt64() != state.MaxBucketsPerUser.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 50: resources_core.FormatInt64(plan.MaxSizeGbPerUser), - 51: resources_core.FormatInt64(plan.MaxObjectsPerBucket), - 52: resources_core.FormatInt64(plan.MaxBucketsPerUser), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 12, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *S3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *S3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/12_s3_sub_user_resource.go b/provider/internal/resources_gen/12_s3_sub_user_resource.go deleted file mode 100644 index b8e0168..0000000 --- a/provider/internal/resources_gen/12_s3_sub_user_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3 -// Subresource: sub_user - -var _ resource.Resource = &S3SubUserResource{} - -// S3SubUserResource manages a subresource via operations. -type S3SubUserResource struct { - client *core.UniversalClient -} - -type S3SubUserModel struct { - ID types.String `tfsdk:"id"` - S3ID types.String `tfsdk:"s3_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SubUserAccess types.String `tfsdk:"sub_user_access"` - SubUserName types.String `tfsdk:"sub_user_name"` -} - -func NewS3SubUserResource() resource.Resource { - return &S3SubUserResource{} -} - -func (r *S3SubUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3_sub_user" -} - -func (r *S3SubUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "s3_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "sub_user_access": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "sub_user_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3SubUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan S3SubUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.SubUserAccess.IsNull() || plan.SubUserAccess.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "sub_user_access is required.") - return - } - if plan.SubUserName.IsNull() || plan.SubUserName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "sub_user_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - "subUserName": resources_core.FormatString(plan.SubUserName), - } - identityCodes := []string{ - "subUserAccess", - "subUserName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "subUserName": resources_core.FormatString(plan.SubUserName), - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *S3SubUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3SubUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3SubUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - "subUserName": resources_core.FormatString(plan.SubUserName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *S3SubUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_sub_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.S3ID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(state.SubUserAccess), - "subUserName": resources_core.FormatString(state.SubUserName), - } - identityCodes := []string{ - "subUserAccess", - "subUserName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "subUserName": resources_core.FormatString(state.SubUserName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_sub_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *S3SubUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/13_s3bucket_resource.go b/provider/internal/resources_gen/13_s3bucket_resource.go deleted file mode 100644 index deb2272..0000000 --- a/provider/internal/resources_gen/13_s3bucket_resource.go +++ /dev/null @@ -1,478 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3bucket -// Service ID: 13 - -var _ resource.Resource = &S3bucketResource{} -var _ resource.ResourceWithModifyPlan = &S3bucketResource{} -var _ resource.ResourceWithImportState = &S3bucketResource{} - -type S3bucketResource struct { - client *core.UniversalClient -} - -type S3bucketModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - BucketName types.String `tfsdk:"bucket_name"` - CorsAll types.Bool `tfsdk:"cors_all"` - ListAll types.Bool `tfsdk:"list_all"` - MaxSize types.String `tfsdk:"max_size"` - Placement types.String `tfsdk:"placement"` - ReadAll types.Bool `tfsdk:"read_all"` - S3UserUid types.String `tfsdk:"s3_user_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewS3bucketResource() resource.Resource { - return &S3bucketResource{} -} - -func (r *S3bucketResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3bucket" -} - -func (r *S3bucketResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "bucket_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя бакета s3 Не изменяется после создания."}, - "cors_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS Не изменяется после создания."}, - "list_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. Не изменяется после создания."}, - "max_size": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("-1"), MarkdownDescription: "Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes Не изменяется после создания."}, - "placement": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("HOT"), MarkdownDescription: "Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения Не изменяется после создания."}, - "read_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. Не изменяется после создания."}, - "s3_user_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UID Корневой услуги S3. На основе него будет создан бакет. Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3bucketResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *S3bucketModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.BucketName.IsNull() && !plan.BucketName.IsUnknown() && !state.BucketName.IsNull() && !state.BucketName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.BucketName.ValueString(), state.BucketName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить bucket_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.CorsAll.IsNull() && !plan.CorsAll.IsUnknown() && !state.CorsAll.IsNull() && !state.CorsAll.IsUnknown() { - if plan.CorsAll.ValueBool() != state.CorsAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить cors_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ListAll.IsNull() && !plan.ListAll.IsUnknown() && !state.ListAll.IsNull() && !state.ListAll.IsUnknown() { - if plan.ListAll.ValueBool() != state.ListAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить list_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MaxSize.IsNull() && !plan.MaxSize.IsUnknown() && !state.MaxSize.IsNull() && !state.MaxSize.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.MaxSize.ValueString(), state.MaxSize.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить max_size", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Placement.IsNull() && !plan.Placement.IsUnknown() && !state.Placement.IsNull() && !state.Placement.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Placement.ValueString(), state.Placement.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить placement", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ReadAll.IsNull() && !plan.ReadAll.IsUnknown() && !state.ReadAll.IsNull() && !state.ReadAll.IsUnknown() { - if plan.ReadAll.ValueBool() != state.ReadAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить read_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.S3UserUid.IsNull() && !plan.S3UserUid.IsUnknown() && !state.S3UserUid.IsNull() && !state.S3UserUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.S3UserUid.ValueString(), state.S3UserUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить s3_user_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.BucketName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") - return - } - if config.BucketName.IsUnknown() { - return - } - if config.S3UserUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") - return - } - if config.S3UserUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.S3UserUid.IsNull() && !config.S3UserUid.IsUnknown() { - resolvedS3UserUid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3UserUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve s3_user_uid", err.Error()) - } else if resolvedS3UserUid != "" && resolvedS3UserUid != config.S3UserUid.ValueString() { - config.S3UserUid = types.StringValue(resolvedS3UserUid) - } - } - params := map[int]string{ - 124: resources_core.FormatString(config.S3UserUid), - 125: resources_core.FormatString(config.BucketName), - 126: resources_core.FormatString(config.MaxSize), - 127: resources_core.FormatBool(config.ReadAll), - 128: resources_core.FormatBool(config.ListAll), - 129: resources_core.FormatBool(config.CorsAll), - 130: resources_core.FormatString(config.Placement), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 13, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *S3bucketResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.BucketName.IsNull() || data.BucketName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") - return - } - if data.S3UserUid.IsNull() || data.S3UserUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 124: resources_core.FormatString(data.S3UserUid), - 125: resources_core.FormatString(data.BucketName), - 126: resources_core.FormatString(data.MaxSize), - 127: resources_core.FormatBool(data.ReadAll), - 128: resources_core.FormatBool(data.ListAll), - 129: resources_core.FormatBool(data.CorsAll), - 130: resources_core.FormatString(data.Placement), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 13, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3bucketResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 13, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *S3bucketResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3bucketModel - var state S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 13, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3bucketResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *S3bucketResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *S3bucketResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/148_vc_mgmt_sthutrval_cluster_resource.go b/provider/internal/resources_gen/148_vc_mgmt_sthutrval_cluster_resource.go deleted file mode 100644 index 92d35cd..0000000 --- a/provider/internal/resources_gen/148_vc_mgmt_sthutrval_cluster_resource.go +++ /dev/null @@ -1,419 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_mgmt_sthutrval_cluster -// Service ID: 148 - -var _ resource.Resource = &VcMgmtSthutrvalClusterResource{} -var _ resource.ResourceWithModifyPlan = &VcMgmtSthutrvalClusterResource{} -var _ resource.ResourceWithImportState = &VcMgmtSthutrvalClusterResource{} - -type VcMgmtSthutrvalClusterResource struct { - client *core.UniversalClient -} - -// VcMgmtSthutrvalClusterAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type VcMgmtSthutrvalClusterAccessConfigurationModel struct { - AccessIpListAPI types.String `tfsdk:"access_ip_list_a_p_i" json:"accessIpListAPI"` - NeedExternalAddressIngress types.Bool `tfsdk:"need_external_address_ingress" json:"needExternalAddressIngress"` - AccessIpListIngress types.String `tfsdk:"access_ip_list_ingress" json:"accessIpListIngress"` - NeedExternalAddressAPI types.Bool `tfsdk:"need_external_address_a_p_i" json:"needExternalAddressAPI"` -} - -// VcMgmtSthutrvalClusterClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type VcMgmtSthutrvalClusterClusterConfigurationModel struct { - AppVersion types.String `tfsdk:"app_version" json:"appVersion"` -} - -// VcMgmtSthutrvalClusterControlPlaneConfigurationModel — вложенная модель для map-fixed параметра controlPlaneConfiguration. -type VcMgmtSthutrvalClusterControlPlaneConfigurationModel struct { - SizingDisk types.Int64 `tfsdk:"sizing_disk" json:"sizingDisk"` - Count types.Int64 `tfsdk:"count" json:"count"` - SizingPolicy types.String `tfsdk:"sizing_policy" json:"sizingPolicy"` -} - -// VcMgmtSthutrvalClusterStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type VcMgmtSthutrvalClusterStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` - ClusterName types.String `tfsdk:"cluster_name" json:"clusterName"` -} - -type VcMgmtSthutrvalClusterModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *VcMgmtSthutrvalClusterAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *VcMgmtSthutrvalClusterClusterConfigurationModel `tfsdk:"cluster_configuration"` - ControlPlaneConfiguration *VcMgmtSthutrvalClusterControlPlaneConfigurationModel `tfsdk:"control_plane_configuration"` - StartupConfiguration *VcMgmtSthutrvalClusterStartupConfigurationModel `tfsdk:"startup_configuration"` - WorkerConfiguration types.String `tfsdk:"worker_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcMgmtSthutrvalClusterResource() resource.Resource { - return &VcMgmtSthutrvalClusterResource{} -} - -func (r *VcMgmtSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_mgmt_sthutrval_cluster" -} - -func (r *VcMgmtSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "access_ip_list_a_p_i": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**"}, - "need_external_address_ingress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к Ingress"}, - "access_ip_list_ingress": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**"}, - "need_external_address_a_p_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.13.1")}, - }}, - "control_plane_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50)}, - "count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "sizing_policy": schema.StringAttribute{Required: true, MarkdownDescription: "Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Облако в котором будет развёрнут тенант"}, - "cluster_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("example-e5uamzll")}, - }}, - "worker_configuration": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcMgmtSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.WorkerConfiguration.IsNull() && !plan.WorkerConfiguration.IsUnknown() && !state.WorkerConfiguration.IsNull() && !state.WorkerConfiguration.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.WorkerConfiguration.ValueString(), state.WorkerConfiguration.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить worker_configuration", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 771: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ClusterName.ValueString())}), - 772: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", config.ClusterConfiguration.AppVersion.ValueString())}), - 773: resources_core.BuildJSON(map[string]string{"sizingDisk": fmt.Sprintf("%d", config.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", config.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", config.ControlPlaneConfiguration.SizingPolicy.ValueString())}), - 774: resources_core.FormatString(config.WorkerConfiguration), - 775: resources_core.BuildJSON(map[string]string{"accessIpListAPI": config.AccessConfiguration.AccessIpListAPI.ValueString(), "needExternalAddressIngress": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": config.AccessConfiguration.AccessIpListIngress.ValueString(), "needExternalAddressAPI": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressAPI.ValueBool())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 148, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 148, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 771: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ClusterName.ValueString())}), - 772: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", data.ClusterConfiguration.AppVersion.ValueString())}), - 773: resources_core.BuildJSON(map[string]string{"sizingDisk": fmt.Sprintf("%d", data.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", data.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", data.ControlPlaneConfiguration.SizingPolicy.ValueString())}), - 774: resources_core.FormatString(data.WorkerConfiguration), - 775: resources_core.BuildJSON(map[string]string{"accessIpListAPI": data.AccessConfiguration.AccessIpListAPI.ValueString(), "needExternalAddressIngress": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": data.AccessConfiguration.AccessIpListIngress.ValueString(), "needExternalAddressAPI": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressAPI.ValueBool())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 148, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 148, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 148, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcMgmtSthutrvalClusterModel - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 148, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcMgmtSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcMgmtSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/149_valo_tenant_resource.go b/provider/internal/resources_gen/149_valo_tenant_resource.go deleted file mode 100644 index a3c1a9f..0000000 --- a/provider/internal/resources_gen/149_valo_tenant_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: valo_tenant -// Service ID: 149 - -var _ resource.Resource = &ValoTenantResource{} -var _ resource.ResourceWithModifyPlan = &ValoTenantResource{} -var _ resource.ResourceWithImportState = &ValoTenantResource{} - -type ValoTenantResource struct { - client *core.UniversalClient -} - -// ValoTenantValoOptionsModel — вложенная модель для map-fixed параметра valoOptions. -type ValoTenantValoOptionsModel struct { - UserCount types.Int64 `tfsdk:"user_count" json:"userCount"` - RequestApproveMaxPeriod types.Int64 `tfsdk:"request_approve_max_period" json:"requestApproveMaxPeriod"` - FfOnlineEditor types.Bool `tfsdk:"ff_online_editor" json:"ffOnlineEditor"` - FfWatermark types.Bool `tfsdk:"ff_watermark" json:"ffWatermark"` - FfUserImport types.Bool `tfsdk:"ff_user_import" json:"ffUserImport"` - FfThemes types.Bool `tfsdk:"ff_themes" json:"ffThemes"` - FfPublicLink types.Bool `tfsdk:"ff_public_link" json:"ffPublicLink"` - FfLogging types.Bool `tfsdk:"ff_logging" json:"ffLogging"` -} - -type ValoTenantModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceRealm types.String `tfsdk:"resource_realm"` - S3Storage types.Int64 `tfsdk:"s3_storage"` - ValoOptions *ValoTenantValoOptionsModel `tfsdk:"valo_options"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewValoTenantResource() resource.Resource { - return &ValoTenantResource{} -} - -func (r *ValoTenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_valo_tenant" -} - -func (r *ValoTenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Будет добавлено в зону valo.ngcloud.ru Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("valo.ngcloud.ru"), MarkdownDescription: "Не изменяется после создания."}, - "s3_storage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Параметр указывается в `Gb`"}, - "valo_options": schema.SingleNestedAttribute{Optional: true, Computed: true, - Attributes: map[string]schema.Attribute{ - "user_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10)}, - "request_approve_max_period": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2), MarkdownDescription: "Максимальное значение периода согласования (в днях)"}, - "ff_online_editor": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_watermark": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_user_import": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_themes": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_public_link": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_logging": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ValoTenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ValoTenantModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Domain.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if config.Domain.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 655: resources_core.FormatString(config.ResourceRealm), - 656: resources_core.FormatString(config.Domain), - 657: resources_core.BuildJSON(map[string]string{"userCount": fmt.Sprintf("%d", config.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", config.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", config.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", config.ValoOptions.FfWatermark.ValueBool()), "ffUserImport": fmt.Sprintf("%v", config.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", config.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", config.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", config.ValoOptions.FfLogging.ValueBool())}), - 658: resources_core.FormatInt64(config.S3Storage), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 149, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ValoTenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Domain.IsNull() || data.Domain.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 655: resources_core.FormatString(data.ResourceRealm), - 656: resources_core.FormatString(data.Domain), - 657: resources_core.BuildJSON(map[string]string{"userCount": fmt.Sprintf("%d", data.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", data.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", data.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", data.ValoOptions.FfWatermark.ValueBool()), "ffUserImport": fmt.Sprintf("%v", data.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", data.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", data.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", data.ValoOptions.FfLogging.ValueBool())}), - 658: resources_core.FormatInt64(data.S3Storage), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 149, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ValoTenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 149, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ValoTenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ValoTenantModel - var state ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.S3Storage.IsNull() != state.S3Storage.IsNull() || plan.S3Storage.IsUnknown() != state.S3Storage.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.S3Storage.IsNull() && !plan.S3Storage.IsUnknown() && !state.S3Storage.IsNull() && !state.S3Storage.IsUnknown() { - if plan.S3Storage.ValueInt64() != state.S3Storage.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 659: resources_core.BuildJSON(map[string]string{"ffUserImport": fmt.Sprintf("%v", plan.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", plan.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", plan.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", plan.ValoOptions.FfLogging.ValueBool()), "userCount": fmt.Sprintf("%d", plan.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", plan.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", plan.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", plan.ValoOptions.FfWatermark.ValueBool())}), - 660: resources_core.FormatInt64(plan.S3Storage), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 149, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ValoTenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ValoTenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ValoTenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go b/provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go deleted file mode 100644 index 256cb6c..0000000 --- a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go +++ /dev/null @@ -1,429 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_sthutrval_cluster -// Service ID: 150 - -var _ resource.Resource = &K8sSthutrvalClusterResource{} -var _ resource.ResourceWithModifyPlan = &K8sSthutrvalClusterResource{} -var _ resource.ResourceWithImportState = &K8sSthutrvalClusterResource{} - -type K8sSthutrvalClusterResource struct { - client *core.UniversalClient -} - -// K8sSthutrvalClusterAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type K8sSthutrvalClusterAccessConfigurationModel struct { - NeedExternalAddressIngress types.Bool `tfsdk:"need_external_address_ingress" json:"needExternalAddressIngress"` - AccessIpListIngress types.String `tfsdk:"access_ip_list_ingress" json:"accessIpListIngress"` - NeedExternalAddressAPI types.Bool `tfsdk:"need_external_address_a_p_i" json:"needExternalAddressAPI"` - AccessIpListAPI types.String `tfsdk:"access_ip_list_a_p_i" json:"accessIpListAPI"` -} - -// K8sSthutrvalClusterClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sSthutrvalClusterClusterConfigurationModel struct { - AppVersion types.String `tfsdk:"app_version" json:"appVersion"` -} - -// K8sSthutrvalClusterControlPlaneConfigurationModel — вложенная модель для map-fixed параметра controlPlaneConfiguration. -type K8sSthutrvalClusterControlPlaneConfigurationModel struct { - SizingDisk types.Int64 `tfsdk:"sizing_disk" json:"sizingDisk"` - Count types.Int64 `tfsdk:"count" json:"count"` - SizingPolicy types.String `tfsdk:"sizing_policy" json:"sizingPolicy"` -} - -// K8sSthutrvalClusterStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sSthutrvalClusterStartupConfigurationModel struct { - ClusterName types.String `tfsdk:"cluster_name" json:"clusterName"` - VdcUid types.String `tfsdk:"vdc_uid" json:"vdcUid"` - NsxtUid types.String `tfsdk:"nsxt_uid" json:"nsxtUid"` -} - -type K8sSthutrvalClusterModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *K8sSthutrvalClusterAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *K8sSthutrvalClusterClusterConfigurationModel `tfsdk:"cluster_configuration"` - ControlPlaneConfiguration *K8sSthutrvalClusterControlPlaneConfigurationModel `tfsdk:"control_plane_configuration"` - StartupConfiguration *K8sSthutrvalClusterStartupConfigurationModel `tfsdk:"startup_configuration"` - WorkerConfiguration types.String `tfsdk:"worker_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sSthutrvalClusterResource() resource.Resource { - return &K8sSthutrvalClusterResource{} -} - -func (r *K8sSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster" -} - -func (r *K8sSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "need_external_address_ingress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к Ingress"}, - "access_ip_list_ingress": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, - "need_external_address_a_p_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false"}, - "access_ip_list_a_p_i": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.13.1")}, - }}, - "control_plane_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50), MarkdownDescription: "Дисковая политика берётся из услуги VDC, первый в списке"}, - "count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "sizing_policy": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр`"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cluster_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("example-vlzeyhxy")}, - "vdc_uid": schema.StringAttribute{Required: true}, - "nsxt_uid": schema.StringAttribute{Required: true}, - }}, - "worker_configuration": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]")}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 750: resources_core.BuildJSON(map[string]string{"clusterName": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ClusterName.ValueString()), "vdcUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.VdcUid.ValueString()), "nsxtUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.NsxtUid.ValueString())}), - 751: resources_core.BuildJSON(map[string]string{"sizingDisk": fmt.Sprintf("%d", config.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", config.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", config.ControlPlaneConfiguration.SizingPolicy.ValueString())}), - 752: resources_core.FormatString(config.WorkerConfiguration), - 753: resources_core.BuildJSON(map[string]string{"needExternalAddressIngress": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": config.AccessConfiguration.AccessIpListIngress.ValueString(), "needExternalAddressAPI": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": config.AccessConfiguration.AccessIpListAPI.ValueString()}), - 755: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", config.ClusterConfiguration.AppVersion.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 150, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 750: resources_core.BuildJSON(map[string]string{"clusterName": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ClusterName.ValueString()), "vdcUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.VdcUid.ValueString()), "nsxtUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.NsxtUid.ValueString())}), - 751: resources_core.BuildJSON(map[string]string{"sizingDisk": fmt.Sprintf("%d", data.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", data.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", data.ControlPlaneConfiguration.SizingPolicy.ValueString())}), - 752: resources_core.FormatString(data.WorkerConfiguration), - 753: resources_core.BuildJSON(map[string]string{"needExternalAddressIngress": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": data.AccessConfiguration.AccessIpListIngress.ValueString(), "needExternalAddressAPI": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": data.AccessConfiguration.AccessIpListAPI.ValueString()}), - 755: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", data.ClusterConfiguration.AppVersion.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 150, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 150, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sSthutrvalClusterModel - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.WorkerConfiguration.IsNull() != state.WorkerConfiguration.IsNull() || plan.WorkerConfiguration.IsUnknown() != state.WorkerConfiguration.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.WorkerConfiguration.IsNull() && !plan.WorkerConfiguration.IsUnknown() && !state.WorkerConfiguration.IsNull() && !state.WorkerConfiguration.IsUnknown() { - if !strings.EqualFold(plan.WorkerConfiguration.ValueString(), state.WorkerConfiguration.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 756: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", plan.ClusterConfiguration.AppVersion.ValueString())}), - 757: resources_core.BuildJSON(map[string]string{"sizingPolicy": fmt.Sprintf("\"%s\"", plan.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", plan.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", plan.ControlPlaneConfiguration.Count.ValueInt64())}), - 758: resources_core.FormatString(plan.WorkerConfiguration), - 759: resources_core.BuildJSON(map[string]string{"needExternalAddressIngress": fmt.Sprintf("%v", plan.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": plan.AccessConfiguration.AccessIpListIngress.ValueString(), "needExternalAddressAPI": fmt.Sprintf("%v", plan.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": plan.AccessConfiguration.AccessIpListAPI.ValueString()}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 150, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go b/provider/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go deleted file mode 100644 index 55faf0e..0000000 --- a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_sthutrval_cluster -// Subresource: user - -var _ resource.Resource = &K8sSthutrvalClusterUserResource{} - -// K8sSthutrvalClusterUserResource manages a subresource via operations. -type K8sSthutrvalClusterUserResource struct { - client *core.UniversalClient -} - -type K8sSthutrvalClusterUserModel struct { - ID types.String `tfsdk:"id"` - K8sSthutrvalClusterID types.String `tfsdk:"k8s_sthutrval_cluster_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Admins types.String `tfsdk:"admins"` -} - -func NewK8sSthutrvalClusterUserResource() resource.Resource { - return &K8sSthutrvalClusterUserResource{} -} - -func (r *K8sSthutrvalClusterUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster_user" -} - -func (r *K8sSthutrvalClusterUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_sthutrval_cluster_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "admins": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sSthutrvalClusterUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sSthutrvalClusterUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sSthutrvalClusterUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(state.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(state.Admins), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sSthutrvalClusterUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/151_k8s_openbao_kv_resource.go b/provider/internal/resources_gen/151_k8s_openbao_kv_resource.go deleted file mode 100644 index 773a5c2..0000000 --- a/provider/internal/resources_gen/151_k8s_openbao_kv_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Subresource: kv - -var _ resource.Resource = &K8sOpenbaoKvResource{} - -// K8sOpenbaoKvResource manages a subresource via operations. -type K8sOpenbaoKvResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoKvModel struct { - ID types.String `tfsdk:"id"` - K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KvName types.String `tfsdk:"kv_name"` -} - -func NewK8sOpenbaoKvResource() resource.Resource { - return &K8sOpenbaoKvResource{} -} - -func (r *K8sOpenbaoKvResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao_kv" -} - -func (r *K8sOpenbaoKvResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_openbao_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "kv_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("kvname-ldnwybqu"), MarkdownDescription: "Будет создан `v2 тип движка`

Уникальное в рамках услуги Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoKvResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sOpenbaoKvModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - } - identityCodes := []string{ - "kvName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("kv", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_kv", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_kv", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoKvResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoKvModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoKvResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoKvModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sOpenbaoKvModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoKvResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoKvModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_kv" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - } - identityCodes := []string{ - "kvName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("kv", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_kv", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sOpenbaoKvResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/151_k8s_openbao_policy_resource.go b/provider/internal/resources_gen/151_k8s_openbao_policy_resource.go deleted file mode 100644 index 668ed29..0000000 --- a/provider/internal/resources_gen/151_k8s_openbao_policy_resource.go +++ /dev/null @@ -1,355 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Subresource: policy - -var _ resource.Resource = &K8sOpenbaoPolicyResource{} - -// K8sOpenbaoPolicyResource manages a subresource via operations. -type K8sOpenbaoPolicyResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoPolicyModel struct { - ID types.String `tfsdk:"id"` - K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - PolicyName types.String `tfsdk:"policy_name"` - PolicySettings types.String `tfsdk:"policy_settings"` -} - -func NewK8sOpenbaoPolicyResource() resource.Resource { - return &K8sOpenbaoPolicyResource{} -} - -func (r *K8sOpenbaoPolicyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao_policy" -} - -func (r *K8sOpenbaoPolicyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_openbao_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "policy_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("policy-re1w8vbp"), MarkdownDescription: "Сделано для дальнейшего удобного использования Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "policy_settings": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoPolicyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.PolicySettings.IsNull() || plan.PolicySettings.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "policy_settings is required.") - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "policyName": resources_core.FormatString(plan.PolicyName), - "policySettings": resources_core.FormatString(plan.PolicySettings), - } - identityCodes := []string{ - "policyName", - "policySettings", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("policy", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "policySettings": resources_core.FormatString(plan.PolicySettings), - "policyName": resources_core.FormatString(plan.PolicyName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_policy", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_policy", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoPolicyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoPolicyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "policyName": resources_core.FormatString(plan.PolicyName), - "policySettings": resources_core.FormatString(plan.PolicySettings), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoPolicyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_policy" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "policyName": resources_core.FormatString(state.PolicyName), - "policySettings": resources_core.FormatString(state.PolicySettings), - } - identityCodes := []string{ - "policyName", - "policySettings", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("policy", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "policyName": resources_core.FormatString(state.PolicyName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_policy", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sOpenbaoPolicyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/151_k8s_openbao_resource.go b/provider/internal/resources_gen/151_k8s_openbao_resource.go deleted file mode 100644 index 821648e..0000000 --- a/provider/internal/resources_gen/151_k8s_openbao_resource.go +++ /dev/null @@ -1,468 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Service ID: 151 - -var _ resource.Resource = &K8sOpenbaoResource{} -var _ resource.ResourceWithModifyPlan = &K8sOpenbaoResource{} -var _ resource.ResourceWithImportState = &K8sOpenbaoResource{} - -type K8sOpenbaoResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AppVersion types.String `tfsdk:"app_version"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sOpenbaoResource() resource.Resource { - return &K8sOpenbaoResource{} -} - -func (r *K8sOpenbaoResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao" -} - -func (r *K8sOpenbaoResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.5.2"), MarkdownDescription: "Выбрать из списка"}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("bao-pasczlik"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `500`"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `10`"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3), MarkdownDescription: "Пример: `3`"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `2048`"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sOpenbaoModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sOpenbaoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sOpenbaoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 663: resources_core.FormatString(config.ResourceRealm), - 664: resources_core.FormatInt64(config.ResourceCPU), - 665: resources_core.FormatInt64(config.ResourceInstances), - 666: resources_core.FormatInt64(config.ResourceMemory), - 667: resources_core.FormatInt64(config.ResourceDisk), - 668: resources_core.FormatString(config.AppVersion), - 669: resources_core.FormatString(config.Domain), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 151, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sOpenbaoResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sOpenbaoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 151, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 663: resources_core.FormatString(data.ResourceRealm), - 664: resources_core.FormatInt64(data.ResourceCPU), - 665: resources_core.FormatInt64(data.ResourceInstances), - 666: resources_core.FormatInt64(data.ResourceMemory), - 667: resources_core.FormatInt64(data.ResourceDisk), - 668: resources_core.FormatString(data.AppVersion), - 669: resources_core.FormatString(data.Domain), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 151, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 151, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 151, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sOpenbaoResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoModel - var state K8sOpenbaoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { - if !strings.EqualFold(plan.AppVersion.ValueString(), state.AppVersion.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 670: resources_core.FormatInt64(plan.ResourceInstances), - 671: resources_core.FormatInt64(plan.ResourceCPU), - 672: resources_core.FormatInt64(plan.ResourceMemory), - 673: resources_core.FormatInt64(plan.ResourceDisk), - 674: resources_core.FormatString(plan.AppVersion), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 151, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sOpenbaoResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sOpenbaoResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/151_k8s_openbao_secrets_resource.go b/provider/internal/resources_gen/151_k8s_openbao_secrets_resource.go deleted file mode 100644 index 161164e..0000000 --- a/provider/internal/resources_gen/151_k8s_openbao_secrets_resource.go +++ /dev/null @@ -1,368 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Subresource: secrets - -var _ resource.Resource = &K8sOpenbaoSecretsResource{} - -// K8sOpenbaoSecretsResource manages a subresource via operations. -type K8sOpenbaoSecretsResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoSecretsModel struct { - ID types.String `tfsdk:"id"` - K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KvName types.String `tfsdk:"kv_name"` - SecretData types.String `tfsdk:"secret_data"` - SecretPath types.String `tfsdk:"secret_path"` -} - -func NewK8sOpenbaoSecretsResource() resource.Resource { - return &K8sOpenbaoSecretsResource{} -} - -func (r *K8sOpenbaoSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao_secrets" -} - -func (r *K8sOpenbaoSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_openbao_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "kv_name": schema.StringAttribute{Required: true, MarkdownDescription: "Выберите из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "secret_data": schema.StringAttribute{Required: true, MarkdownDescription: "Хотя бы один секрет необходимо добавить Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "secret_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("path/to/mysecret"), MarkdownDescription: "Пример: `path` или `road/to/path` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.KvName.IsNull() || plan.KvName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "kv_name is required.") - return - } - if plan.SecretData.IsNull() || plan.SecretData.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "secret_data is required.") - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - "secretData": resources_core.FormatString(plan.SecretData), - "secretPath": resources_core.FormatString(plan.SecretPath), - } - identityCodes := []string{ - "kvName", - "secretData", - "secretPath", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - "secretPath": resources_core.FormatString(plan.SecretPath), - "secretData": resources_core.FormatString(plan.SecretData), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - "secretData": resources_core.FormatString(plan.SecretData), - "secretPath": resources_core.FormatString(plan.SecretPath), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_secrets" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - "secretData": resources_core.FormatString(state.SecretData), - "secretPath": resources_core.FormatString(state.SecretPath), - } - identityCodes := []string{ - "kvName", - "secretData", - "secretPath", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - "secretPath": resources_core.FormatString(state.SecretPath), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_secrets", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sOpenbaoSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/153_nifi_resource.go b/provider/internal/resources_gen/153_nifi_resource.go deleted file mode 100644 index 12049ba..0000000 --- a/provider/internal/resources_gen/153_nifi_resource.go +++ /dev/null @@ -1,474 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nifi -// Service ID: 153 - -var _ resource.Resource = &NifiResource{} -var _ resource.ResourceWithModifyPlan = &NifiResource{} -var _ resource.ResourceWithImportState = &NifiResource{} - -type NifiResource struct { - client *core.UniversalClient -} - -type NifiModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AppVersion types.String `tfsdk:"app_version"` - Domain types.String `tfsdk:"domain"` - JdbcDrivers types.String `tfsdk:"jdbc_drivers"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNifiResource() resource.Resource { - return &NifiResource{} -} - -func (r *NifiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nifi" -} - -func (r *NifiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.6.0"), MarkdownDescription: "Выбрать из списка Не изменяется после создания."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nifi-lvu4xj7j"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "jdbc_drivers": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `https://jdbc.postgresql.org/download/postgresql-42.7.8.jar` Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Пример: `2000` Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `10` Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1` Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(4096), MarkdownDescription: "Пример: `2048` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NifiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NifiModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.AppVersion.ValueString(), state.AppVersion.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить app_version", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.JdbcDrivers.IsNull() && !plan.JdbcDrivers.IsUnknown() && !state.JdbcDrivers.IsNull() && !state.JdbcDrivers.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.JdbcDrivers.ValueString(), state.JdbcDrivers.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить jdbc_drivers", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.JdbcDrivers.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "jdbc_drivers is required.") - return - } - if config.JdbcDrivers.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 711: resources_core.FormatString(config.Domain), - 712: resources_core.FormatInt64(config.ResourceCPU), - 713: resources_core.FormatInt64(config.ResourceMemory), - 714: resources_core.FormatInt64(config.ResourceInstances), - 715: resources_core.FormatInt64(config.ResourceDisk), - 716: resources_core.FormatString(config.AppVersion), - 717: resources_core.FormatString(config.ResourceRealm), - 718: resources_core.FormatString(config.JdbcDrivers), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 153, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NifiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.JdbcDrivers.IsNull() || data.JdbcDrivers.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "jdbc_drivers is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 153, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 711: resources_core.FormatString(data.Domain), - 712: resources_core.FormatInt64(data.ResourceCPU), - 713: resources_core.FormatInt64(data.ResourceMemory), - 714: resources_core.FormatInt64(data.ResourceInstances), - 715: resources_core.FormatInt64(data.ResourceDisk), - 716: resources_core.FormatString(data.AppVersion), - 717: resources_core.FormatString(data.ResourceRealm), - 718: resources_core.FormatString(data.JdbcDrivers), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 153, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 153, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "jdbcDrivers", Field: "JdbcDrivers", Type: "array-map-fixed"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NifiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 153, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "jdbcDrivers", Field: "JdbcDrivers", Type: "array-map-fixed"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NifiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NifiModel - var state NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 153, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "jdbcDrivers", Field: "JdbcDrivers", Type: "array-map-fixed"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NifiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NifiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NifiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/163_llm_ai_resource.go b/provider/internal/resources_gen/163_llm_ai_resource.go deleted file mode 100644 index 22a1d70..0000000 --- a/provider/internal/resources_gen/163_llm_ai_resource.go +++ /dev/null @@ -1,345 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Service ID: 163 - -var _ resource.Resource = &LlmAiResource{} -var _ resource.ResourceWithModifyPlan = &LlmAiResource{} -var _ resource.ResourceWithImportState = &LlmAiResource{} - -type LlmAiResource struct { - client *core.UniversalClient -} - -type LlmAiModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewLlmAiResource() resource.Resource { - return &LlmAiResource{} -} - -func (r *LlmAiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai" -} - -func (r *LlmAiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("llmAi.aillm.ru"), MarkdownDescription: "Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *LlmAiModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 826: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 163, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *LlmAiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 163, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 826: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 163, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 163, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 163, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *LlmAiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiModel - var state LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 163, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *LlmAiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *LlmAiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/163_llm_ai_secrets_resource.go b/provider/internal/resources_gen/163_llm_ai_secrets_resource.go deleted file mode 100644 index 0525342..0000000 --- a/provider/internal/resources_gen/163_llm_ai_secrets_resource.go +++ /dev/null @@ -1,401 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Subresource: secrets - -var _ resource.Resource = &LlmAiSecretsResource{} - -// LlmAiSecretsResource manages a subresource via operations. -type LlmAiSecretsResource struct { - client *core.UniversalClient -} - -type LlmAiSecretsModel struct { - ID types.String `tfsdk:"id"` - LlmAiID types.String `tfsdk:"llm_ai_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KeyName types.String `tfsdk:"key_name"` - Models types.String `tfsdk:"models"` - RpmLimit types.Int64 `tfsdk:"rpm_limit"` - TeamName types.String `tfsdk:"team_name"` - TpmLimit types.Int64 `tfsdk:"tpm_limit"` - VariablesKeys types.String `tfsdk:"variables_keys"` -} - -func NewLlmAiSecretsResource() resource.Resource { - return &LlmAiSecretsResource{} -} - -func (r *LlmAiSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai_secrets" -} - -func (r *LlmAiSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "llm_ai_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "key_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "models": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "rpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(60), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()}}, - "team_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "tpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100000), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()}}, - "variables_keys": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan LlmAiSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.KeyName.IsNull() || plan.KeyName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "key_name is required.") - return - } - if plan.Models.IsNull() || plan.Models.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "models is required.") - return - } - if plan.TeamName.IsNull() || plan.TeamName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "team_name is required.") - return - } - if plan.VariablesKeys.IsNull() || plan.VariablesKeys.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "variables_keys is required.") - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "keyName": resources_core.FormatString(plan.KeyName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "teamName": resources_core.FormatString(plan.TeamName), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - "variablesKeys": resources_core.FormatString(plan.VariablesKeys), - } - identityCodes := []string{ - "keyName", - "models", - "rpmLimit", - "teamName", - "tpmLimit", - "variablesKeys", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - "keyName": resources_core.FormatString(plan.KeyName), - "teamName": resources_core.FormatString(plan.TeamName), - "variablesKeys": resources_core.FormatString(plan.VariablesKeys), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "keyName": resources_core.FormatString(plan.KeyName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "teamName": resources_core.FormatString(plan.TeamName), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - "variablesKeys": resources_core.FormatString(plan.VariablesKeys), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_secrets" == "" { - return - } - - instanceUID := strings.TrimSpace(state.LlmAiID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "keyName": resources_core.FormatString(state.KeyName), - "models": resources_core.FormatString(state.Models), - "rpmLimit": resources_core.FormatInt64(state.RpmLimit), - "teamName": resources_core.FormatString(state.TeamName), - "tpmLimit": resources_core.FormatInt64(state.TpmLimit), - "variablesKeys": resources_core.FormatString(state.VariablesKeys), - } - identityCodes := []string{ - "keyName", - "models", - "rpmLimit", - "teamName", - "tpmLimit", - "variablesKeys", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "keyName": resources_core.FormatString(state.KeyName), - "teamName": resources_core.FormatString(state.TeamName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_secrets", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *LlmAiSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/163_llm_ai_team_resource.go b/provider/internal/resources_gen/163_llm_ai_team_resource.go deleted file mode 100644 index e411362..0000000 --- a/provider/internal/resources_gen/163_llm_ai_team_resource.go +++ /dev/null @@ -1,365 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Subresource: team - -var _ resource.Resource = &LlmAiTeamResource{} - -// LlmAiTeamResource manages a subresource via operations. -type LlmAiTeamResource struct { - client *core.UniversalClient -} - -type LlmAiTeamModel struct { - ID types.String `tfsdk:"id"` - LlmAiID types.String `tfsdk:"llm_ai_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Models types.String `tfsdk:"models"` - RpmLimit types.Int64 `tfsdk:"rpm_limit"` - TeamName types.String `tfsdk:"team_name"` - TpmLimit types.Int64 `tfsdk:"tpm_limit"` -} - -func NewLlmAiTeamResource() resource.Resource { - return &LlmAiTeamResource{} -} - -func (r *LlmAiTeamResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai_team" -} - -func (r *LlmAiTeamResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "llm_ai_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "models": schema.StringAttribute{Required: true}, - "rpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(60)}, - "team_name": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "tpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100000)}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiTeamResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan LlmAiTeamModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Models.IsNull() || plan.Models.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "models is required.") - return - } - if plan.TeamName.IsNull() || plan.TeamName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "team_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - } - identityCodes := []string{ - "teamName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("team", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_team", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_team", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiTeamResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiTeamResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiTeamModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_team" == "" { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - }) - - idParams := map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_team", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiTeamResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_team" == "" { - return - } - - instanceUID := strings.TrimSpace(state.LlmAiID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "teamName": resources_core.FormatString(state.TeamName), - } - identityCodes := []string{ - "teamName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("team", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(state.TeamName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_team", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *LlmAiTeamResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/175_k8s_go_resource.go b/provider/internal/resources_gen/175_k8s_go_resource.go deleted file mode 100644 index f6cc30f..0000000 --- a/provider/internal/resources_gen/175_k8s_go_resource.go +++ /dev/null @@ -1,422 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_go -// Service ID: 175 - -var _ resource.Resource = &K8sGoResource{} -var _ resource.ResourceWithModifyPlan = &K8sGoResource{} -var _ resource.ResourceWithImportState = &K8sGoResource{} - -type K8sGoResource struct { - client *core.UniversalClient -} - -// K8sGoAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type K8sGoAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// K8sGoAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type K8sGoAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// K8sGoClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sGoClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// K8sGoStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sGoStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type K8sGoModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *K8sGoAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *K8sGoAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *K8sGoClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *K8sGoStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sGoResource() resource.Resource { - return &K8sGoResource{} -} - -func (r *K8sGoResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_go" -} - -func (r *K8sGoResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("lucee-utovgtao"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("6.0")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testlucee"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sGoResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sGoModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sGoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sGoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1046: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1047: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1048: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1049: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1050: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 175, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sGoResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sGoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 175, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1046: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1047: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1048: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1049: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1050: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 175, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 175, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sGoResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sGoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 175, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sGoResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sGoModel - var state K8sGoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1051: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1052: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1053: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 175, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sGoResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sGoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sGoResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sGoResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/19_vc_org_resource.go b/provider/internal/resources_gen/19_vc_org_resource.go deleted file mode 100644 index c2e768b..0000000 --- a/provider/internal/resources_gen/19_vc_org_resource.go +++ /dev/null @@ -1,391 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org -// Service ID: 19 - -var _ resource.Resource = &VcOrgResource{} -var _ resource.ResourceWithModifyPlan = &VcOrgResource{} -var _ resource.ResourceWithImportState = &VcOrgResource{} - -type VcOrgResource struct { - client *core.UniversalClient -} - -type VcOrgModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - OrganizationType types.String `tfsdk:"organization_type"` - ResourceRealm types.String `tfsdk:"resource_realm"` - VIPConfigure types.String `tfsdk:"v_i_p_configure"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcOrgResource() resource.Resource { - return &VcOrgResource{} -} - -func (r *VcOrgResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org" -} - -func (r *VcOrgResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "organization_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("iaas"), MarkdownDescription: "Доступ к организации предоставляется только при выборе `iaas` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Облако в котором будет развёрнут тенант Не изменяется после создания."}, - "v_i_p_configure": schema.StringAttribute{Required: true}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcOrgModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.OrganizationType.IsNull() && !plan.OrganizationType.IsUnknown() && !state.OrganizationType.IsNull() && !state.OrganizationType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.OrganizationType.ValueString(), state.OrganizationType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить organization_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VIPConfigure.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") - return - } - if config.VIPConfigure.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 418: resources_core.FormatString(config.ResourceRealm), - 556: resources_core.FormatString(config.OrganizationType), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 19, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcOrgResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VIPConfigure.IsNull() || data.VIPConfigure.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 418: resources_core.FormatString(data.ResourceRealm), - 556: resources_core.FormatString(data.OrganizationType), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 19, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 19, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcOrgResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgModel - var state VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VIPConfigure.IsNull() != state.VIPConfigure.IsNull() || plan.VIPConfigure.IsUnknown() != state.VIPConfigure.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VIPConfigure.IsNull() && !plan.VIPConfigure.IsUnknown() && !state.VIPConfigure.IsNull() && !state.VIPConfigure.IsUnknown() { - if !strings.EqualFold(plan.VIPConfigure.ValueString(), state.VIPConfigure.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 662: resources_core.FormatString(plan.VIPConfigure), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 19, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcOrgResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcOrgResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/19_vc_org_user_resource.go b/provider/internal/resources_gen/19_vc_org_user_resource.go deleted file mode 100644 index b18fcdd..0000000 --- a/provider/internal/resources_gen/19_vc_org_user_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org -// Subresource: user - -var _ resource.Resource = &VcOrgUserResource{} - -// VcOrgUserResource manages a subresource via operations. -type VcOrgUserResource struct { - client *core.UniversalClient -} - -type VcOrgUserModel struct { - ID types.String `tfsdk:"id"` - VcOrgID types.String `tfsdk:"vc_org_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Admins types.String `tfsdk:"admins"` -} - -func NewVcOrgUserResource() resource.Resource { - return &VcOrgUserResource{} -} - -func (r *VcOrgUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org_user" -} - -func (r *VcOrgUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "vc_org_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "admins": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VcOrgUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcOrgUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcOrgUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.VcOrgID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(state.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(state.Admins), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *VcOrgUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/1_dummy_resource.go b/provider/internal/resources_gen/1_dummy_resource.go deleted file mode 100644 index e2070da..0000000 --- a/provider/internal/resources_gen/1_dummy_resource.go +++ /dev/null @@ -1,582 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dummy -// Service ID: 1 - -var _ resource.Resource = &DummyResource{} -var _ resource.ResourceWithModifyPlan = &DummyResource{} -var _ resource.ResourceWithImportState = &DummyResource{} - -type DummyResource struct { - client *core.UniversalClient -} - -// DummyMapExampleModel — вложенная модель для map-fixed параметра mapExample. -type DummyMapExampleModel struct { - Subparam1 types.String `tfsdk:"subparam1" json:"subparam1"` - Secret types.String `tfsdk:"secret" json:"secret"` - Subparam2 types.String `tfsdk:"subparam2" json:"subparam2"` -} - -// DummyMapFixedModel — вложенная модель для map-fixed параметра mapFixed. -type DummyMapFixedModel struct { - Bol1 types.Bool `tfsdk:"bol1" json:"bol1"` - MinStr1 types.String `tfsdk:"min_str1" json:"minStr1"` - Param1 types.Int64 `tfsdk:"param1" json:"param1"` - Param2 types.String `tfsdk:"param2" json:"param2"` - Param3 types.Int64 `tfsdk:"param3" json:"param3"` -} - -type DummyModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Arr types.String `tfsdk:"arr"` - ArrayMapFixedExample types.String `tfsdk:"array_map_fixed_example"` - Bodymessage types.String `tfsdk:"bodymessage"` - DurationMs types.Int64 `tfsdk:"duration_ms"` - FailAtStart types.Bool `tfsdk:"fail_at_start"` - FailInProgress types.Bool `tfsdk:"fail_in_progress"` - JsonExample types.String `tfsdk:"json_example"` - MapExample *DummyMapExampleModel `tfsdk:"map_example"` - MapFixed *DummyMapFixedModel `tfsdk:"map_fixed"` - NestedRefExample types.String `tfsdk:"nested_ref_example"` - ResourceRealm types.String `tfsdk:"resource_realm"` - WhereFail types.Int64 `tfsdk:"where_fail"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDummyResource() resource.Resource { - return &DummyResource{} -} - -func (r *DummyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dummy" -} - -func (r *DummyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "arr": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "array_map_fixed_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Array of map-fixed Не изменяется после создания."}, - "bodymessage": schema.StringAttribute{Optional: true, MarkdownDescription: "Дата, уходящая в Vault"}, - "duration_ms": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "перед завершением подождать (sleep) столько миллисекунд"}, - "fail_at_start": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, завершить джоб, ничего не записывая в cmdb"}, - "fail_in_progress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb"}, - "json_example": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для тестирования интерфейса", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "map_example": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "subparam1": schema.StringAttribute{Optional: true, MarkdownDescription: "это для администратора"}, - "secret": schema.StringAttribute{Optional: true, MarkdownDescription: "это секрет"}, - "subparam2": schema.StringAttribute{Optional: true}, - }}, - "map_fixed": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "bol1": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Булевский, обязательный"}, - "min_str1": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("abracadabra"), MarkdownDescription: "не меньше 10 символов"}, - "param1": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Integer, secret"}, - "param2": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Дескрипшен"}, - "param3": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "целое положительное"}, - }}, - "nested_ref_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("dummy"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "where_fail": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write"}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DummyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DummyModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Arr.IsNull() && !plan.Arr.IsUnknown() && !state.Arr.IsNull() && !state.Arr.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Arr.ValueString(), state.Arr.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить arr", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ArrayMapFixedExample.IsNull() && !plan.ArrayMapFixedExample.IsUnknown() && !state.ArrayMapFixedExample.IsNull() && !state.ArrayMapFixedExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ArrayMapFixedExample.ValueString(), state.ArrayMapFixedExample.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить array_map_fixed_example", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NestedRefExample.IsNull() && !plan.NestedRefExample.IsUnknown() && !state.NestedRefExample.IsNull() && !state.NestedRefExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NestedRefExample.ValueString(), state.NestedRefExample.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить nested_ref_example", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Arr.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } - if config.Arr.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 198: resources_core.FormatInt64(config.DurationMs), - 199: resources_core.FormatBool(config.FailAtStart), - 200: resources_core.FormatBool(config.FailInProgress), - 201: resources_core.FormatInt64(config.WhereFail), - 242: resources_core.FormatString(config.ResourceRealm), - 286: resources_core.FormatString(config.Bodymessage), - 321: resources_core.BuildJSON(map[string]string{"subparam1": fmt.Sprintf("\"%s\"", config.MapExample.Subparam1.ValueString()), "secret": fmt.Sprintf("\"%s\"", config.MapExample.Secret.ValueString()), "subparam2": fmt.Sprintf("\"%s\"", config.MapExample.Subparam2.ValueString())}), - 322: resources_core.FormatString(config.JsonExample), - 396: resources_core.FormatString(config.NestedRefExample), - 647: resources_core.BuildJSON(map[string]string{"bol1": fmt.Sprintf("%v", config.MapFixed.Bol1.ValueBool()), "minStr1": fmt.Sprintf("\"%s\"", config.MapFixed.MinStr1.ValueString()), "param1": fmt.Sprintf("%d", config.MapFixed.Param1.ValueInt64()), "param2": fmt.Sprintf("\"%s\"", config.MapFixed.Param2.ValueString()), "param3": fmt.Sprintf("%d", config.MapFixed.Param3.ValueInt64())}), - 654: resources_core.FormatString(config.ArrayMapFixedExample), - 863: resources_core.FormatString(config.Arr), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 1, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Arr.IsNull() || data.Arr.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 198: resources_core.FormatInt64(data.DurationMs), - 199: resources_core.FormatBool(data.FailAtStart), - 200: resources_core.FormatBool(data.FailInProgress), - 201: resources_core.FormatInt64(data.WhereFail), - 242: resources_core.FormatString(data.ResourceRealm), - 286: resources_core.FormatString(data.Bodymessage), - 321: resources_core.BuildJSON(map[string]string{"subparam1": fmt.Sprintf("\"%s\"", data.MapExample.Subparam1.ValueString()), "secret": fmt.Sprintf("\"%s\"", data.MapExample.Secret.ValueString()), "subparam2": fmt.Sprintf("\"%s\"", data.MapExample.Subparam2.ValueString())}), - 322: resources_core.FormatString(data.JsonExample), - 396: resources_core.FormatString(data.NestedRefExample), - 647: resources_core.BuildJSON(map[string]string{"bol1": fmt.Sprintf("%v", data.MapFixed.Bol1.ValueBool()), "minStr1": fmt.Sprintf("\"%s\"", data.MapFixed.MinStr1.ValueString()), "param1": fmt.Sprintf("%d", data.MapFixed.Param1.ValueInt64()), "param2": fmt.Sprintf("\"%s\"", data.MapFixed.Param2.ValueString()), "param3": fmt.Sprintf("%d", data.MapFixed.Param3.ValueInt64())}), - 654: resources_core.FormatString(data.ArrayMapFixedExample), - 863: resources_core.FormatString(data.Arr), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 1, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DummyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 1, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DummyModel - var state DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.DurationMs.IsNull() != state.DurationMs.IsNull() || plan.DurationMs.IsUnknown() != state.DurationMs.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.DurationMs.IsNull() && !plan.DurationMs.IsUnknown() && !state.DurationMs.IsNull() && !state.DurationMs.IsUnknown() { - if plan.DurationMs.ValueInt64() != state.DurationMs.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.FailAtStart.IsNull() != state.FailAtStart.IsNull() || plan.FailAtStart.IsUnknown() != state.FailAtStart.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.FailAtStart.IsNull() && !plan.FailAtStart.IsUnknown() && !state.FailAtStart.IsNull() && !state.FailAtStart.IsUnknown() { - if plan.FailAtStart.ValueBool() != state.FailAtStart.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.FailInProgress.IsNull() != state.FailInProgress.IsNull() || plan.FailInProgress.IsUnknown() != state.FailInProgress.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.FailInProgress.IsNull() && !plan.FailInProgress.IsUnknown() && !state.FailInProgress.IsNull() && !state.FailInProgress.IsUnknown() { - if plan.FailInProgress.ValueBool() != state.FailInProgress.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.WhereFail.IsNull() != state.WhereFail.IsNull() || plan.WhereFail.IsUnknown() != state.WhereFail.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.WhereFail.IsNull() && !plan.WhereFail.IsUnknown() && !state.WhereFail.IsNull() && !state.WhereFail.IsUnknown() { - if plan.WhereFail.ValueInt64() != state.WhereFail.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.Bodymessage.IsNull() != state.Bodymessage.IsNull() || plan.Bodymessage.IsUnknown() != state.Bodymessage.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Bodymessage.IsNull() && !plan.Bodymessage.IsUnknown() && !state.Bodymessage.IsNull() && !state.Bodymessage.IsUnknown() { - if !strings.EqualFold(plan.Bodymessage.ValueString(), state.Bodymessage.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.JsonExample.IsNull() != state.JsonExample.IsNull() || plan.JsonExample.IsUnknown() != state.JsonExample.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonExample.IsNull() && !plan.JsonExample.IsUnknown() && !state.JsonExample.IsNull() && !state.JsonExample.IsUnknown() { - if !strings.EqualFold(plan.JsonExample.ValueString(), state.JsonExample.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 287: resources_core.FormatInt64(plan.DurationMs), - 288: resources_core.FormatBool(plan.FailAtStart), - 289: resources_core.FormatBool(plan.FailInProgress), - 290: resources_core.FormatInt64(plan.WhereFail), - 291: resources_core.FormatString(plan.Bodymessage), - 373: resources_core.BuildJSON(map[string]string{"secret": fmt.Sprintf("\"%s\"", plan.MapExample.Secret.ValueString())}), - 374: resources_core.FormatString(plan.JsonExample), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 1, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DummyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DummyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DummyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/20_vc_org_saas_resource.go b/provider/internal/resources_gen/20_vc_org_saas_resource.go deleted file mode 100644 index 83cdd6e..0000000 --- a/provider/internal/resources_gen/20_vc_org_saas_resource.go +++ /dev/null @@ -1,348 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org_saas -// Service ID: 20 - -var _ resource.Resource = &VcOrgSaasResource{} -var _ resource.ResourceWithModifyPlan = &VcOrgSaasResource{} -var _ resource.ResourceWithImportState = &VcOrgSaasResource{} - -type VcOrgSaasResource struct { - client *core.UniversalClient -} - -type VcOrgSaasModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcOrgSaasResource() resource.Resource { - return &VcOrgSaasResource{} -} - -func (r *VcOrgSaasResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org_saas" -} - -func (r *VcOrgSaasResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgSaasResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcOrgSaasModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 185: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 20, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcOrgSaasResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 185: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 20, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgSaasResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 20, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcOrgSaasResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgSaasModel - var state VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 20, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgSaasResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcOrgSaasResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcOrgSaasResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/21_vc_vdc_resource.go b/provider/internal/resources_gen/21_vc_vdc_resource.go deleted file mode 100644 index 21d0d3d..0000000 --- a/provider/internal/resources_gen/21_vc_vdc_resource.go +++ /dev/null @@ -1,573 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc -// Service ID: 21 - -var _ resource.Resource = &VcVdcResource{} -var _ resource.ResourceWithModifyPlan = &VcVdcResource{} -var _ resource.ResourceWithImportState = &VcVdcResource{} - -type VcVdcResource struct { - client *core.UniversalClient -} - -type VcVdcModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - CpuAllocated types.Int64 `tfsdk:"cpu_allocated"` - CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` - MemAllocated types.Int64 `tfsdk:"mem_allocated"` - NetworkProvider types.String `tfsdk:"network_provider"` - OrganizationUid types.String `tfsdk:"organization_uid"` - ProviderVdc types.String `tfsdk:"provider_vdc"` - StorageConfig types.String `tfsdk:"storage_config"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVdcResource() resource.Resource { - return &VcVdcResource{} -} - -func (r *VcVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc" -} - -func (r *VcVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "cpu_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(80), MarkdownDescription: "шт"}, - "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "% резервирования Не изменяется после создания."}, - "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "gb"}, - "network_provider": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "organization_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` Не изменяется после создания."}, - "provider_vdc": schema.StringAttribute{Required: true, MarkdownDescription: "Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
Не изменяется после создания."}, - "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Первая политика в списке указывается как "По умолчанию""}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVdcModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { - if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NetworkProvider.IsNull() && !plan.NetworkProvider.IsUnknown() && !state.NetworkProvider.IsNull() && !state.NetworkProvider.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NetworkProvider.ValueString(), state.NetworkProvider.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить network_provider", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.OrganizationUid.ValueString(), state.OrganizationUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить organization_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ProviderVdc.IsNull() && !plan.ProviderVdc.IsUnknown() && !state.ProviderVdc.IsNull() && !state.ProviderVdc.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ProviderVdc.ValueString(), state.ProviderVdc.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить provider_vdc", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.NetworkProvider.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") - return - } - if config.NetworkProvider.IsUnknown() { - return - } - if config.OrganizationUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") - return - } - if config.OrganizationUid.IsUnknown() { - return - } - if config.ProviderVdc.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") - return - } - if config.ProviderVdc.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.OrganizationUid.IsNull() && !config.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, config.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != config.OrganizationUid.ValueString() { - config.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - params := map[int]string{ - 30: resources_core.FormatString(config.OrganizationUid), - 335: resources_core.FormatString(config.ProviderVdc), - 361: resources_core.FormatString(config.StorageConfig), - 397: resources_core.FormatInt64(config.CpuGuaranteed), - 557: resources_core.FormatInt64(config.CpuAllocated), - 558: resources_core.FormatInt64(config.MemAllocated), - 746: resources_core.FormatString(config.NetworkProvider), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 21, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.NetworkProvider.IsNull() || data.NetworkProvider.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") - return - } - if data.OrganizationUid.IsNull() || data.OrganizationUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") - return - } - if data.ProviderVdc.IsNull() || data.ProviderVdc.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalOrganizationUid := data.OrganizationUid - if !data.OrganizationUid.IsNull() && !data.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, data.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedOrganizationUid != "" && resolvedOrganizationUid != data.OrganizationUid.ValueString() { - data.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 30: resources_core.FormatString(data.OrganizationUid), - 335: resources_core.FormatString(data.ProviderVdc), - 361: resources_core.FormatString(data.StorageConfig), - 397: resources_core.FormatInt64(data.CpuGuaranteed), - 557: resources_core.FormatInt64(data.CpuAllocated), - 558: resources_core.FormatInt64(data.MemAllocated), - 746: resources_core.FormatString(data.NetworkProvider), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 21, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { - state.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalOrganizationUid.IsNull() && !originalOrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - if strings.EqualFold(state.OrganizationUid.ValueString(), originalOrganizationUid.ValueString()) { - state.OrganizationUid = originalOrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 21, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, newState.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != newState.OrganizationUid.ValueString() { - newState.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() && !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { - if strings.EqualFold(newState.OrganizationUid.ValueString(), state.OrganizationUid.ValueString()) { - newState.OrganizationUid = state.OrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcModel - var state VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.CpuAllocated.IsNull() != state.CpuAllocated.IsNull() || plan.CpuAllocated.IsUnknown() != state.CpuAllocated.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { - if plan.CpuAllocated.ValueInt64() != state.CpuAllocated.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MemAllocated.IsNull() != state.MemAllocated.IsNull() || plan.MemAllocated.IsUnknown() != state.MemAllocated.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { - if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.StorageConfig.IsNull() != state.StorageConfig.IsNull() || plan.StorageConfig.IsUnknown() != state.StorageConfig.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { - if !strings.EqualFold(plan.StorageConfig.ValueString(), state.StorageConfig.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 560: resources_core.FormatInt64(plan.CpuAllocated), - 561: resources_core.FormatInt64(plan.MemAllocated), - 562: resources_core.FormatString(plan.StorageConfig), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 21, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { - state.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - if strings.EqualFold(state.OrganizationUid.ValueString(), plan.OrganizationUid.ValueString()) { - state.OrganizationUid = plan.OrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVdcResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/22_vc_nsxt_resource.go b/provider/internal/resources_gen/22_vc_nsxt_resource.go deleted file mode 100644 index 58189cc..0000000 --- a/provider/internal/resources_gen/22_vc_nsxt_resource.go +++ /dev/null @@ -1,614 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_nsxt -// Service ID: 22 - -var _ resource.Resource = &VcNsxtResource{} -var _ resource.ResourceWithModifyPlan = &VcNsxtResource{} -var _ resource.ResourceWithImportState = &VcNsxtResource{} - -type VcNsxtResource struct { - client *core.UniversalClient -} - -type VcNsxtModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` - QosProfile types.String `tfsdk:"qos_profile"` - VdcGroupUid types.String `tfsdk:"vdc_group_uid"` - VdcType types.String `tfsdk:"vdc_type"` - VdcUid types.String `tfsdk:"vdc_uid"` - VirtualServicesCount types.Int64 `tfsdk:"virtual_services_count"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcNsxtResource() resource.Resource { - return &VcNsxtResource{} -} - -func (r *VcNsxtResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_nsxt" -} - -func (r *VcNsxtResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "ip_space_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ip Space для внешнего IP"}, - "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включение Load Balancer"}, - "qos_profile": schema.StringAttribute{Optional: true, MarkdownDescription: "Параметр пока не работает
Должен возвращать поле из ресурсной платформы типа vc из .vcd.hardware.gatewayQoSProfiles

Как по идее должен работать.
Нужно заполнить или CFS vdcUid, или vdcGroupUid
Идеально конечно проверять `if (params.vdcType == "vdc" && params.vdcUid != "")` или `if (params.vdcType == "vdcGroup" && params.vdcGroupUid != "")`

Далее надо пойти по стейту vdc -> org -> resPlatform, взять gatewayQoSProfiles
Или пойти по стейту vdcgroup -> vdc -> org -> resPlatform, взять gatewayQoSProfiles

Регулярку могу написать (Виталя)"}, - "vdc_group_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Группа виртуальных датацентров (groupvDC)` Не изменяется после создания."}, - "vdc_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vdc"), MarkdownDescription: "Тип родительской услуги Не изменяется после создания."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, - "virtual_services_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Кол-во виртуальных сервисов, которые **резервируются** на AVI"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcNsxtResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcNsxtModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcGroupUid.ValueString(), state.VdcGroupUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_group_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcType.IsNull() && !plan.VdcType.IsUnknown() && !state.VdcType.IsNull() && !state.VdcType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcType.ValueString(), state.VdcType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VdcGroupUid.IsNull() && !config.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, config.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != config.VdcGroupUid.ValueString() { - config.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 8: resources_core.FormatString(config.VdcUid), - 340: resources_core.FormatBool(config.NeedEnableAVI), - 341: resources_core.FormatInt64(config.VirtualServicesCount), - 621: resources_core.FormatString(config.VdcType), - 622: resources_core.FormatString(config.VdcGroupUid), - 825: resources_core.FormatString(config.QosProfile), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 22, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcNsxtResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcGroupUid := data.VdcGroupUid - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.VdcGroupUid.IsNull() && !data.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, data.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != data.VdcGroupUid.ValueString() { - data.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 8: resources_core.FormatString(data.VdcUid), - 340: resources_core.FormatBool(data.NeedEnableAVI), - 341: resources_core.FormatInt64(data.VirtualServicesCount), - 621: resources_core.FormatString(data.VdcType), - 622: resources_core.FormatString(data.VdcGroupUid), - 825: resources_core.FormatString(data.QosProfile), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 22, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { - state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVdcGroupUid.IsNull() && !originalVdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - if strings.EqualFold(state.VdcGroupUid.ValueString(), originalVdcGroupUid.ValueString()) { - state.VdcGroupUid = originalVdcGroupUid - } - } - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNsxtResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 22, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, newState.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != newState.VdcGroupUid.ValueString() { - newState.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() && !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { - if strings.EqualFold(newState.VdcGroupUid.ValueString(), state.VdcGroupUid.ValueString()) { - newState.VdcGroupUid = state.VdcGroupUid - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcNsxtResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcNsxtModel - var state VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.NeedEnableAVI.IsNull() != state.NeedEnableAVI.IsNull() || plan.NeedEnableAVI.IsUnknown() != state.NeedEnableAVI.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { - if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VirtualServicesCount.IsNull() != state.VirtualServicesCount.IsNull() || plan.VirtualServicesCount.IsUnknown() != state.VirtualServicesCount.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { - if plan.VirtualServicesCount.ValueInt64() != state.VirtualServicesCount.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.QosProfile.IsNull() != state.QosProfile.IsNull() || plan.QosProfile.IsUnknown() != state.QosProfile.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.QosProfile.IsNull() && !plan.QosProfile.IsUnknown() && !state.QosProfile.IsNull() && !state.QosProfile.IsUnknown() { - if !strings.EqualFold(plan.QosProfile.ValueString(), state.QosProfile.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 368: resources_core.FormatBool(plan.NeedEnableAVI), - 369: resources_core.FormatInt64(plan.VirtualServicesCount), - 372: resources_core.FormatString(plan.IpSpaceName), - 856: resources_core.FormatString(plan.QosProfile), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 22, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { - state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - if strings.EqualFold(state.VdcGroupUid.ValueString(), plan.VdcGroupUid.ValueString()) { - state.VdcGroupUid = plan.VdcGroupUid - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNsxtResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcNsxtResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcNsxtResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/23_vc_vm_resource.go b/provider/internal/resources_gen/23_vc_vm_resource.go deleted file mode 100644 index d39e2e1..0000000 --- a/provider/internal/resources_gen/23_vc_vm_resource.go +++ /dev/null @@ -1,581 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm -// Service ID: 23 - -var _ resource.Resource = &VcVmResource{} -var _ resource.ResourceWithModifyPlan = &VcVmResource{} -var _ resource.ResourceWithImportState = &VcVmResource{} - -type VcVmResource struct { - client *core.UniversalClient -} - -type VcVmModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessIpList types.String `tfsdk:"access_ip_list"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - TemplateName types.String `tfsdk:"template_name"` - VappUid types.String `tfsdk:"vapp_uid"` - VmCpu types.String `tfsdk:"vm_cpu"` - VmDisksize types.String `tfsdk:"vm_disksize"` - VmName types.String `tfsdk:"vm_name"` - VmRam types.String `tfsdk:"vm_ram"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmResource() resource.Resource { - return &VcVmResource{} -} - -func (r *VcVmResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm" -} - -func (r *VcVmResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("79.111.119.78/32 185.247.187.1/32"), MarkdownDescription: "Вставить содержимое из 'curl ifconfig.io'
IP адреса в CIDR формате через пробел для настройки аксес листов"}, - "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Брать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно) Не изменяется после создания."}, - "template_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("RockyLinux_9-16G"), MarkdownDescription: "ОС Не изменяется после создания."}, - "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "InstanceUid vApp. vApp должен быть создан Не изменяется после создания."}, - "vm_cpu": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "CPU"}, - "vm_disksize": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("10"), MarkdownDescription: "Размер диска под сервис (GB)"}, - "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vm0001"), MarkdownDescription: "Имя ВМ. Не поддерживает спец-символы Не изменяется после создания."}, - "vm_ram": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "RAM (GB)"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.TemplateName.IsNull() && !plan.TemplateName.IsUnknown() && !state.TemplateName.IsNull() && !state.TemplateName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.TemplateName.ValueString(), state.TemplateName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить template_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappUid.ValueString(), state.VappUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmName.ValueString(), state.VmName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.VappUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - if config.VappUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { - config.VappUid = types.StringValue(resolvedVappUid) - } - } - params := map[int]string{ - 33: resources_core.FormatString(config.VappUid), - 35: resources_core.FormatString(config.VmName), - 36: resources_core.FormatString(config.VmCpu), - 37: resources_core.FormatString(config.VmRam), - 38: resources_core.FormatString(config.VmDisksize), - 176: resources_core.FormatString(config.TemplateName), - 177: resources_core.FormatString(config.AccessIpList), - 406: resources_core.FormatString(config.IpSpaceName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 23, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.VappUid.IsNull() || data.VappUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVappUid := data.VappUid - if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { - data.VappUid = types.StringValue(resolvedVappUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 33: resources_core.FormatString(data.VappUid), - 35: resources_core.FormatString(data.VmName), - 36: resources_core.FormatString(data.VmCpu), - 37: resources_core.FormatString(data.VmRam), - 38: resources_core.FormatString(data.VmDisksize), - 176: resources_core.FormatString(data.TemplateName), - 177: resources_core.FormatString(data.AccessIpList), - 406: resources_core.FormatString(data.IpSpaceName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 23, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVappUid.IsNull() && !originalVappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), originalVappUid.ValueString()) { - state.VappUid = originalVappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 23, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { - newState.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() && !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - if strings.EqualFold(newState.VappUid.ValueString(), state.VappUid.ValueString()) { - newState.VappUid = state.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmModel - var state VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { - if !strings.EqualFold(plan.VmCpu.ValueString(), state.VmCpu.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { - if !strings.EqualFold(plan.VmRam.ValueString(), state.VmRam.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmDisksize.IsNull() != state.VmDisksize.IsNull() || plan.VmDisksize.IsUnknown() != state.VmDisksize.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmDisksize.IsNull() && !plan.VmDisksize.IsUnknown() && !state.VmDisksize.IsNull() && !state.VmDisksize.IsUnknown() { - if !strings.EqualFold(plan.VmDisksize.ValueString(), state.VmDisksize.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { - if !strings.EqualFold(plan.AccessIpList.ValueString(), state.AccessIpList.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 186: resources_core.FormatString(plan.VmCpu), - 187: resources_core.FormatString(plan.VmRam), - 188: resources_core.FormatString(plan.VmDisksize), - 189: resources_core.FormatString(plan.AccessIpList), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 23, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), plan.VappUid.ValueString()) { - state.VappUid = plan.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/24_vc_nat_resource.go b/provider/internal/resources_gen/24_vc_nat_resource.go deleted file mode 100644 index 20fe4bd..0000000 --- a/provider/internal/resources_gen/24_vc_nat_resource.go +++ /dev/null @@ -1,322 +0,0 @@ -package resources_gen - -import ( - "context" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_nat -// Service ID: 24 - -var _ resource.Resource = &VcNatResource{} -var _ resource.ResourceWithModifyPlan = &VcNatResource{} -var _ resource.ResourceWithImportState = &VcNatResource{} - -type VcNatResource struct { - client *core.UniversalClient -} - -type VcNatModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcNatResource() resource.Resource { - return &VcNatResource{} -} - -func (r *VcNatResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_nat" -} - -func (r *VcNatResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcNatResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcNatModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcNatModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcNatModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{} - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 24, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcNatResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcNatModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 24, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{} - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 24, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 24, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNatResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcNatModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 24, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcNatResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcNatModel - var state VcNatModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 24, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNatResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcNatModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcNatResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcNatResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/25_vcexternalip_resource.go b/provider/internal/resources_gen/25_vcexternalip_resource.go deleted file mode 100644 index c375621..0000000 --- a/provider/internal/resources_gen/25_vcexternalip_resource.go +++ /dev/null @@ -1,604 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vcexternalip -// Service ID: 25 - -var _ resource.Resource = &VcexternalipResource{} -var _ resource.ResourceWithModifyPlan = &VcexternalipResource{} -var _ resource.ResourceWithImportState = &VcexternalipResource{} - -type VcexternalipResource struct { - client *core.UniversalClient -} - -type VcexternalipModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DnatCreate types.Bool `tfsdk:"dnat_create"` - FromServiceCloudEdgeName types.String `tfsdk:"from_service_cloud_edge_name"` - FromServiceCloudEdgeScope types.String `tfsdk:"from_service_cloud_edge_scope"` - FromServiceCloudOrgName types.String `tfsdk:"from_service_cloud_org_name"` - FromServiceCloudVdcName types.String `tfsdk:"from_service_cloud_vdc_name"` - FromServiceCloudVmwareUrl types.String `tfsdk:"from_service_cloud_vmware_url"` - FromServiceNamespace types.String `tfsdk:"from_service_namespace"` - FromServiceVdcGroupName types.String `tfsdk:"from_service_vdc_group_name"` - InternalAddrAccess types.String `tfsdk:"internal_addr_access"` - InternalPortAccess types.String `tfsdk:"internal_port_access"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ServiceUid types.String `tfsdk:"service_uid"` - SnatCreate types.Bool `tfsdk:"snat_create"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcexternalipResource() resource.Resource { - return &VcexternalipResource{} -} - -func (r *VcexternalipResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vcexternalip" -} - -func (r *VcexternalipResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "dnat_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Необходимо создать DNAT-правило."}, - "from_service_cloud_edge_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_edge_scope": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "from_service_cloud_org_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Org составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_vdc_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_vmware_url": schema.StringAttribute{Optional: true, MarkdownDescription: "Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_namespace": schema.StringAttribute{Required: true, MarkdownDescription: "Namespace составной услуги (для отображения правил FW) Не изменяется после создания."}, - "from_service_vdc_group_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "internal_addr_access": schema.StringAttribute{Required: true, MarkdownDescription: "Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true"}, - "internal_port_access": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("5432"), MarkdownDescription: "Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
"}, - "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1)"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "service_uid": schema.StringAttribute{Required: true}, - "snat_create": schema.BoolAttribute{Optional: true, MarkdownDescription: "Необходимо создать SNAT-правило.
"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcexternalipResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcexternalipModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.FromServiceCloudEdgeName.IsNull() && !plan.FromServiceCloudEdgeName.IsUnknown() && !state.FromServiceCloudEdgeName.IsNull() && !state.FromServiceCloudEdgeName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudEdgeName.ValueString(), state.FromServiceCloudEdgeName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudEdgeScope.IsNull() && !plan.FromServiceCloudEdgeScope.IsUnknown() && !state.FromServiceCloudEdgeScope.IsNull() && !state.FromServiceCloudEdgeScope.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudEdgeScope.ValueString(), state.FromServiceCloudEdgeScope.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_scope", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudOrgName.IsNull() && !plan.FromServiceCloudOrgName.IsUnknown() && !state.FromServiceCloudOrgName.IsNull() && !state.FromServiceCloudOrgName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudOrgName.ValueString(), state.FromServiceCloudOrgName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_org_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudVdcName.IsNull() && !plan.FromServiceCloudVdcName.IsUnknown() && !state.FromServiceCloudVdcName.IsNull() && !state.FromServiceCloudVdcName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudVdcName.ValueString(), state.FromServiceCloudVdcName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vdc_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudVmwareUrl.IsNull() && !plan.FromServiceCloudVmwareUrl.IsUnknown() && !state.FromServiceCloudVmwareUrl.IsNull() && !state.FromServiceCloudVmwareUrl.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudVmwareUrl.ValueString(), state.FromServiceCloudVmwareUrl.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vmware_url", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceNamespace.IsNull() && !plan.FromServiceNamespace.IsUnknown() && !state.FromServiceNamespace.IsNull() && !state.FromServiceNamespace.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceNamespace.ValueString(), state.FromServiceNamespace.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_namespace", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceVdcGroupName.IsNull() && !plan.FromServiceVdcGroupName.IsUnknown() && !state.FromServiceVdcGroupName.IsNull() && !state.FromServiceVdcGroupName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceVdcGroupName.ValueString(), state.FromServiceVdcGroupName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_vdc_group_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.FromServiceNamespace.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") - return - } - if config.FromServiceNamespace.IsUnknown() { - return - } - if config.InternalAddrAccess.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") - return - } - if config.InternalAddrAccess.IsUnknown() { - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.ServiceUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") - return - } - if config.ServiceUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 140: resources_core.FormatString(config.ServiceUid), - 141: resources_core.FormatBool(config.DnatCreate), - 142: resources_core.FormatString(config.InternalPortAccess), - 143: resources_core.FormatBool(config.SnatCreate), - 144: resources_core.FormatString(config.InternalAddrAccess), - 324: resources_core.FormatString(config.FromServiceNamespace), - 325: resources_core.FormatString(config.FromServiceCloudEdgeName), - 326: resources_core.FormatString(config.FromServiceCloudVdcName), - 327: resources_core.FormatString(config.FromServiceCloudOrgName), - 328: resources_core.FormatString(config.FromServiceCloudVmwareUrl), - 336: resources_core.FormatString(config.IpSpaceName), - 488: resources_core.FormatString(config.ResourceRealm), - 624: resources_core.FormatString(config.FromServiceCloudEdgeScope), - 626: resources_core.FormatString(config.FromServiceVdcGroupName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 25, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcexternalipResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.FromServiceNamespace.IsNull() || data.FromServiceNamespace.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") - return - } - if data.InternalAddrAccess.IsNull() || data.InternalAddrAccess.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.ServiceUid.IsNull() || data.ServiceUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 140: resources_core.FormatString(data.ServiceUid), - 141: resources_core.FormatBool(data.DnatCreate), - 142: resources_core.FormatString(data.InternalPortAccess), - 143: resources_core.FormatBool(data.SnatCreate), - 144: resources_core.FormatString(data.InternalAddrAccess), - 324: resources_core.FormatString(data.FromServiceNamespace), - 325: resources_core.FormatString(data.FromServiceCloudEdgeName), - 326: resources_core.FormatString(data.FromServiceCloudVdcName), - 327: resources_core.FormatString(data.FromServiceCloudOrgName), - 328: resources_core.FormatString(data.FromServiceCloudVmwareUrl), - 336: resources_core.FormatString(data.IpSpaceName), - 488: resources_core.FormatString(data.ResourceRealm), - 624: resources_core.FormatString(data.FromServiceCloudEdgeScope), - 626: resources_core.FormatString(data.FromServiceVdcGroupName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 25, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcexternalipResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 25, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcexternalipResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcexternalipModel - var state VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ServiceUid.IsNull() != state.ServiceUid.IsNull() || plan.ServiceUid.IsUnknown() != state.ServiceUid.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ServiceUid.IsNull() && !plan.ServiceUid.IsUnknown() && !state.ServiceUid.IsNull() && !state.ServiceUid.IsUnknown() { - if !strings.EqualFold(plan.ServiceUid.ValueString(), state.ServiceUid.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.DnatCreate.IsNull() != state.DnatCreate.IsNull() || plan.DnatCreate.IsUnknown() != state.DnatCreate.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.DnatCreate.IsNull() && !plan.DnatCreate.IsUnknown() && !state.DnatCreate.IsNull() && !state.DnatCreate.IsUnknown() { - if plan.DnatCreate.ValueBool() != state.DnatCreate.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.SnatCreate.IsNull() != state.SnatCreate.IsNull() || plan.SnatCreate.IsUnknown() != state.SnatCreate.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.SnatCreate.IsNull() && !plan.SnatCreate.IsUnknown() && !state.SnatCreate.IsNull() && !state.SnatCreate.IsUnknown() { - if plan.SnatCreate.ValueBool() != state.SnatCreate.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.InternalPortAccess.IsNull() != state.InternalPortAccess.IsNull() || plan.InternalPortAccess.IsUnknown() != state.InternalPortAccess.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.InternalPortAccess.IsNull() && !plan.InternalPortAccess.IsUnknown() && !state.InternalPortAccess.IsNull() && !state.InternalPortAccess.IsUnknown() { - if !strings.EqualFold(plan.InternalPortAccess.ValueString(), state.InternalPortAccess.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.InternalAddrAccess.IsNull() != state.InternalAddrAccess.IsNull() || plan.InternalAddrAccess.IsUnknown() != state.InternalAddrAccess.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.InternalAddrAccess.IsNull() && !plan.InternalAddrAccess.IsUnknown() && !state.InternalAddrAccess.IsNull() && !state.InternalAddrAccess.IsUnknown() { - if !strings.EqualFold(plan.InternalAddrAccess.ValueString(), state.InternalAddrAccess.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 627: resources_core.FormatString(plan.ServiceUid), - 628: resources_core.FormatString(plan.IpSpaceName), - 629: resources_core.FormatBool(plan.DnatCreate), - 630: resources_core.FormatBool(plan.SnatCreate), - 631: resources_core.FormatString(plan.InternalPortAccess), - 632: resources_core.FormatString(plan.InternalAddrAccess), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 25, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcexternalipResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcexternalipResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcexternalipResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/26_vapp_resource.go b/provider/internal/resources_gen/26_vapp_resource.go deleted file mode 100644 index e210b7f..0000000 --- a/provider/internal/resources_gen/26_vapp_resource.go +++ /dev/null @@ -1,571 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vapp -// Service ID: 26 - -var _ resource.Resource = &VappResource{} -var _ resource.ResourceWithModifyPlan = &VappResource{} -var _ resource.ResourceWithImportState = &VappResource{} - -type VappResource struct { - client *core.UniversalClient -} - -type VappModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - NsxtUid types.String `tfsdk:"nsxt_uid"` - VappName types.String `tfsdk:"vapp_name"` - VdcUid types.String `tfsdk:"vdc_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVappResource() resource.Resource { - return &VappResource{} -} - -func (r *VappResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vapp" -} - -func (r *VappResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "nsxt_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Сетевой шлюз периметра (Edge)` Не изменяется после создания."}, - "vapp_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("9pv2jybu"), MarkdownDescription: "Уникальное в рамках организации Не изменяется после создания."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VappResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VappModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NsxtUid.ValueString(), state.NsxtUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить nsxt_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappName.ValueString(), state.VappName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.NsxtUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") - return - } - if config.NsxtUid.IsUnknown() { - return - } - if config.VdcUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - if config.VdcUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.NsxtUid.IsNull() && !config.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, config.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != config.NsxtUid.ValueString() { - config.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 190: resources_core.FormatString(config.NsxtUid), - 191: resources_core.FormatString(config.VappName), - 623: resources_core.FormatString(config.VdcUid), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 26, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VappResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.NsxtUid.IsNull() || data.NsxtUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") - return - } - if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalNsxtUid := data.NsxtUid - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.NsxtUid.IsNull() && !data.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, data.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedNsxtUid != "" && resolvedNsxtUid != data.NsxtUid.ValueString() { - data.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 190: resources_core.FormatString(data.NsxtUid), - 191: resources_core.FormatString(data.VappName), - 623: resources_core.FormatString(data.VdcUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 26, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { - state.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalNsxtUid.IsNull() && !originalNsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - if strings.EqualFold(state.NsxtUid.ValueString(), originalNsxtUid.ValueString()) { - state.NsxtUid = originalNsxtUid - } - } - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VappResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 26, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, newState.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != newState.NsxtUid.ValueString() { - newState.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() && !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { - if strings.EqualFold(newState.NsxtUid.ValueString(), state.NsxtUid.ValueString()) { - newState.NsxtUid = state.NsxtUid - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VappResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VappModel - var state VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 26, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { - state.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - if strings.EqualFold(state.NsxtUid.ValueString(), plan.NsxtUid.ValueString()) { - state.NsxtUid = plan.NsxtUid - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VappResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VappResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VappResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/27_vc_vm_v2_resource.go b/provider/internal/resources_gen/27_vc_vm_v2_resource.go deleted file mode 100644 index 6a05554..0000000 --- a/provider/internal/resources_gen/27_vc_vm_v2_resource.go +++ /dev/null @@ -1,350 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm_v2 -// Service ID: 27 - -var _ resource.Resource = &VcVmV2Resource{} -var _ resource.ResourceWithModifyPlan = &VcVmV2Resource{} -var _ resource.ResourceWithImportState = &VcVmV2Resource{} - -type VcVmV2Resource struct { - client *core.UniversalClient -} - -type VcVmV2Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ServerListJson types.String `tfsdk:"server_list_json"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmV2Resource() resource.Resource { - return &VcVmV2Resource{} -} - -func (r *VcVmV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm_v2" -} - -func (r *VcVmV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "server_list_json": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2)", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmV2Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 301: resources_core.FormatString(config.ServerListJson), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 27, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 301: resources_core.FormatString(data.ServerListJson), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 27, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 27, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmV2Model - var state VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ServerListJson.IsNull() != state.ServerListJson.IsNull() || plan.ServerListJson.IsUnknown() != state.ServerListJson.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ServerListJson.IsNull() && !plan.ServerListJson.IsUnknown() && !state.ServerListJson.IsNull() && !state.ServerListJson.IsUnknown() { - if !strings.EqualFold(plan.ServerListJson.ValueString(), state.ServerListJson.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 302: resources_core.FormatString(plan.ServerListJson), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 27, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/28_vc_vm_v3_resource.go b/provider/internal/resources_gen/28_vc_vm_v3_resource.go deleted file mode 100644 index 61fe97c..0000000 --- a/provider/internal/resources_gen/28_vc_vm_v3_resource.go +++ /dev/null @@ -1,697 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm_v3 -// Service ID: 28 - -var _ resource.Resource = &VcVmV3Resource{} -var _ resource.ResourceWithModifyPlan = &VcVmV3Resource{} -var _ resource.ResourceWithImportState = &VcVmV3Resource{} - -type VcVmV3Resource struct { - client *core.UniversalClient -} - -type VcVmV3Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessIpList types.String `tfsdk:"access_ip_list"` - AccessPortList types.String `tfsdk:"access_port_list"` - CloudInit types.String `tfsdk:"cloud_init"` - ImageVm types.String `tfsdk:"image_vm"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - UserLogin types.String `tfsdk:"user_login"` - UserPublicKey types.String `tfsdk:"user_public_key"` - VappUid types.String `tfsdk:"vapp_uid"` - VmCpu types.Int64 `tfsdk:"vm_cpu"` - VmDisk types.Int64 `tfsdk:"vm_disk"` - VmName types.String `tfsdk:"vm_name"` - VmRam types.Int64 `tfsdk:"vm_ram"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmV3Resource() resource.Resource { - return &VcVmV3Resource{} -} - -func (r *VcVmV3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm_v3" -} - -func (r *VcVmV3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "access_port_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"22\"]"), MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX"}, - "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. Не изменяется после создания."}, - "image_vm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "ip_space_name": schema.StringAttribute{Required: true}, - "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myuser"), MarkdownDescription: "Учётка SSH Не изменяется после создания."}, - "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя Не изменяется после создания."}, - "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=26 (mapped to UUID in core) Не изменяется после создания."}, - "vm_cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "шт"}, - "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, - "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Не изменяется после создания."}, - "vm_ram": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Gb"}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmV3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmV3Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CloudInit.IsNull() && !plan.CloudInit.IsUnknown() && !state.CloudInit.IsNull() && !state.CloudInit.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.CloudInit.ValueString(), state.CloudInit.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить cloud_init", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ImageVm.IsNull() && !plan.ImageVm.IsUnknown() && !state.ImageVm.IsNull() && !state.ImageVm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ImageVm.ValueString(), state.ImageVm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить image_vm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserLogin.IsNull() && !plan.UserLogin.IsUnknown() && !state.UserLogin.IsNull() && !state.UserLogin.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserLogin.ValueString(), state.UserLogin.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserPublicKey.IsNull() && !plan.UserPublicKey.IsUnknown() && !state.UserPublicKey.IsNull() && !state.UserPublicKey.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserPublicKey.ValueString(), state.UserPublicKey.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_public_key", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappUid.ValueString(), state.VappUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmName.ValueString(), state.VmName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ImageVm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") - return - } - if config.ImageVm.IsUnknown() { - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.UserPublicKey.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") - return - } - if config.UserPublicKey.IsUnknown() { - return - } - if config.VappUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - if config.VappUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { - config.VappUid = types.StringValue(resolvedVappUid) - } - } - params := map[int]string{ - 407: resources_core.FormatString(config.VappUid), - 408: resources_core.FormatString(config.VmName), - 409: resources_core.FormatInt64(config.VmCpu), - 410: resources_core.FormatInt64(config.VmRam), - 411: resources_core.FormatInt64(config.VmDisk), - 412: resources_core.FormatString(config.IpSpaceName), - 413: resources_core.FormatString(config.AccessIpList), - 414: resources_core.FormatString(config.ImageVm), - 415: resources_core.FormatString(config.CloudInit), - 416: resources_core.FormatString(config.UserLogin), - 417: resources_core.FormatString(config.UserPublicKey), - 448: resources_core.FormatString(config.AccessPortList), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 28, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmV3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ImageVm.IsNull() || data.ImageVm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.UserPublicKey.IsNull() || data.UserPublicKey.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") - return - } - if data.VappUid.IsNull() || data.VappUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVappUid := data.VappUid - if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { - data.VappUid = types.StringValue(resolvedVappUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 407: resources_core.FormatString(data.VappUid), - 408: resources_core.FormatString(data.VmName), - 409: resources_core.FormatInt64(data.VmCpu), - 410: resources_core.FormatInt64(data.VmRam), - 411: resources_core.FormatInt64(data.VmDisk), - 412: resources_core.FormatString(data.IpSpaceName), - 413: resources_core.FormatString(data.AccessIpList), - 414: resources_core.FormatString(data.ImageVm), - 415: resources_core.FormatString(data.CloudInit), - 416: resources_core.FormatString(data.UserLogin), - 417: resources_core.FormatString(data.UserPublicKey), - 448: resources_core.FormatString(data.AccessPortList), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 28, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVappUid.IsNull() && !originalVappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), originalVappUid.ValueString()) { - state.VappUid = originalVappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 28, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { - newState.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() && !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - if strings.EqualFold(newState.VappUid.ValueString(), state.VappUid.ValueString()) { - newState.VappUid = state.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmV3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmV3Model - var state VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { - if plan.VmCpu.ValueInt64() != state.VmCpu.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { - if plan.VmRam.ValueInt64() != state.VmRam.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmDisk.IsNull() != state.VmDisk.IsNull() || plan.VmDisk.IsUnknown() != state.VmDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmDisk.IsNull() && !plan.VmDisk.IsUnknown() && !state.VmDisk.IsNull() && !state.VmDisk.IsUnknown() { - if plan.VmDisk.ValueInt64() != state.VmDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { - if !strings.EqualFold(plan.AccessIpList.ValueString(), state.AccessIpList.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessPortList.IsNull() != state.AccessPortList.IsNull() || plan.AccessPortList.IsUnknown() != state.AccessPortList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessPortList.IsNull() && !plan.AccessPortList.IsUnknown() && !state.AccessPortList.IsNull() && !state.AccessPortList.IsUnknown() { - if !strings.EqualFold(plan.AccessPortList.ValueString(), state.AccessPortList.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 493: resources_core.FormatInt64(plan.VmCpu), - 494: resources_core.FormatInt64(plan.VmRam), - 495: resources_core.FormatInt64(plan.VmDisk), - 496: resources_core.FormatString(plan.IpSpaceName), - 497: resources_core.FormatString(plan.AccessIpList), - 498: resources_core.FormatString(plan.AccessPortList), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 28, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), plan.VappUid.ValueString()) { - state.VappUid = plan.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmV3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmV3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/29_vc_vdc_group_resource.go b/provider/internal/resources_gen/29_vc_vdc_group_resource.go deleted file mode 100644 index 41864ef..0000000 --- a/provider/internal/resources_gen/29_vc_vdc_group_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc_group -// Service ID: 29 - -var _ resource.Resource = &VcVdcGroupResource{} -var _ resource.ResourceWithModifyPlan = &VcVdcGroupResource{} -var _ resource.ResourceWithImportState = &VcVdcGroupResource{} - -type VcVdcGroupResource struct { - client *core.UniversalClient -} - -type VcVdcGroupModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - VdcUid types.String `tfsdk:"vdc_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVdcGroupResource() resource.Resource { - return &VcVdcGroupResource{} -} - -func (r *VcVdcGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc_group" -} - -func (r *VcVdcGroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core) Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVdcGroupModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VdcUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - if config.VdcUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 618: resources_core.FormatString(config.VdcUid), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 29, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVdcGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 618: resources_core.FormatString(data.VdcUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 29, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 29, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVdcGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcGroupModel - var state VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 29, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVdcGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVdcGroupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/29_vc_vdc_group_vdc_resource.go b/provider/internal/resources_gen/29_vc_vdc_group_vdc_resource.go deleted file mode 100644 index 3e3e8ef..0000000 --- a/provider/internal/resources_gen/29_vc_vdc_group_vdc_resource.go +++ /dev/null @@ -1,326 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc_group -// Subresource: vdc - -var _ resource.Resource = &VcVdcGroupVdcResource{} - -// VcVdcGroupVdcResource manages a subresource via operations. -type VcVdcGroupVdcResource struct { - client *core.UniversalClient -} - -type VcVdcGroupVdcModel struct { - ID types.String `tfsdk:"id"` - VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewVcVdcGroupVdcResource() resource.Resource { - return &VcVdcGroupVdcResource{} -} - -func (r *VcVdcGroupVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc_group_vdc" -} - -func (r *VcVdcGroupVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "vc_vdc_group_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcGroupVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VcVdcGroupVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("vdc", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcVdcGroupVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcGroupVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcVdcGroupVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.VcVdcGroupID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("vdc", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *VcVdcGroupVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/2_template_resource.go b/provider/internal/resources_gen/2_template_resource.go deleted file mode 100644 index 9c63c5f..0000000 --- a/provider/internal/resources_gen/2_template_resource.go +++ /dev/null @@ -1,397 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: template -// Service ID: 2 - -var _ resource.Resource = &TemplateResource{} -var _ resource.ResourceWithModifyPlan = &TemplateResource{} -var _ resource.ResourceWithImportState = &TemplateResource{} - -type TemplateResource struct { - client *core.UniversalClient -} - -// TemplateAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type TemplateAccessConfigurationModel struct { - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` - Type types.String `tfsdk:"type" json:"type"` - Domain types.String `tfsdk:"domain" json:"domain"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` -} - -// TemplateClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type TemplateClusterConfigurationModel struct { - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// TemplateStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type TemplateStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type TemplateModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *TemplateAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *TemplateClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *TemplateStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewTemplateResource() resource.Resource { - return &TemplateResource{} -} - -func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_template" -} - -func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]")}, - "type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ingress")}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("abracadabra-mgonny3n")}, - "master_ip_space": schema.StringAttribute{Required: true}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]")}, - "slave_ip_space": schema.StringAttribute{Required: true}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(5)}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(128)}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *TemplateResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *TemplateModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 813: resources_core.BuildJSON(map[string]string{"disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 814: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 815: resources_core.BuildJSON(map[string]string{"slaveAccessList": config.AccessConfiguration.SlaveAccessList.ValueString(), "type": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": config.AccessConfiguration.MasterAccessList.ValueString(), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 2, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *TemplateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 813: resources_core.BuildJSON(map[string]string{"disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 814: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 815: resources_core.BuildJSON(map[string]string{"slaveAccessList": data.AccessConfiguration.SlaveAccessList.ValueString(), "type": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": data.AccessConfiguration.MasterAccessList.ValueString(), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 2, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 2, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan TemplateModel - var state TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 816: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 817: resources_core.BuildJSON(map[string]string{"masterAccessList": plan.AccessConfiguration.MasterAccessList.ValueString(), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": plan.AccessConfiguration.SlaveAccessList.ValueString(), "type": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 2, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TemplateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *TemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *TemplateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/32_vmpostgre_resource.go b/provider/internal/resources_gen/32_vmpostgre_resource.go deleted file mode 100644 index 5d39c90..0000000 --- a/provider/internal/resources_gen/32_vmpostgre_resource.go +++ /dev/null @@ -1,514 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vmpostgre -// Service ID: 32 - -var _ resource.Resource = &VmpostgreResource{} -var _ resource.ResourceWithModifyPlan = &VmpostgreResource{} -var _ resource.ResourceWithImportState = &VmpostgreResource{} - -type VmpostgreResource struct { - client *core.UniversalClient -} - -type VmpostgreModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - UserDb types.String `tfsdk:"user_db"` - UserLogin types.String `tfsdk:"user_login"` - UserPassword types.String `tfsdk:"user_password"` - VmUid types.String `tfsdk:"vm_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVmpostgreResource() resource.Resource { - return &VmpostgreResource{} -} - -func (r *VmpostgreResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vmpostgre" -} - -func (r *VmpostgreResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "user_db": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("servicedb"), MarkdownDescription: "Наименование Базы данных
Не изменяется после создания."}, - "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("serviceuser"), MarkdownDescription: "Пользователь базы данных
Не изменяется после создания."}, - "user_password": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("fj29gm48fm!d374mc9"), MarkdownDescription: "Пароль от доступа к Базе данных
Не изменяется после создания."}, - "vm_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "InstanceUid ВМ. ВМ должна быть создана
Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VmpostgreResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VmpostgreModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VmpostgreModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VmpostgreModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserDb.IsNull() && !plan.UserDb.IsUnknown() && !state.UserDb.IsNull() && !state.UserDb.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserDb.ValueString(), state.UserDb.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_db", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserLogin.IsNull() && !plan.UserLogin.IsUnknown() && !state.UserLogin.IsNull() && !state.UserLogin.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserLogin.ValueString(), state.UserLogin.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserPassword.IsNull() && !plan.UserPassword.IsUnknown() && !state.UserPassword.IsNull() && !state.UserPassword.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserPassword.ValueString(), state.UserPassword.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_password", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmUid.IsNull() && !plan.VmUid.IsUnknown() && !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmUid.ValueString(), state.VmUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VmUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vm_uid is required.") - return - } - if config.VmUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VmUid.IsNull() && !config.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, config.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) - } else if resolvedVmUid != "" && resolvedVmUid != config.VmUid.ValueString() { - config.VmUid = types.StringValue(resolvedVmUid) - } - } - params := map[int]string{ - 45: resources_core.FormatString(config.VmUid), - 46: resources_core.FormatString(config.UserDb), - 47: resources_core.FormatString(config.UserLogin), - 48: resources_core.FormatString(config.UserPassword), - 278: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 32, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VmpostgreResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VmpostgreModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VmUid.IsNull() || data.VmUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vm_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVmUid := data.VmUid - if !data.VmUid.IsNull() && !data.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, data.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVmUid != "" && resolvedVmUid != data.VmUid.ValueString() { - data.VmUid = types.StringValue(resolvedVmUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 32, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 45: resources_core.FormatString(data.VmUid), - 46: resources_core.FormatString(data.UserDb), - 47: resources_core.FormatString(data.UserLogin), - 48: resources_core.FormatString(data.UserPassword), - 278: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 32, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 32, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "userDb", Field: "UserDb", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPassword", Field: "UserPassword", Type: "string"}, - {Code: "vmUid", Field: "VmUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, state.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) - } else if resolvedVmUid != "" && resolvedVmUid != state.VmUid.ValueString() { - state.VmUid = types.StringValue(resolvedVmUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVmUid.IsNull() && !originalVmUid.IsUnknown() && !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - if strings.EqualFold(state.VmUid.ValueString(), originalVmUid.ValueString()) { - state.VmUid = originalVmUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VmpostgreResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VmpostgreModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 32, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "userDb", Field: "UserDb", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPassword", Field: "UserPassword", Type: "string"}, - {Code: "vmUid", Field: "VmUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VmUid.IsNull() && !newState.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, newState.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) - } else if resolvedVmUid != "" && resolvedVmUid != newState.VmUid.ValueString() { - newState.VmUid = types.StringValue(resolvedVmUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VmUid.IsNull() && !state.VmUid.IsUnknown() && !newState.VmUid.IsNull() && !newState.VmUid.IsUnknown() { - if strings.EqualFold(newState.VmUid.ValueString(), state.VmUid.ValueString()) { - newState.VmUid = state.VmUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VmpostgreResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VmpostgreModel - var state VmpostgreModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 32, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "userDb", Field: "UserDb", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPassword", Field: "UserPassword", Type: "string"}, - {Code: "vmUid", Field: "VmUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, state.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) - } else if resolvedVmUid != "" && resolvedVmUid != state.VmUid.ValueString() { - state.VmUid = types.StringValue(resolvedVmUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VmUid.IsNull() && !plan.VmUid.IsUnknown() && !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - if strings.EqualFold(state.VmUid.ValueString(), plan.VmUid.ValueString()) { - state.VmUid = plan.VmUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VmpostgreResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VmpostgreModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VmpostgreResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VmpostgreResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/50_nextcloud_backup_resource.go b/provider/internal/resources_gen/50_nextcloud_backup_resource.go deleted file mode 100644 index feb1f0e..0000000 --- a/provider/internal/resources_gen/50_nextcloud_backup_resource.go +++ /dev/null @@ -1,344 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Subresource: backup - -var _ resource.Resource = &NextcloudBackupResource{} - -// NextcloudBackupResource manages a subresource via operations. -type NextcloudBackupResource struct { - client *core.UniversalClient -} - -type NextcloudBackupModel struct { - ID types.String `tfsdk:"id"` - NextcloudID types.String `tfsdk:"nextcloud_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - StartupConfiguration types.String `tfsdk:"startup_configuration"` -} - -func NewNextcloudBackupResource() resource.Resource { - return &NextcloudBackupResource{} -} - -func (r *NextcloudBackupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud_backup" -} - -func (r *NextcloudBackupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "nextcloud_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudBackupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan NextcloudBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.StartupConfiguration.IsNull() || plan.StartupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - } - identityCodes := []string{ - "startupConfiguration", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudBackupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudBackupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudBackupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.NextcloudID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(state.StartupConfiguration), - } - identityCodes := []string{ - "startupConfiguration", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *NextcloudBackupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/50_nextcloud_resource.go b/provider/internal/resources_gen/50_nextcloud_resource.go deleted file mode 100644 index 3e5c608..0000000 --- a/provider/internal/resources_gen/50_nextcloud_resource.go +++ /dev/null @@ -1,414 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Service ID: 50 - -var _ resource.Resource = &NextcloudResource{} -var _ resource.ResourceWithModifyPlan = &NextcloudResource{} -var _ resource.ResourceWithImportState = &NextcloudResource{} - -type NextcloudResource struct { - client *core.UniversalClient -} - -// NextcloudAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type NextcloudAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// NextcloudAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type NextcloudAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - DocumentServer types.String `tfsdk:"document_server" json:"documentServer"` - LogLevel types.String `tfsdk:"log_level" json:"logLevel"` -} - -// NextcloudClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type NextcloudClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// NextcloudStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type NextcloudStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` - PsqlEnabled types.Bool `tfsdk:"psql_enabled" json:"psqlEnabled"` - RedisEnabled types.Bool `tfsdk:"redis_enabled" json:"redisEnabled"` - PsqlUid types.String `tfsdk:"psql_uid" json:"psqlUid"` - RedisUid types.String `tfsdk:"redis_uid" json:"redisUid"` -} - -type NextcloudModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *NextcloudAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *NextcloudAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *NextcloudClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *NextcloudStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNextcloudResource() resource.Resource { - return &NextcloudResource{} -} - -func (r *NextcloudResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud" -} - -func (r *NextcloudResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nextcloud-xcq3u70l"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("33.0.4")}, - "document_server": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("collabora"), MarkdownDescription: "Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ"}, - "log_level": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("INFO"), MarkdownDescription: "Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - "psql_enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud"}, - "redis_enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud"}, - "psql_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета`"}, - "redis_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета`"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NextcloudModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1055: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString()), "psqlEnabled": fmt.Sprintf("%v", config.StartupConfiguration.PsqlEnabled.ValueBool()), "redisEnabled": fmt.Sprintf("%v", config.StartupConfiguration.RedisEnabled.ValueBool()), "psqlUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.PsqlUid.ValueString()), "redisUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.RedisUid.ValueString())}), - 1056: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1057: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1058: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", config.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", config.AppConfiguration.LogLevel.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 50, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NextcloudResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1055: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString()), "psqlEnabled": fmt.Sprintf("%v", data.StartupConfiguration.PsqlEnabled.ValueBool()), "redisEnabled": fmt.Sprintf("%v", data.StartupConfiguration.RedisEnabled.ValueBool()), "psqlUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.PsqlUid.ValueString()), "redisUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.RedisUid.ValueString())}), - 1056: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1057: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1058: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", data.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", data.AppConfiguration.LogLevel.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 50, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 50, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NextcloudResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudModel - var state NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1059: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 1060: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", plan.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", plan.AppConfiguration.LogLevel.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 50, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NextcloudResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NextcloudResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/81_superset_resource.go b/provider/internal/resources_gen/81_superset_resource.go deleted file mode 100644 index 94602e6..0000000 --- a/provider/internal/resources_gen/81_superset_resource.go +++ /dev/null @@ -1,447 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: superset -// Service ID: 81 - -var _ resource.Resource = &SupersetResource{} -var _ resource.ResourceWithModifyPlan = &SupersetResource{} -var _ resource.ResourceWithImportState = &SupersetResource{} - -type SupersetResource struct { - client *core.UniversalClient -} - -type SupersetModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - Emails types.String `tfsdk:"emails"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewSupersetResource() resource.Resource { - return &SupersetResource{} -} - -func (r *SupersetResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_superset" -} - -func (r *SupersetResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("superset-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "emails": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("testuser1"), MarkdownDescription: "Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *SupersetResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *SupersetModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Emails.IsNull() && !plan.Emails.IsUnknown() && !state.Emails.IsNull() && !state.Emails.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Emails.ValueString(), state.Emails.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить emails", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 14: resources_core.FormatString(config.Domain), - 20: resources_core.FormatString(config.Emails), - 96: resources_core.FormatInt64(config.ResourceCPU), - 97: resources_core.FormatInt64(config.ResourceMemory), - 98: resources_core.FormatInt64(config.ResourceDisk), - 109: resources_core.FormatString(config.ResourceRealm), - 227: resources_core.FormatInt64(config.ResourceInstances), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 81, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *SupersetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 14: resources_core.FormatString(data.Domain), - 20: resources_core.FormatString(data.Emails), - 96: resources_core.FormatInt64(data.ResourceCPU), - 97: resources_core.FormatInt64(data.ResourceMemory), - 98: resources_core.FormatInt64(data.ResourceDisk), - 109: resources_core.FormatString(data.ResourceRealm), - 227: resources_core.FormatInt64(data.ResourceInstances), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 81, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *SupersetResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 81, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *SupersetResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan SupersetModel - var state SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 81, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *SupersetResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *SupersetResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *SupersetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/82_harbor_resource.go b/provider/internal/resources_gen/82_harbor_resource.go deleted file mode 100644 index b53b4c3..0000000 --- a/provider/internal/resources_gen/82_harbor_resource.go +++ /dev/null @@ -1,482 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: harbor -// Service ID: 82 - -var _ resource.Resource = &HarborResource{} -var _ resource.ResourceWithModifyPlan = &HarborResource{} -var _ resource.ResourceWithImportState = &HarborResource{} - -type HarborResource struct { - client *core.UniversalClient -} - -type HarborModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - S3Uid types.String `tfsdk:"s3_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewHarborResource() resource.Resource { - return &HarborResource{} -} - -func (r *HarborResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_harbor" -} - -func (r *HarborResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("harbor-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "s3_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID экземпляра услуги S3 Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *HarborResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *HarborModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.S3Uid.ValueString(), state.S3Uid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - if config.S3Uid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") - return - } - if config.S3Uid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { - resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) - } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { - config.S3Uid = types.StringValue(resolvedS3Uid) - } - } - params := map[int]string{ - 16: resources_core.FormatString(config.Domain), - 110: resources_core.FormatString(config.ResourceRealm), - 228: resources_core.FormatInt64(config.ResourceCPU), - 229: resources_core.FormatInt64(config.ResourceMemory), - 230: resources_core.FormatInt64(config.ResourceInstances), - 231: resources_core.FormatString(config.S3Uid), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 82, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *HarborResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 16: resources_core.FormatString(data.Domain), - 110: resources_core.FormatString(data.ResourceRealm), - 228: resources_core.FormatInt64(data.ResourceCPU), - 229: resources_core.FormatInt64(data.ResourceMemory), - 230: resources_core.FormatInt64(data.ResourceInstances), - 231: resources_core.FormatString(data.S3Uid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 82, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HarborResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 82, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *HarborResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan HarborModel - var state HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 822: resources_core.FormatInt64(plan.ResourceInstances), - 823: resources_core.FormatInt64(plan.ResourceCPU), - 824: resources_core.FormatInt64(plan.ResourceMemory), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 82, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HarborResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *HarborResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *HarborResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/86_k8s_velero_resource.go b/provider/internal/resources_gen/86_k8s_velero_resource.go deleted file mode 100644 index 1a180ac..0000000 --- a/provider/internal/resources_gen/86_k8s_velero_resource.go +++ /dev/null @@ -1,401 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_velero -// Service ID: 86 - -var _ resource.Resource = &K8sVeleroResource{} -var _ resource.ResourceWithModifyPlan = &K8sVeleroResource{} -var _ resource.ResourceWithImportState = &K8sVeleroResource{} - -type K8sVeleroResource struct { - client *core.UniversalClient -} - -// K8sVeleroBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type K8sVeleroBackupConfigurationModel struct { - S3BucketUid types.String `tfsdk:"s3_bucket_uid" json:"s3BucketUid"` -} - -// K8sVeleroClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sVeleroClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// K8sVeleroNodeAgentConfigurationModel — вложенная модель для map-fixed параметра nodeAgentConfiguration. -type K8sVeleroNodeAgentConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` -} - -// K8sVeleroStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sVeleroStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type K8sVeleroModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - BackupConfiguration *K8sVeleroBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *K8sVeleroClusterConfigurationModel `tfsdk:"cluster_configuration"` - NodeAgentConfiguration *K8sVeleroNodeAgentConfigurationModel `tfsdk:"node_agent_configuration"` - StartupConfiguration *K8sVeleroStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sVeleroResource() resource.Resource { - return &K8sVeleroResource{} -} - -func (r *K8sVeleroResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_velero" -} - -func (r *K8sVeleroResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_bucket_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3-Бакета обязателен
Учётная запись будет создана автоматически, если её нету"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - }}, - "node_agent_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sVeleroResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sVeleroModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 962: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 963: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 964: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.NodeAgentConfiguration.Memory.ValueInt64())}), - 1076: resources_core.BuildJSON(map[string]string{"s3BucketUid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3BucketUid.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 86, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sVeleroResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 86, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 962: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 963: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 964: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.NodeAgentConfiguration.Memory.ValueInt64())}), - 1076: resources_core.BuildJSON(map[string]string{"s3BucketUid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3BucketUid.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 86, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 86, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sVeleroResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 86, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sVeleroResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sVeleroModel - var state K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 965: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 966: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.NodeAgentConfiguration.Memory.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 86, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sVeleroResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sVeleroResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sVeleroResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/87_k8svalkey_resource.go b/provider/internal/resources_gen/87_k8svalkey_resource.go deleted file mode 100644 index 28dabfe..0000000 --- a/provider/internal/resources_gen/87_k8svalkey_resource.go +++ /dev/null @@ -1,423 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8svalkey -// Service ID: 87 - -var _ resource.Resource = &K8svalkeyResource{} -var _ resource.ResourceWithModifyPlan = &K8svalkeyResource{} -var _ resource.ResourceWithImportState = &K8svalkeyResource{} - -type K8svalkeyResource struct { - client *core.UniversalClient -} - -// K8svalkeyAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type K8svalkeyAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// K8svalkeyClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8svalkeyClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Shards types.Int64 `tfsdk:"shards" json:"shards"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// K8svalkeyStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8svalkeyStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -// K8svalkeyValkeyConfigurationModel — вложенная модель для map-fixed параметра valkeyConfiguration. -type K8svalkeyValkeyConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -type K8svalkeyModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *K8svalkeyAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *K8svalkeyClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *K8svalkeyStartupConfigurationModel `tfsdk:"startup_configuration"` - ValkeyConf types.String `tfsdk:"valkey_conf"` - ValkeyConfiguration *K8svalkeyValkeyConfigurationModel `tfsdk:"valkey_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8svalkeyResource() resource.Resource { - return &K8svalkeyResource{} -} - -func (r *K8svalkeyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8svalkey" -} - -func (r *K8svalkeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "shards": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "valkey_conf": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "valkey_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0.1.0")}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8svalkeyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8svalkeyModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8svalkeyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8svalkeyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ValkeyConf.IsNull() && !plan.ValkeyConf.IsUnknown() && !state.ValkeyConf.IsNull() && !state.ValkeyConf.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ValkeyConf.ValueString(), state.ValkeyConf.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить valkey_conf", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1066: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1067: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", config.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1068: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": config.AccessConfiguration.MasterAccessList.ValueString()}), - 1071: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.ValkeyConfiguration.Version.ValueString())}), - 1072: resources_core.FormatString(config.ValkeyConf), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 87, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8svalkeyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8svalkeyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 87, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1066: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1067: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", data.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1068: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": data.AccessConfiguration.MasterAccessList.ValueString()}), - 1071: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.ValkeyConfiguration.Version.ValueString())}), - 1072: resources_core.FormatString(data.ValkeyConf), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 87, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 87, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "valkeyConf", Field: "ValkeyConf", Type: "array-map-fixed"}, - {Code: "valkeyConfiguration", Field: "ValkeyConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8svalkeyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8svalkeyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 87, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "valkeyConf", Field: "ValkeyConf", Type: "array-map-fixed"}, - {Code: "valkeyConfiguration", Field: "ValkeyConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8svalkeyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8svalkeyModel - var state K8svalkeyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1069: resources_core.BuildJSON(map[string]string{"shards": fmt.Sprintf("%d", plan.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1070: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": plan.AccessConfiguration.MasterAccessList.ValueString()}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 87, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "valkeyConf", Field: "ValkeyConf", Type: "array-map-fixed"}, - {Code: "valkeyConfiguration", Field: "ValkeyConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8svalkeyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8svalkeyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8svalkeyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8svalkeyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/87_k8svalkey_user_resource.go b/provider/internal/resources_gen/87_k8svalkey_user_resource.go deleted file mode 100644 index 3dbe8e0..0000000 --- a/provider/internal/resources_gen/87_k8svalkey_user_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8svalkey -// Subresource: user - -var _ resource.Resource = &K8svalkeyUserResource{} - -// K8svalkeyUserResource manages a subresource via operations. -type K8svalkeyUserResource struct { - client *core.UniversalClient -} - -type K8svalkeyUserModel struct { - ID types.String `tfsdk:"id"` - K8svalkeyID types.String `tfsdk:"k8svalkey_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Permissions types.String `tfsdk:"permissions"` - Username types.String `tfsdk:"username"` -} - -func NewK8svalkeyUserResource() resource.Resource { - return &K8svalkeyUserResource{} -} - -func (r *K8svalkeyUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8svalkey_user" -} - -func (r *K8svalkeyUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8svalkey_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "permissions": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8svalkeyUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8svalkeyUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Permissions.IsNull() || plan.Permissions.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "permissions is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.K8svalkeyID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "permissions": resources_core.FormatString(plan.Permissions), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "permissions", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "permissions": resources_core.FormatString(plan.Permissions), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8svalkeyUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8svalkeyUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8svalkeyUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8svalkeyUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8svalkeyUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8svalkeyID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "permissions": resources_core.FormatString(plan.Permissions), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8svalkeyUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8svalkeyUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8svalkeyID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "permissions": resources_core.FormatString(state.Permissions), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "permissions", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8svalkeyUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/88_k8s_ziti_controller_resource.go b/provider/internal/resources_gen/88_k8s_ziti_controller_resource.go deleted file mode 100644 index 234875e..0000000 --- a/provider/internal/resources_gen/88_k8s_ziti_controller_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_ziti_controller -// Service ID: 88 - -var _ resource.Resource = &K8sZitiControllerResource{} -var _ resource.ResourceWithModifyPlan = &K8sZitiControllerResource{} -var _ resource.ResourceWithImportState = &K8sZitiControllerResource{} - -type K8sZitiControllerResource struct { - client *core.UniversalClient -} - -type K8sZitiControllerModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sZitiControllerResource() resource.Resource { - return &K8sZitiControllerResource{} -} - -func (r *K8sZitiControllerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_ziti_controller" -} - -func (r *K8sZitiControllerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8sZitiController-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Квота ядра пода (milicores) Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sZitiControllerResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sZitiControllerModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 294: resources_core.FormatInt64(config.ResourceCPU), - 295: resources_core.FormatInt64(config.ResourceMemory), - 296: resources_core.FormatInt64(config.ResourceDisk), - 297: resources_core.FormatInt64(config.ResourceInstances), - 298: resources_core.FormatString(config.ResourceRealm), - 299: resources_core.FormatString(config.Domain), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 88, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sZitiControllerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 294: resources_core.FormatInt64(data.ResourceCPU), - 295: resources_core.FormatInt64(data.ResourceMemory), - 296: resources_core.FormatInt64(data.ResourceDisk), - 297: resources_core.FormatInt64(data.ResourceInstances), - 298: resources_core.FormatString(data.ResourceRealm), - 299: resources_core.FormatString(data.Domain), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 88, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sZitiControllerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 88, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sZitiControllerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sZitiControllerModel - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 88, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sZitiControllerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sZitiControllerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sZitiControllerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/89_flask_resource.go b/provider/internal/resources_gen/89_flask_resource.go deleted file mode 100644 index f6624df..0000000 --- a/provider/internal/resources_gen/89_flask_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: flask -// Service ID: 89 - -var _ resource.Resource = &FlaskResource{} -var _ resource.ResourceWithModifyPlan = &FlaskResource{} -var _ resource.ResourceWithImportState = &FlaskResource{} - -type FlaskResource struct { - client *core.UniversalClient -} - -// FlaskAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type FlaskAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// FlaskAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type FlaskAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// FlaskClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type FlaskClusterConfigurationModel struct { - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` -} - -// FlaskStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type FlaskStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type FlaskModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *FlaskAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *FlaskAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *FlaskClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *FlaskStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewFlaskResource() resource.Resource { - return &FlaskResource{} -} - -func (r *FlaskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_flask" -} - -func (r *FlaskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("flask-y0lfavjf"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3.12")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/Foxyhhd/Baldurs-Gate-test.git"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *FlaskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *FlaskModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1037: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1038: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64())}), - 1039: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1040: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1041: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 89, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *FlaskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1037: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1038: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64())}), - 1039: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1040: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1041: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 89, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *FlaskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 89, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *FlaskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan FlaskModel - var state FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1042: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1043: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1044: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 89, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *FlaskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *FlaskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *FlaskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/90_postgres_backup_resource.go b/provider/internal/resources_gen/90_postgres_backup_resource.go deleted file mode 100644 index 957eb3e..0000000 --- a/provider/internal/resources_gen/90_postgres_backup_resource.go +++ /dev/null @@ -1,326 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: backup - -var _ resource.Resource = &PostgresBackupResource{} - -// PostgresBackupResource manages a subresource via operations. -type PostgresBackupResource struct { - client *core.UniversalClient -} - -type PostgresBackupModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewPostgresBackupResource() resource.Resource { - return &PostgresBackupResource{} -} - -func (r *PostgresBackupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_backup" -} - -func (r *PostgresBackupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresBackupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresBackupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresBackupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresBackupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresBackupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/90_postgres_database_resource.go b/provider/internal/resources_gen/90_postgres_database_resource.go deleted file mode 100644 index 7310720..0000000 --- a/provider/internal/resources_gen/90_postgres_database_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: database - -var _ resource.Resource = &PostgresDatabaseResource{} - -// PostgresDatabaseResource manages a subresource via operations. -type PostgresDatabaseResource struct { - client *core.UniversalClient -} - -type PostgresDatabaseModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` - DbOwner types.String `tfsdk:"db_owner"` -} - -func NewPostgresDatabaseResource() resource.Resource { - return &PostgresDatabaseResource{} -} - -func (r *PostgresDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_database" -} - -func (r *PostgresDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_owner": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.DbName.IsNull() || plan.DbName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") - return - } - if plan.DbOwner.IsNull() || plan.DbOwner.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_owner is required.") - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - } - identityCodes := []string{ - "dbName", - "dbOwner", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - "dbOwner": resources_core.FormatString(state.DbOwner), - } - identityCodes := []string{ - "dbName", - "dbOwner", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/90_postgres_resource.go b/provider/internal/resources_gen/90_postgres_resource.go deleted file mode 100644 index 6924e3a..0000000 --- a/provider/internal/resources_gen/90_postgres_resource.go +++ /dev/null @@ -1,499 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Service ID: 90 - -var _ resource.Resource = &PostgresResource{} -var _ resource.ResourceWithModifyPlan = &PostgresResource{} -var _ resource.ResourceWithImportState = &PostgresResource{} - -type PostgresResource struct { - client *core.UniversalClient -} - -// PostgresAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type PostgresAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` -} - -// PostgresAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type PostgresAutoscaleConfigurationModel struct { - Percent types.Int64 `tfsdk:"percent" json:"percent"` - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` -} - -// PostgresBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type PostgresBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// PostgresClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type PostgresClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// PostgresMtlsConfigurationModel — вложенная модель для map-fixed параметра mtlsConfiguration. -type PostgresMtlsConfigurationModel struct { - Type types.String `tfsdk:"type" json:"type"` - DurationCA types.Int64 `tfsdk:"duration_c_a" json:"durationCA"` - DurationServer types.Int64 `tfsdk:"duration_server" json:"durationServer"` - CertCA types.String `tfsdk:"cert_c_a" json:"certCA"` - CertServer types.String `tfsdk:"cert_server" json:"certServer"` -} - -// PostgresPostgresConfigurationModel — вложенная модель для map-fixed параметра postgresConfiguration. -type PostgresPostgresConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - SslRequired types.Bool `tfsdk:"ssl_required" json:"sslRequired"` - PoolerMaster types.Bool `tfsdk:"pooler_master" json:"poolerMaster"` - PoolerSlave types.Bool `tfsdk:"pooler_slave" json:"poolerSlave"` -} - -// PostgresStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type PostgresStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type PostgresModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *PostgresAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *PostgresAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *PostgresBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *PostgresClusterConfigurationModel `tfsdk:"cluster_configuration"` - MtlsConfiguration *PostgresMtlsConfigurationModel `tfsdk:"mtls_configuration"` - PostgresConf types.String `tfsdk:"postgres_conf"` - PostgresConfiguration *PostgresPostgresConfigurationModel `tfsdk:"postgres_configuration"` - StartupConfiguration *PostgresStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewPostgresResource() resource.Resource { - return &PostgresResource{} -} - -func (r *PostgresResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres" -} - -func (r *PostgresResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "slave_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - }}, - "mtls_configuration": schema.SingleNestedAttribute{Optional: true, Computed: true, - Attributes: map[string]schema.Attribute{ - "type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("off"), MarkdownDescription: "`off` - Настройка MTLS производиться не будет
`manual` - После создания кластера в полях данных будет запрос на создание сертификата. **Вам необходимо будет выписать сертификат через вашу CA** и уже после включить через операцию **modify** режим MTLS
`auto` - Сертификаты будут созданы через Cert-Manager

Режимы **auto** и **manual** требует включить настройку `Включить sslMode` - **true**
MTLS в режиме **auto** будет работать сразу, в режиме **manual** только после того как будет предоставлен **CA** и **Сертификат сервера**"}, - "duration_c_a": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(175200), MarkdownDescription: "Указывается в **часах**
Необходимо настраивать, когда параметр `Режим настройки` - **auto**"}, - "duration_server": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(87600), MarkdownDescription: "Указывается в **часах**
Не может быть больше чем время жизни СА
Обновляется **раз в год**, если выставлено значение **более 2 лет**
Обновляется **раз в пол года**, если выставлено значение **менее 2 лет**
Необходимо настраивать, когда параметр `Режим настройки` - **auto**"}, - "cert_c_a": schema.StringAttribute{Optional: true}, - "cert_server": schema.StringAttribute{Optional: true}, - }}, - "postgres_conf": schema.StringAttribute{Optional: true}, - "postgres_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("17")}, - "ssl_required": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "По умолчанию подключение обязательное (sslmode=require)"}, - "pooler_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi**"}, - "pooler_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi**"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *PostgresModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 788: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 789: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 790: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": config.AccessConfiguration.MasterAccessList.ValueString(), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": config.AccessConfiguration.SlaveAccessList.ValueString()}), - 791: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", config.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", config.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", config.PostgresConfiguration.PoolerSlave.ValueBool())}), - 792: resources_core.FormatString(config.PostgresConf), - 793: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 794: resources_core.BuildJSON(map[string]string{"percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64())}), - 1094: resources_core.BuildJSON(map[string]string{"type": fmt.Sprintf("\"%s\"", config.MtlsConfiguration.Type.ValueString()), "durationCA": fmt.Sprintf("%d", config.MtlsConfiguration.DurationCA.ValueInt64()), "durationServer": fmt.Sprintf("%d", config.MtlsConfiguration.DurationServer.ValueInt64()), "certCA": fmt.Sprintf("\"%s\"", config.MtlsConfiguration.CertCA.ValueString()), "certServer": fmt.Sprintf("\"%s\"", config.MtlsConfiguration.CertServer.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 90, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *PostgresResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 788: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 789: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 790: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": data.AccessConfiguration.MasterAccessList.ValueString(), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": data.AccessConfiguration.SlaveAccessList.ValueString()}), - 791: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", data.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", data.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", data.PostgresConfiguration.PoolerSlave.ValueBool())}), - 792: resources_core.FormatString(data.PostgresConf), - 793: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 794: resources_core.BuildJSON(map[string]string{"percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64())}), - 1094: resources_core.BuildJSON(map[string]string{"type": fmt.Sprintf("\"%s\"", data.MtlsConfiguration.Type.ValueString()), "durationCA": fmt.Sprintf("%d", data.MtlsConfiguration.DurationCA.ValueInt64()), "durationServer": fmt.Sprintf("%d", data.MtlsConfiguration.DurationServer.ValueInt64()), "certCA": fmt.Sprintf("\"%s\"", data.MtlsConfiguration.CertCA.ValueString()), "certServer": fmt.Sprintf("\"%s\"", data.MtlsConfiguration.CertServer.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 90, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mtlsConfiguration", Field: "MtlsConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 90, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mtlsConfiguration", Field: "MtlsConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *PostgresResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresModel - var state PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.PostgresConf.IsNull() != state.PostgresConf.IsNull() || plan.PostgresConf.IsUnknown() != state.PostgresConf.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.PostgresConf.IsNull() && !plan.PostgresConf.IsUnknown() && !state.PostgresConf.IsNull() && !state.PostgresConf.IsUnknown() { - if !strings.EqualFold(plan.PostgresConf.ValueString(), state.PostgresConf.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 795: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 796: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": plan.AccessConfiguration.MasterAccessList.ValueString(), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": plan.AccessConfiguration.SlaveAccessList.ValueString()}), - 797: resources_core.BuildJSON(map[string]string{"poolerSlave": fmt.Sprintf("%v", plan.PostgresConfiguration.PoolerSlave.ValueBool()), "version": fmt.Sprintf("\"%s\"", plan.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", plan.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", plan.PostgresConfiguration.PoolerMaster.ValueBool())}), - 798: resources_core.FormatString(plan.PostgresConf), - 799: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 800: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - 1095: resources_core.BuildJSON(map[string]string{"certServer": fmt.Sprintf("\"%s\"", plan.MtlsConfiguration.CertServer.ValueString()), "type": fmt.Sprintf("\"%s\"", plan.MtlsConfiguration.Type.ValueString()), "durationCA": fmt.Sprintf("%d", plan.MtlsConfiguration.DurationCA.ValueInt64()), "durationServer": fmt.Sprintf("%d", plan.MtlsConfiguration.DurationServer.ValueInt64()), "certCA": fmt.Sprintf("\"%s\"", plan.MtlsConfiguration.CertCA.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 90, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mtlsConfiguration", Field: "MtlsConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *PostgresResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *PostgresResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/90_postgres_user_resource.go b/provider/internal/resources_gen/90_postgres_user_resource.go deleted file mode 100644 index 8ac874c..0000000 --- a/provider/internal/resources_gen/90_postgres_user_resource.go +++ /dev/null @@ -1,367 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: user - -var _ resource.Resource = &PostgresUserResource{} - -// PostgresUserResource manages a subresource via operations. -type PostgresUserResource struct { - client *core.UniversalClient -} - -type PostgresUserModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - MtlsAccess types.Bool `tfsdk:"mtls_access"` - Role types.String `tfsdk:"role"` - Username types.String `tfsdk:"username"` -} - -func NewPostgresUserResource() resource.Resource { - return &PostgresUserResource{} -} - -func (r *PostgresUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_user" -} - -func (r *PostgresUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "mtls_access": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Подключение пользователя без ввода паролей
Политика применяется **ко всем базам** внутри Postgresql

RBAC на базы настраивать вручную Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Bool{boolplanmodifier.RequiresReplace()}}, - "role": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать роль из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Role.IsNull() || plan.Role.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "role is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "mtlsAccess": resources_core.FormatBool(plan.MtlsAccess), - "role": resources_core.FormatString(plan.Role), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "mtlsAccess", - "role", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "role": resources_core.FormatString(plan.Role), - "mtlsAccess": resources_core.FormatBool(plan.MtlsAccess), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "mtlsAccess": resources_core.FormatBool(plan.MtlsAccess), - "role": resources_core.FormatString(plan.Role), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "mtlsAccess": resources_core.FormatBool(state.MtlsAccess), - "role": resources_core.FormatString(state.Role), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "mtlsAccess", - "role", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/91_redis_resource.go b/provider/internal/resources_gen/91_redis_resource.go deleted file mode 100644 index 58f2336..0000000 --- a/provider/internal/resources_gen/91_redis_resource.go +++ /dev/null @@ -1,393 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: redis -// Service ID: 91 - -var _ resource.Resource = &RedisResource{} -var _ resource.ResourceWithModifyPlan = &RedisResource{} -var _ resource.ResourceWithImportState = &RedisResource{} - -type RedisResource struct { - client *core.UniversalClient -} - -// RedisAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type RedisAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` -} - -// RedisClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type RedisClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// RedisStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type RedisStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type RedisModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *RedisAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *RedisClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *RedisStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewRedisResource() resource.Resource { - return &RedisResource{} -} - -func (r *RedisResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_redis" -} - -func (r *RedisResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "slave_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *RedisResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *RedisModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1061: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1062: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1063: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": config.AccessConfiguration.MasterAccessList.ValueString(), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": config.AccessConfiguration.SlaveAccessList.ValueString()}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 91, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *RedisResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1061: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1062: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1063: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": data.AccessConfiguration.MasterAccessList.ValueString(), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": data.AccessConfiguration.SlaveAccessList.ValueString()}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 91, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RedisResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 91, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *RedisResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan RedisModel - var state RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1064: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 1065: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": plan.AccessConfiguration.MasterAccessList.ValueString(), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": plan.AccessConfiguration.SlaveAccessList.ValueString()}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 91, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RedisResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *RedisResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *RedisResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/92_mongodb_resource.go b/provider/internal/resources_gen/92_mongodb_resource.go deleted file mode 100644 index 81626ac..0000000 --- a/provider/internal/resources_gen/92_mongodb_resource.go +++ /dev/null @@ -1,389 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mongodb -// Service ID: 92 - -var _ resource.Resource = &MongodbResource{} -var _ resource.ResourceWithModifyPlan = &MongodbResource{} -var _ resource.ResourceWithImportState = &MongodbResource{} - -type MongodbResource struct { - client *core.UniversalClient -} - -// MongodbAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type MongodbAccessConfigurationModel struct { - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` -} - -// MongodbClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type MongodbClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// MongodbStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type MongodbStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type MongodbModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *MongodbAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *MongodbClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *MongodbStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewMongodbResource() resource.Resource { - return &MongodbResource{} -} - -func (r *MongodbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mongodb" -} - -func (r *MongodbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024)}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(5), MarkdownDescription: "Указывается в Gb"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MongodbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *MongodbModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1089: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1090: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 1091: resources_core.BuildJSON(map[string]string{"masterAccessList": config.AccessConfiguration.MasterAccessList.ValueString(), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 92, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *MongodbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1089: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1090: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 1091: resources_core.BuildJSON(map[string]string{"masterAccessList": data.AccessConfiguration.MasterAccessList.ValueString(), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 92, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 92, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *MongodbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MongodbModel - var state MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1092: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 1093: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": plan.AccessConfiguration.MasterAccessList.ValueString()}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 92, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *MongodbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *MongodbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/92_mongodb_user_resource.go b/provider/internal/resources_gen/92_mongodb_user_resource.go deleted file mode 100644 index 6f0e40d..0000000 --- a/provider/internal/resources_gen/92_mongodb_user_resource.go +++ /dev/null @@ -1,364 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mongodb -// Subresource: user - -var _ resource.Resource = &MongodbUserResource{} - -// MongodbUserResource manages a subresource via operations. -type MongodbUserResource struct { - client *core.UniversalClient -} - -type MongodbUserModel struct { - ID types.String `tfsdk:"id"` - MongodbID types.String `tfsdk:"mongodb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` - Role types.String `tfsdk:"role"` - Username types.String `tfsdk:"username"` -} - -func NewMongodbUserResource() resource.Resource { - return &MongodbUserResource{} -} - -func (r *MongodbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mongodb_user" -} - -func (r *MongodbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mongodb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Required: true}, - "role": schema.StringAttribute{Required: true}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MongodbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MongodbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.DbName.IsNull() || plan.DbName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") - return - } - if plan.Role.IsNull() || plan.Role.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "role is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "role": resources_core.FormatString(plan.Role), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MongodbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MongodbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_user" == "" { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "role": resources_core.FormatString(plan.Role), - }) - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MongodbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MongodbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MongodbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/93_rabbitmq_resource.go b/provider/internal/resources_gen/93_rabbitmq_resource.go deleted file mode 100644 index a43cd4d..0000000 --- a/provider/internal/resources_gen/93_rabbitmq_resource.go +++ /dev/null @@ -1,474 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: rabbitmq -// Service ID: 93 - -var _ resource.Resource = &RabbitmqResource{} -var _ resource.ResourceWithModifyPlan = &RabbitmqResource{} -var _ resource.ResourceWithImportState = &RabbitmqResource{} - -type RabbitmqResource struct { - client *core.UniversalClient -} - -type RabbitmqModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` - MasterAccessList types.String `tfsdk:"master_access_list"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewRabbitmqResource() resource.Resource { - return &RabbitmqResource{} -} - -func (r *RabbitmqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_rabbitmq" -} - -func (r *RabbitmqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "ip_space_name_master": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Список IP-адресов, с которых разрешён доступ. Если оставить пустым — доступ открыт всем.", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Пример: `2000`"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(30), MarkdownDescription: "Пример: `20`"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1124), MarkdownDescription: "Пример: `2048`"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *RabbitmqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *RabbitmqModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.IpSpaceNameMaster.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name_master is required.") - return - } - if config.IpSpaceNameMaster.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 116: resources_core.FormatInt64(config.ResourceCPU), - 117: resources_core.FormatInt64(config.ResourceMemory), - 118: resources_core.FormatInt64(config.ResourceDisk), - 119: resources_core.FormatString(config.ResourceRealm), - 224: resources_core.FormatInt64(config.ResourceInstances), - 225: resources_core.FormatString(config.MasterAccessList), - 543: resources_core.FormatString(config.IpSpaceNameMaster), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 93, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *RabbitmqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.IpSpaceNameMaster.IsNull() || data.IpSpaceNameMaster.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name_master is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 116: resources_core.FormatInt64(data.ResourceCPU), - 117: resources_core.FormatInt64(data.ResourceMemory), - 118: resources_core.FormatInt64(data.ResourceDisk), - 119: resources_core.FormatString(data.ResourceRealm), - 224: resources_core.FormatInt64(data.ResourceInstances), - 225: resources_core.FormatString(data.MasterAccessList), - 543: resources_core.FormatString(data.IpSpaceNameMaster), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 93, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "masterAccessList", Field: "MasterAccessList", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RabbitmqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 93, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "masterAccessList", Field: "MasterAccessList", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *RabbitmqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan RabbitmqModel - var state RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MasterAccessList.IsNull() != state.MasterAccessList.IsNull() || plan.MasterAccessList.IsUnknown() != state.MasterAccessList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MasterAccessList.IsNull() && !plan.MasterAccessList.IsUnknown() && !state.MasterAccessList.IsNull() && !state.MasterAccessList.IsUnknown() { - if !strings.EqualFold(plan.MasterAccessList.ValueString(), state.MasterAccessList.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceNameMaster.ValueString(), state.IpSpaceNameMaster.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 304: resources_core.FormatInt64(plan.ResourceCPU), - 305: resources_core.FormatInt64(plan.ResourceMemory), - 306: resources_core.FormatInt64(plan.ResourceDisk), - 307: resources_core.FormatInt64(plan.ResourceInstances), - 308: resources_core.FormatString(plan.MasterAccessList), - 545: resources_core.FormatString(plan.IpSpaceNameMaster), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 93, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "masterAccessList", Field: "MasterAccessList", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RabbitmqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *RabbitmqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *RabbitmqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/94_lucee_resource.go b/provider/internal/resources_gen/94_lucee_resource.go deleted file mode 100644 index d78129f..0000000 --- a/provider/internal/resources_gen/94_lucee_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: lucee -// Service ID: 94 - -var _ resource.Resource = &LuceeResource{} -var _ resource.ResourceWithModifyPlan = &LuceeResource{} -var _ resource.ResourceWithImportState = &LuceeResource{} - -type LuceeResource struct { - client *core.UniversalClient -} - -// LuceeAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type LuceeAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// LuceeAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type LuceeAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// LuceeClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type LuceeClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// LuceeStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type LuceeStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type LuceeModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *LuceeAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *LuceeAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *LuceeClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *LuceeStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewLuceeResource() resource.Resource { - return &LuceeResource{} -} - -func (r *LuceeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_lucee" -} - -func (r *LuceeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("lucee-kctea8wd"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("6.0")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testlucee"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LuceeResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *LuceeModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1019: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1020: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1021: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1022: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1023: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 94, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *LuceeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1019: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1020: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1021: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1022: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1023: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 94, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LuceeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 94, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *LuceeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LuceeModel - var state LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1024: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1025: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1026: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 94, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LuceeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *LuceeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *LuceeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/95_nodejs_resource.go b/provider/internal/resources_gen/95_nodejs_resource.go deleted file mode 100644 index 10900b8..0000000 --- a/provider/internal/resources_gen/95_nodejs_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nodejs -// Service ID: 95 - -var _ resource.Resource = &NodejsResource{} -var _ resource.ResourceWithModifyPlan = &NodejsResource{} -var _ resource.ResourceWithImportState = &NodejsResource{} - -type NodejsResource struct { - client *core.UniversalClient -} - -// NodejsAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type NodejsAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// NodejsAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type NodejsAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// NodejsClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type NodejsClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// NodejsStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type NodejsStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type NodejsModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *NodejsAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *NodejsAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *NodejsClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *NodejsStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNodejsResource() resource.Resource { - return &NodejsResource{} -} - -func (r *NodejsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nodejs" -} - -func (r *NodejsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("node-bd1l2h8o"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("23")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testnode.git"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NodejsResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NodejsModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1028: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1029: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1030: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1031: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1032: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 95, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NodejsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1028: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1029: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1030: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1031: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1032: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 95, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NodejsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 95, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NodejsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NodejsModel - var state NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1033: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1034: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1035: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 95, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NodejsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NodejsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NodejsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/96_pgadmin_resource.go b/provider/internal/resources_gen/96_pgadmin_resource.go deleted file mode 100644 index aac4518..0000000 --- a/provider/internal/resources_gen/96_pgadmin_resource.go +++ /dev/null @@ -1,515 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: pgadmin -// Service ID: 96 - -var _ resource.Resource = &PgadminResource{} -var _ resource.ResourceWithModifyPlan = &PgadminResource{} -var _ resource.ResourceWithImportState = &PgadminResource{} - -type PgadminResource struct { - client *core.UniversalClient -} - -type PgadminModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Domain types.String `tfsdk:"domain"` - Login types.String `tfsdk:"login"` - Password types.String `tfsdk:"password"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewPgadminResource() resource.Resource { - return &PgadminResource{} -} - -func (r *PgadminResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_pgadmin" -} - -func (r *PgadminResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("pgadmin-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "login": schema.StringAttribute{Required: true, MarkdownDescription: "Логин для подключения к pgAdmin, в формате email (login@email.net) Не изменяется после создания."}, - "password": schema.StringAttribute{Required: true, MarkdownDescription: "Пароль для подключения к pgAdmin Не изменяется после создания.", Sensitive: true}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores). 1000Mili = 1 Ядро"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes)"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PgadminResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *PgadminModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Login.IsNull() && !plan.Login.IsUnknown() && !state.Login.IsNull() && !state.Login.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Login.ValueString(), state.Login.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Password.IsNull() && !plan.Password.IsUnknown() && !state.Password.IsNull() && !state.Password.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Password.ValueString(), state.Password.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить password", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Login.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "login is required.") - return - } - if config.Login.IsUnknown() { - return - } - if config.Password.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "password is required.") - return - } - if config.Password.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 164: resources_core.FormatString(config.Domain), - 165: resources_core.FormatInt64(config.ResourceCPU), - 166: resources_core.FormatInt64(config.ResourceMemory), - 167: resources_core.FormatInt64(config.ResourceDisk), - 169: resources_core.FormatString(config.ResourceRealm), - 170: resources_core.FormatString(config.Login), - 171: resources_core.FormatString(config.Password), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 96, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *PgadminResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Login.IsNull() || data.Login.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "login is required.") - return - } - if data.Password.IsNull() || data.Password.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "password is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 164: resources_core.FormatString(data.Domain), - 165: resources_core.FormatInt64(data.ResourceCPU), - 166: resources_core.FormatInt64(data.ResourceMemory), - 167: resources_core.FormatInt64(data.ResourceDisk), - 169: resources_core.FormatString(data.ResourceRealm), - 170: resources_core.FormatString(data.Login), - 171: resources_core.FormatString(data.Password), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 96, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PgadminResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 96, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *PgadminResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PgadminModel - var state PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 172: resources_core.FormatInt64(plan.ResourceCPU), - 173: resources_core.FormatInt64(plan.ResourceMemory), - 174: resources_core.FormatInt64(plan.ResourceDisk), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 96, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PgadminResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *PgadminResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *PgadminResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/97_nodered_resource.go b/provider/internal/resources_gen/97_nodered_resource.go deleted file mode 100644 index 7b00a75..0000000 --- a/provider/internal/resources_gen/97_nodered_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nodered -// Service ID: 97 - -var _ resource.Resource = &NoderedResource{} -var _ resource.ResourceWithModifyPlan = &NoderedResource{} -var _ resource.ResourceWithImportState = &NoderedResource{} - -type NoderedResource struct { - client *core.UniversalClient -} - -type NoderedModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNoderedResource() resource.Resource { - return &NoderedResource{} -} - -func (r *NoderedResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nodered" -} - -func (r *NoderedResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nodered-1"), MarkdownDescription: "Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NoderedResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NoderedModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 193: resources_core.FormatInt64(config.ResourceCPU), - 194: resources_core.FormatInt64(config.ResourceMemory), - 195: resources_core.FormatInt64(config.ResourceDisk), - 196: resources_core.FormatString(config.ResourceRealm), - 197: resources_core.FormatString(config.Domain), - 323: resources_core.FormatInt64(config.ResourceInstances), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 97, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NoderedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 193: resources_core.FormatInt64(data.ResourceCPU), - 194: resources_core.FormatInt64(data.ResourceMemory), - 195: resources_core.FormatInt64(data.ResourceDisk), - 196: resources_core.FormatString(data.ResourceRealm), - 197: resources_core.FormatString(data.Domain), - 323: resources_core.FormatInt64(data.ResourceInstances), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 97, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NoderedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 97, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NoderedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NoderedModel - var state NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 97, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NoderedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NoderedResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NoderedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/98_http_resource.go b/provider/internal/resources_gen/98_http_resource.go deleted file mode 100644 index 5d287fa..0000000 --- a/provider/internal/resources_gen/98_http_resource.go +++ /dev/null @@ -1,509 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: http -// Service ID: 98 - -var _ resource.Resource = &HttpResource{} -var _ resource.ResourceWithModifyPlan = &HttpResource{} -var _ resource.ResourceWithImportState = &HttpResource{} - -type HttpResource struct { - client *core.UniversalClient -} - -type HttpModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Domain types.String `tfsdk:"domain"` - IngressAnnotations types.String `tfsdk:"ingress_annotations"` - JsonEnv types.String `tfsdk:"json_env"` - RegistryPath types.String `tfsdk:"registry_path"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewHttpResource() resource.Resource { - return &HttpResource{} -} - -func (r *HttpResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_http" -} - -func (r *HttpResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("http-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "ingress_annotations": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Формат ключ/значение
Поддерживает пока простые аннотации Не изменяется после создания."}, - "json_env": schema.StringAttribute{Optional: true, MarkdownDescription: "Массив переменных, будут переданы в env приложения Не изменяется после создания."}, - "registry_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nexus-sa.tst.nubes.ru/docker-nubes/jolt"), MarkdownDescription: "Путь до image, который будет установлен как deployment Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili)"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *HttpResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *HttpModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.IngressAnnotations.IsNull() && !plan.IngressAnnotations.IsUnknown() && !state.IngressAnnotations.IsNull() && !state.IngressAnnotations.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IngressAnnotations.ValueString(), state.IngressAnnotations.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ingress_annotations", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить json_env", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RegistryPath.IsNull() && !plan.RegistryPath.IsUnknown() && !state.RegistryPath.IsNull() && !state.RegistryPath.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RegistryPath.ValueString(), state.RegistryPath.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить registry_path", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 212: resources_core.FormatString(config.Domain), - 213: resources_core.FormatInt64(config.ResourceCPU), - 214: resources_core.FormatInt64(config.ResourceMemory), - 215: resources_core.FormatInt64(config.ResourceInstances), - 216: resources_core.FormatString(config.ResourceRealm), - 217: resources_core.FormatString(config.RegistryPath), - 241: resources_core.FormatString(config.JsonEnv), - 782: resources_core.FormatString(config.IngressAnnotations), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 98, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *HttpResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 212: resources_core.FormatString(data.Domain), - 213: resources_core.FormatInt64(data.ResourceCPU), - 214: resources_core.FormatInt64(data.ResourceMemory), - 215: resources_core.FormatInt64(data.ResourceInstances), - 216: resources_core.FormatString(data.ResourceRealm), - 217: resources_core.FormatString(data.RegistryPath), - 241: resources_core.FormatString(data.JsonEnv), - 782: resources_core.FormatString(data.IngressAnnotations), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 98, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HttpResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 98, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *HttpResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan HttpModel - var state HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 218: resources_core.FormatInt64(plan.ResourceCPU), - 219: resources_core.FormatInt64(plan.ResourceMemory), - 220: resources_core.FormatInt64(plan.ResourceInstances), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 98, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HttpResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *HttpResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *HttpResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/99_gitea_resource.go b/provider/internal/resources_gen/99_gitea_resource.go deleted file mode 100644 index 97139b1..0000000 --- a/provider/internal/resources_gen/99_gitea_resource.go +++ /dev/null @@ -1,579 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "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/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: gitea -// Service ID: 99 - -var _ resource.Resource = &GiteaResource{} -var _ resource.ResourceWithModifyPlan = &GiteaResource{} -var _ resource.ResourceWithImportState = &GiteaResource{} - -type GiteaResource struct { - client *core.UniversalClient -} - -type GiteaModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - IngressExtraAnnotations types.String `tfsdk:"ingress_extra_annotations"` - IniServerParams types.String `tfsdk:"ini_server_params"` - PsqlUid types.String `tfsdk:"psql_uid"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewGiteaResource() resource.Resource { - return &GiteaResource{} -} - -func (r *GiteaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_gitea" -} - -func (r *GiteaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("gitea-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "ingress_extra_annotations": schema.StringAttribute{Optional: true}, - "ini_server_params": schema.StringAttribute{Optional: true}, - "psql_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *GiteaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *GiteaModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.PsqlUid.ValueString(), state.PsqlUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить psql_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.PsqlUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") - return - } - if config.PsqlUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.PsqlUid.IsNull() && !config.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, config.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != config.PsqlUid.ValueString() { - config.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - params := map[int]string{ - 232: resources_core.FormatInt64(config.ResourceCPU), - 233: resources_core.FormatInt64(config.ResourceMemory), - 234: resources_core.FormatInt64(config.ResourceDisk), - 235: resources_core.FormatInt64(config.ResourceInstances), - 237: resources_core.FormatString(config.Domain), - 238: resources_core.FormatString(config.PsqlUid), - 818: resources_core.FormatString(config.IngressExtraAnnotations), - 821: resources_core.FormatString(config.IniServerParams), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 99, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *GiteaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.PsqlUid.IsNull() || data.PsqlUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalPsqlUid := data.PsqlUid - if !data.PsqlUid.IsNull() && !data.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, data.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedPsqlUid != "" && resolvedPsqlUid != data.PsqlUid.ValueString() { - data.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 232: resources_core.FormatInt64(data.ResourceCPU), - 233: resources_core.FormatInt64(data.ResourceMemory), - 234: resources_core.FormatInt64(data.ResourceDisk), - 235: resources_core.FormatInt64(data.ResourceInstances), - 237: resources_core.FormatString(data.Domain), - 238: resources_core.FormatString(data.PsqlUid), - 818: resources_core.FormatString(data.IngressExtraAnnotations), - 821: resources_core.FormatString(data.IniServerParams), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 99, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { - state.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalPsqlUid.IsNull() && !originalPsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - if strings.EqualFold(state.PsqlUid.ValueString(), originalPsqlUid.ValueString()) { - state.PsqlUid = originalPsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 99, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, newState.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != newState.PsqlUid.ValueString() { - newState.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() && !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { - if strings.EqualFold(newState.PsqlUid.ValueString(), state.PsqlUid.ValueString()) { - newState.PsqlUid = state.PsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *GiteaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan GiteaModel - var state GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IngressExtraAnnotations.IsNull() != state.IngressExtraAnnotations.IsNull() || plan.IngressExtraAnnotations.IsUnknown() != state.IngressExtraAnnotations.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IngressExtraAnnotations.IsNull() && !plan.IngressExtraAnnotations.IsUnknown() && !state.IngressExtraAnnotations.IsNull() && !state.IngressExtraAnnotations.IsUnknown() { - if !strings.EqualFold(plan.IngressExtraAnnotations.ValueString(), state.IngressExtraAnnotations.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IniServerParams.IsNull() != state.IniServerParams.IsNull() || plan.IniServerParams.IsUnknown() != state.IniServerParams.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IniServerParams.IsNull() && !plan.IniServerParams.IsUnknown() && !state.IniServerParams.IsNull() && !state.IniServerParams.IsUnknown() { - if !strings.EqualFold(plan.IniServerParams.ValueString(), state.IniServerParams.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 259: resources_core.FormatInt64(plan.ResourceCPU), - 260: resources_core.FormatInt64(plan.ResourceMemory), - 261: resources_core.FormatInt64(plan.ResourceDisk), - 262: resources_core.FormatInt64(plan.ResourceInstances), - 819: resources_core.FormatString(plan.IngressExtraAnnotations), - 820: resources_core.FormatString(plan.IniServerParams), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 99, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { - state.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - if strings.EqualFold(state.PsqlUid.ValueString(), plan.PsqlUid.ValueString()) { - state.PsqlUid = plan.PsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *GiteaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *GiteaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/registry.go b/provider/internal/resources_gen/registry.go deleted file mode 100644 index 893cf0d..0000000 --- a/provider/internal/resources_gen/registry.go +++ /dev/null @@ -1,81 +0,0 @@ -package resources_gen - -import "github.com/hashicorp/terraform-plugin-framework/resource" - -// Code generated by tools/gen_v2. DO NOT EDIT. -func AllResources() []func() resource.Resource { - return []func() resource.Resource{ - NewAkhqResource, - NewClickhouseResource, - NewDnsrecordResource, - NewDnszoneResource, - NewDummyResource, - NewFlaskResource, - NewGiteaResource, - NewHarborResource, - NewHttpResource, - NewK8sGoResource, - NewK8sOpenbaoResource, - NewK8sSthutrvalClusterResource, - NewK8sVeleroResource, - NewK8sZitiControllerResource, - NewK8svalkeyResource, - NewKafkaResource, - NewLlmAiResource, - NewLuceeResource, - NewMariadbResource, - NewMongodbResource, - NewNextcloudResource, - NewNifiResource, - NewNodejsResource, - NewNoderedResource, - NewOpenwhiskResource, - NewPgadminResource, - NewPostgresResource, - NewRabbitmqResource, - NewRedisResource, - NewS3Resource, - NewS3bucketResource, - NewSupersetResource, - NewTemplateResource, - NewTenantResource, - NewValoTenantResource, - NewVappResource, - NewVcComplexResource, - NewVcMgmtSthutrvalClusterResource, - NewVcNatResource, - NewVcNsxtResource, - NewVcOrgResource, - NewVcOrgSaasResource, - NewVcVdcResource, - NewVcVdcGroupResource, - NewVcVmResource, - NewVcVmV2Resource, - NewVcVmV3Resource, - NewVcexternalipResource, - NewVmpostgreResource, - NewZonesV2Resource, - NewClickhouseDatabaseResource, - NewClickhouseUserResource, - NewK8sOpenbaoKvResource, - NewK8sOpenbaoPolicyResource, - NewK8sOpenbaoSecretsResource, - NewK8sSthutrvalClusterUserResource, - NewK8svalkeyUserResource, - NewKafkaTopicResource, - NewKafkaUserResource, - NewLlmAiSecretsResource, - NewLlmAiTeamResource, - NewMariadbDatabaseResource, - NewMariadbUserResource, - NewMongodbUserResource, - NewNextcloudBackupResource, - NewPostgresBackupResource, - NewPostgresDatabaseResource, - NewPostgresUserResource, - NewS3SubUserResource, - NewVcOrgUserResource, - NewVcVdcGroupVdcResource, - NewZonesV2RecordResource, - } -}