add: generated resources batch 2/4

This commit is contained in:
“Naeel”
2026-06-30 15:43:35 +04:00
parent 897d910295
commit c01b4d0b2a
11 changed files with 4468 additions and 0 deletions
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}