diff --git a/universal_rebuild/internal/resources_gen/116_kafka_topic_resource.go b/universal_rebuild/internal/resources_gen/116_kafka_topic_resource.go new file mode 100644 index 0000000..5bdf934 --- /dev/null +++ b/universal_rebuild/internal/resources_gen/116_kafka_topic_resource.go @@ -0,0 +1,340 @@ +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"` + 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}, + "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(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "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 err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout); err != nil { + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(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 { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в 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("Нарушена консистентность", "Операция 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 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 { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+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 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/universal_rebuild/internal/resources_gen/116_kafka_user_resource.go b/universal_rebuild/internal/resources_gen/116_kafka_user_resource.go new file mode 100644 index 0000000..ede89d8 --- /dev/null +++ b/universal_rebuild/internal/resources_gen/116_kafka_user_resource.go @@ -0,0 +1,356 @@ +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: 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"` + 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}, + "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(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "access_hosts": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `192.168.1.0/24` или `*`"}, + "group": schema.StringAttribute{Required: true, 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.AccessHosts.IsNull() || plan.AccessHosts.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "access_hosts is required.") + return + } + if plan.Group.IsNull() || plan.Group.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "group is required.") + 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 err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout); err != nil { + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(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 { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в 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("Нарушена консистентность", "Операция 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 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 { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+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 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/universal_rebuild/internal/resources_gen/117_nifi_resource.go b/universal_rebuild/internal/resources_gen/117_nifi_resource.go new file mode 100644 index 0000000..d616f15 --- /dev/null +++ b/universal_rebuild/internal/resources_gen/117_nifi_resource.go @@ -0,0 +1,461 @@ +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/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: nifi +// Service ID: 117 + +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"` + KafkaUid types.String `tfsdk:"kafka_uid"` + NameTopic types.String `tfsdk:"name_topic"` + Partitions types.Int64 `tfsdk:"partitions"` + Replicas types.Int64 `tfsdk:"replicas"` + 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}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "kafka_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать кластер из списка Не изменяется после создания."}, + "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Не изменяется после создания."}, + "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50`"}, + "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество партиций ≤ (число брокеров). Пример `2`"}, + "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 + } + planChanged := false + if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, plan.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != plan.KafkaUid.ValueString() { + plan.KafkaUid = types.StringValue(resolvedKafkaUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(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.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + if plan.KafkaUid.ValueString() != state.KafkaUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить kafka_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NameTopic.IsNull() && !plan.NameTopic.IsUnknown() && !state.NameTopic.IsNull() && !state.NameTopic.IsUnknown() { + if plan.NameTopic.ValueString() != state.NameTopic.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить name_topic", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.KafkaUid.IsNull() || config.KafkaUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") + return + } + if config.NameTopic.IsNull() || config.NameTopic.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") + return + } + if config.Partitions.IsNull() || config.Partitions.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") + return + } + if config.Replicas.IsNull() || config.Replicas.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") + 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{ + 471: resources_core.FormatString(config.KafkaUid), + 472: resources_core.FormatInt64(config.Partitions), + 473: resources_core.FormatInt64(config.Replicas), + 474: resources_core.FormatString(config.NameTopic), + } + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParams(ctx, r.client, 117, config.ResourceName.ValueString(), adoptExistingOnCreate, params)...) +} + +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.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") + return + } + if data.NameTopic.IsNull() || data.NameTopic.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") + return + } + if data.Partitions.IsNull() || data.Partitions.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") + return + } + if data.Replicas.IsNull() || data.Replicas.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") + return + } + 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() + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnostics(ctx, r.client, 117, resourceName, data.AdoptExistingOnCreate.ValueBool())...) + + params := map[int]string{ + 471: resources_core.FormatString(data.KafkaUid), + 472: resources_core.FormatInt64(data.Partitions), + 473: resources_core.FormatInt64(data.Replicas), + 474: resources_core.FormatString(data.NameTopic), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 117, 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, 117, 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: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "nameTopic", Field: "NameTopic", Type: "string"}, + {Code: "partitions", Field: "Partitions", Type: "int64"}, + {Code: "replicas", Field: "Replicas", Type: "int64"}, + }) + 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) + } + } + + 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(), 117, 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: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "nameTopic", Field: "NameTopic", Type: "string"}, + {Code: "partitions", Field: "Partitions", Type: "int64"}, + {Code: "replicas", Field: "Replicas", Type: "int64"}, + }) + 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) + } + } + + 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 { + if plan.Partitions.IsNull() != state.Partitions.IsNull() || plan.Partitions.IsUnknown() != state.Partitions.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.Partitions.IsNull() && !plan.Partitions.IsUnknown() && !state.Partitions.IsNull() && !state.Partitions.IsUnknown() { + if plan.Partitions.ValueInt64() != state.Partitions.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.Replicas.IsNull() != state.Replicas.IsNull() || plan.Replicas.IsUnknown() != state.Replicas.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.Replicas.IsNull() && !plan.Replicas.IsUnknown() && !state.Replicas.IsNull() && !state.Replicas.IsUnknown() { + if plan.Replicas.ValueInt64() != state.Replicas.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 + } + + params := map[int]string{ + 477: resources_core.FormatInt64(plan.Partitions), + 478: resources_core.FormatInt64(plan.Replicas), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.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(), 117, 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: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "nameTopic", Field: "NameTopic", Type: "string"}, + {Code: "partitions", Field: "Partitions", Type: "int64"}, + {Code: "replicas", Field: "Replicas", Type: "int64"}, + }) + 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) + } + } + + 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 := "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 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/universal_rebuild/internal/resources_gen/119_akhq_resource.go b/universal_rebuild/internal/resources_gen/119_akhq_resource.go new file mode 100644 index 0000000..6942179 --- /dev/null +++ b/universal_rebuild/internal/resources_gen/119_akhq_resource.go @@ -0,0 +1,476 @@ +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/int64default" + "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"` + 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}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "kafka_uid": schema.StringAttribute{Required: 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 + } + planChanged := false + if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, plan.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != plan.KafkaUid.ValueString() { + plan.KafkaUid = types.StringValue(resolvedKafkaUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(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() { + if 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() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Domain.IsNull() || config.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if config.KafkaUid.IsNull() || config.KafkaUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + 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), + } + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParams(ctx, r.client, 119, config.ResourceName.ValueString(), adoptExistingOnCreate, params)...) +} + +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 + } + 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() + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnostics(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool())...) + + 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() + } + 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) + } + } + + 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) + } + } + + 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 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 + } + + params := map[int]string{ + 565: resources_core.FormatString(plan.KafkaUid), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.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(), 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) + } + } + + 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 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/universal_rebuild/internal/resources_gen/120_clickhouse_database_resource.go b/universal_rebuild/internal/resources_gen/120_clickhouse_database_resource.go new file mode 100644 index 0000000..a805419 --- /dev/null +++ b/universal_rebuild/internal/resources_gen/120_clickhouse_database_resource.go @@ -0,0 +1,322 @@ +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: 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"` + DatabaseName types.String `tfsdk:"database_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}, + "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(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "database_name": schema.StringAttribute{Required: true, 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 + } + if plan.DatabaseName.IsNull() || plan.DatabaseName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "database_name is required.") + 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{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + } + identityCodes := []string{ + "databaseName", + } + 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{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout); err != nil { + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(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 { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в 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("Нарушена консистентность", "Операция 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{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.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{ + "databaseName": resources_core.FormatString(state.DatabaseName), + } + identityCodes := []string{ + "databaseName", + } + 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 skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "databaseName": resources_core.FormatString(state.DatabaseName), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.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/universal_rebuild/internal/resources_gen/120_clickhouse_resource.go b/universal_rebuild/internal/resources_gen/120_clickhouse_resource.go new file mode 100644 index 0000000..7fe7e3d --- /dev/null +++ b/universal_rebuild/internal/resources_gen/120_clickhouse_resource.go @@ -0,0 +1,614 @@ +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/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "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 +} + +type ClickhouseModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AppVersion types.String `tfsdk:"app_version"` + ExtBACKUPNUMTORETAIN types.Int64 `tfsdk:"ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n"` + ExtBACKUPSCHEDULE types.String `tfsdk:"ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedCreateKeeper types.Bool `tfsdk:"need_create_keeper"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceCPUKeeper types.Int64 `tfsdk:"resource_c_p_u_keeper"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceInstancesKeeper types.Int64 `tfsdk:"resource_instances_keeper"` + ResourceInstancesShards types.Int64 `tfsdk:"resource_instances_shards"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceMemoryKeeper types.Int64 `tfsdk:"resource_memory_keeper"` + 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 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}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("25.10"), MarkdownDescription: "Версия `clickhouse-server`"}, + "ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7), MarkdownDescription: "Пример: `7`"}, + "ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *"), MarkdownDescription: "Принимает формат `CRON`"}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, + "need_create_keeper": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Необходим для кластеризации ClickHouse"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Позволяет подключиться извне к ClickHouse"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Пример: `1000`"}, + "resource_c_p_u_keeper": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50), MarkdownDescription: "Пример: `100`"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `100`"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `3`"}, + "resource_instances_keeper": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, + "resource_instances_shards": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1536), MarkdownDescription: "Пример: `2048`"}, + "resource_memory_keeper": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(152), MarkdownDescription: "Пример: `256`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, + "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно Не изменяется после создания."}, + "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 + } + planChanged := false + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != plan.S3Uid.ValueString() { + plan.S3Uid = types.StringValue(resolvedS3Uid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(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() { + if 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() { + if plan.S3Uid.ValueString() != state.S3Uid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.S3Uid.IsNull() || config.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + 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{ + 572: resources_core.FormatString(config.ResourceRealm), + 573: resources_core.FormatString(config.S3Uid), + 574: resources_core.FormatInt64(config.ResourceInstances), + 575: resources_core.FormatInt64(config.ResourceInstancesShards), + 576: resources_core.FormatInt64(config.ResourceCPU), + 577: resources_core.FormatInt64(config.ResourceMemory), + 578: resources_core.FormatString(config.AppVersion), + 579: resources_core.FormatInt64(config.ResourceDisk), + 580: resources_core.FormatBool(config.NeedExternalAddressMaster), + 581: resources_core.FormatString(config.IpSpaceNameMaster), + 582: resources_core.FormatBool(config.NeedCreateKeeper), + 583: resources_core.FormatInt64(config.ResourceCPUKeeper), + 584: resources_core.FormatInt64(config.ResourceMemoryKeeper), + 585: resources_core.FormatInt64(config.ResourceInstancesKeeper), + 586: resources_core.FormatString(config.ExtBACKUPSCHEDULE), + 587: resources_core.FormatInt64(config.ExtBACKUPNUMTORETAIN), + } + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParams(ctx, r.client, 120, config.ResourceName.ValueString(), adoptExistingOnCreate, params)...) +} + +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 + } + 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() + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnostics(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool())...) + + params := map[int]string{ + 572: resources_core.FormatString(data.ResourceRealm), + 573: resources_core.FormatString(data.S3Uid), + 574: resources_core.FormatInt64(data.ResourceInstances), + 575: resources_core.FormatInt64(data.ResourceInstancesShards), + 576: resources_core.FormatInt64(data.ResourceCPU), + 577: resources_core.FormatInt64(data.ResourceMemory), + 578: resources_core.FormatString(data.AppVersion), + 579: resources_core.FormatInt64(data.ResourceDisk), + 580: resources_core.FormatBool(data.NeedExternalAddressMaster), + 581: resources_core.FormatString(data.IpSpaceNameMaster), + 582: resources_core.FormatBool(data.NeedCreateKeeper), + 583: resources_core.FormatInt64(data.ResourceCPUKeeper), + 584: resources_core.FormatInt64(data.ResourceMemoryKeeper), + 585: resources_core.FormatInt64(data.ResourceInstancesKeeper), + 586: resources_core.FormatString(data.ExtBACKUPSCHEDULE), + 587: resources_core.FormatInt64(data.ExtBACKUPNUMTORETAIN), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.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: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needCreateKeeper", Field: "NeedCreateKeeper", Type: "bool"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceCPUKeeper", Field: "ResourceCPUKeeper", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceInstancesKeeper", Field: "ResourceInstancesKeeper", Type: "int64"}, + {Code: "resourceInstancesShards", Field: "ResourceInstancesShards", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceMemoryKeeper", Field: "ResourceMemoryKeeper", 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 *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: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needCreateKeeper", Field: "NeedCreateKeeper", Type: "bool"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceCPUKeeper", Field: "ResourceCPUKeeper", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceInstancesKeeper", Field: "ResourceInstancesKeeper", Type: "int64"}, + {Code: "resourceInstancesShards", Field: "ResourceInstancesShards", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceMemoryKeeper", Field: "ResourceMemoryKeeper", 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 *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.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.ResourceInstancesShards.IsNull() != state.ResourceInstancesShards.IsNull() || plan.ResourceInstancesShards.IsUnknown() != state.ResourceInstancesShards.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstancesShards.IsNull() && !plan.ResourceInstancesShards.IsUnknown() && !state.ResourceInstancesShards.IsNull() && !state.ResourceInstancesShards.IsUnknown() { + if plan.ResourceInstancesShards.ValueInt64() != state.ResourceInstancesShards.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.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 plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + 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.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + 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 plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedCreateKeeper.IsNull() != state.NeedCreateKeeper.IsNull() || plan.NeedCreateKeeper.IsUnknown() != state.NeedCreateKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedCreateKeeper.IsNull() && !plan.NeedCreateKeeper.IsUnknown() && !state.NeedCreateKeeper.IsNull() && !state.NeedCreateKeeper.IsUnknown() { + if plan.NeedCreateKeeper.ValueBool() != state.NeedCreateKeeper.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPUKeeper.IsNull() != state.ResourceCPUKeeper.IsNull() || plan.ResourceCPUKeeper.IsUnknown() != state.ResourceCPUKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPUKeeper.IsNull() && !plan.ResourceCPUKeeper.IsUnknown() && !state.ResourceCPUKeeper.IsNull() && !state.ResourceCPUKeeper.IsUnknown() { + if plan.ResourceCPUKeeper.ValueInt64() != state.ResourceCPUKeeper.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemoryKeeper.IsNull() != state.ResourceMemoryKeeper.IsNull() || plan.ResourceMemoryKeeper.IsUnknown() != state.ResourceMemoryKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemoryKeeper.IsNull() && !plan.ResourceMemoryKeeper.IsUnknown() && !state.ResourceMemoryKeeper.IsNull() && !state.ResourceMemoryKeeper.IsUnknown() { + if plan.ResourceMemoryKeeper.ValueInt64() != state.ResourceMemoryKeeper.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstancesKeeper.IsNull() != state.ResourceInstancesKeeper.IsNull() || plan.ResourceInstancesKeeper.IsUnknown() != state.ResourceInstancesKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstancesKeeper.IsNull() && !plan.ResourceInstancesKeeper.IsUnknown() && !state.ResourceInstancesKeeper.IsNull() && !state.ResourceInstancesKeeper.IsUnknown() { + if plan.ResourceInstancesKeeper.ValueInt64() != state.ResourceInstancesKeeper.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPSCHEDULE.IsNull() != state.ExtBACKUPSCHEDULE.IsNull() || plan.ExtBACKUPSCHEDULE.IsUnknown() != state.ExtBACKUPSCHEDULE.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPSCHEDULE.IsNull() && !plan.ExtBACKUPSCHEDULE.IsUnknown() && !state.ExtBACKUPSCHEDULE.IsNull() && !state.ExtBACKUPSCHEDULE.IsUnknown() { + if plan.ExtBACKUPSCHEDULE.ValueString() != state.ExtBACKUPSCHEDULE.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPNUMTORETAIN.IsNull() != state.ExtBACKUPNUMTORETAIN.IsNull() || plan.ExtBACKUPNUMTORETAIN.IsUnknown() != state.ExtBACKUPNUMTORETAIN.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPNUMTORETAIN.IsNull() && !plan.ExtBACKUPNUMTORETAIN.IsUnknown() && !state.ExtBACKUPNUMTORETAIN.IsNull() && !state.ExtBACKUPNUMTORETAIN.IsUnknown() { + if plan.ExtBACKUPNUMTORETAIN.ValueInt64() != state.ExtBACKUPNUMTORETAIN.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 + } + + params := map[int]string{ + 603: resources_core.FormatInt64(plan.ResourceInstances), + 604: resources_core.FormatInt64(plan.ResourceInstancesShards), + 605: resources_core.FormatInt64(plan.ResourceCPU), + 606: resources_core.FormatInt64(plan.ResourceMemory), + 607: resources_core.FormatString(plan.AppVersion), + 608: resources_core.FormatInt64(plan.ResourceDisk), + 609: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 610: resources_core.FormatString(plan.IpSpaceNameMaster), + 611: resources_core.FormatBool(plan.NeedCreateKeeper), + 612: resources_core.FormatInt64(plan.ResourceCPUKeeper), + 613: resources_core.FormatInt64(plan.ResourceMemoryKeeper), + 614: resources_core.FormatInt64(plan.ResourceInstancesKeeper), + 615: resources_core.FormatString(plan.ExtBACKUPSCHEDULE), + 616: resources_core.FormatInt64(plan.ExtBACKUPNUMTORETAIN), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.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(), 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: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needCreateKeeper", Field: "NeedCreateKeeper", Type: "bool"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceCPUKeeper", Field: "ResourceCPUKeeper", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceInstancesKeeper", Field: "ResourceInstancesKeeper", Type: "int64"}, + {Code: "resourceInstancesShards", Field: "ResourceInstancesShards", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceMemoryKeeper", Field: "ResourceMemoryKeeper", 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 *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 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/universal_rebuild/internal/resources_gen/120_clickhouse_user_resource.go b/universal_rebuild/internal/resources_gen/120_clickhouse_user_resource.go new file mode 100644 index 0000000..c542544 --- /dev/null +++ b/universal_rebuild/internal/resources_gen/120_clickhouse_user_resource.go @@ -0,0 +1,339 @@ +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"` + AccessHosts types.String `tfsdk:"access_hosts"` + DatabaseName types.String `tfsdk:"database_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}, + "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(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("::/0"), MarkdownDescription: "Пример: `::/0` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "database_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"database\"]"), MarkdownDescription: "Необходимо указать массив существующих баз Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "username": schema.StringAttribute{Required: true, 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 + } + if plan.Username.IsNull() || plan.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + 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), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "username": resources_core.FormatString(plan.Username), + } + identityCodes := []string{ + "accessHosts", + "databaseName", + "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), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "accessHosts": resources_core.FormatString(plan.AccessHosts), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout); err != nil { + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(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 { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в 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("Нарушена консистентность", "Операция 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), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "username": resources_core.FormatString(plan.Username), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.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), + "databaseName": resources_core.FormatString(state.DatabaseName), + "username": resources_core.FormatString(state.Username), + } + identityCodes := []string{ + "accessHosts", + "databaseName", + "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 skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+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 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/universal_rebuild/internal/resources_gen/12_s3_resource.go b/universal_rebuild/internal/resources_gen/12_s3_resource.go new file mode 100644 index 0000000..243d720 --- /dev/null +++ b/universal_rebuild/internal/resources_gen/12_s3_resource.go @@ -0,0 +1,398 @@ +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/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"` + 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}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "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{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 *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() { + if 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() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.DisplayName.IsNull() || config.DisplayName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + 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), + } + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParams(ctx, r.client, 12, config.ResourceName.ValueString(), adoptExistingOnCreate, params)...) +} + +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 + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnostics(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool())...) + + 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() + } + 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 + } + + params := map[int]string{ + 50: resources_core.FormatInt64(plan.MaxSizeGbPerUser), + 51: resources_core.FormatInt64(plan.MaxObjectsPerBucket), + 52: resources_core.FormatInt64(plan.MaxBucketsPerUser), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.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(), 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 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/universal_rebuild/internal/resources_gen/12_s3_sub_user_resource.go b/universal_rebuild/internal/resources_gen/12_s3_sub_user_resource.go new file mode 100644 index 0000000..c8ac15d --- /dev/null +++ b/universal_rebuild/internal/resources_gen/12_s3_sub_user_resource.go @@ -0,0 +1,334 @@ +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"` + 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}, + "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(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "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 err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout); err != nil { + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(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 { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в 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("Нарушена консистентность", "Операция 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 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 { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+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 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/universal_rebuild/internal/resources_gen/13_s3bucket_resource.go b/universal_rebuild/internal/resources_gen/13_s3bucket_resource.go new file mode 100644 index 0000000..bc48525 --- /dev/null +++ b/universal_rebuild/internal/resources_gen/13_s3bucket_resource.go @@ -0,0 +1,439 @@ +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/stringdefault" + "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"` + 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}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "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{Required: 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 + } + planChanged := false + if !plan.S3UserUid.IsNull() && !plan.S3UserUid.IsUnknown() { + resolvedS3UserUid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3UserUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_user_uid", err.Error()) + } else if resolvedS3UserUid != "" && resolvedS3UserUid != plan.S3UserUid.ValueString() { + plan.S3UserUid = types.StringValue(resolvedS3UserUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(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.BucketName.IsNull() && !plan.BucketName.IsUnknown() && !state.BucketName.IsNull() && !state.BucketName.IsUnknown() { + if 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() { + if 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() { + if 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() { + if plan.S3UserUid.ValueString() != state.S3UserUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_user_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.BucketName.IsNull() || config.BucketName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") + return + } + if config.S3UserUid.IsNull() || config.S3UserUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") + 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), + } + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParams(ctx, r.client, 13, config.ResourceName.ValueString(), adoptExistingOnCreate, params)...) +} + +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() + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnostics(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool())...) + + 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() + } + 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 + } + + 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 + } + + 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 + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.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(), 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 + } + + 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 := "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 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/universal_rebuild/internal/resources_gen/149_valo_tenant_resource.go b/universal_rebuild/internal/resources_gen/149_valo_tenant_resource.go new file mode 100644 index 0000000..c97e6f4 --- /dev/null +++ b/universal_rebuild/internal/resources_gen/149_valo_tenant_resource.go @@ -0,0 +1,389 @@ +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/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "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 +} + +type ValoTenantModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Storage types.Int64 `tfsdk:"s3_storage"` + ValoOptions types.String `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}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Будет добавлено в зону valo.ngcloud.ru Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "s3_storage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Параметр указывается в `Gb`"}, + "valo_options": 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 *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() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Domain.IsNull() || config.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + 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.FormatString(config.ValoOptions), + 658: resources_core.FormatInt64(config.S3Storage), + } + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParams(ctx, r.client, 149, config.ResourceName.ValueString(), adoptExistingOnCreate, params)...) +} + +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 + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnostics(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool())...) + + params := map[int]string{ + 655: resources_core.FormatString(data.ResourceRealm), + 656: resources_core.FormatString(data.Domain), + 657: resources_core.FormatString(data.ValoOptions), + 658: resources_core.FormatInt64(data.S3Storage), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.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: "string"}, + }) + 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: "string"}, + }) + 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.ValoOptions.IsNull() != state.ValoOptions.IsNull() || plan.ValoOptions.IsUnknown() != state.ValoOptions.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ValoOptions.IsNull() && !plan.ValoOptions.IsUnknown() && !state.ValoOptions.IsNull() && !state.ValoOptions.IsUnknown() { + if plan.ValoOptions.ValueString() != state.ValoOptions.ValueString() { + hasServiceParamChanges = true + } + } + } + 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 + } + + params := map[int]string{ + 659: resources_core.FormatString(plan.ValoOptions), + 660: resources_core.FormatInt64(plan.S3Storage), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.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(), 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: "string"}, + }) + 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 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 +}