feat(provider): ресурс аллокации принимает имя организации (резолв в UUID через ResolveRefSvcParamValue, как в nubes_vc_vdc); конфиги и доки без org_uid
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
|
||||
# 1. Внешние IP на организации (modify: vIPConfigure, массив перезаписывается целиком)
|
||||
resource "nubes_vc_org_ip_allocation" "org_ip" {
|
||||
org_uid = var.org_uid
|
||||
organization = var.organization
|
||||
|
||||
vip_configure = jsonencode([
|
||||
{
|
||||
@@ -45,7 +45,7 @@ resource "nubes_vc_nsxt_snat" "snat" {
|
||||
output "allocated_org_ip" {
|
||||
description = "Выделено внешних IP на организации"
|
||||
value = {
|
||||
org_uid = var.org_uid
|
||||
organization = var.organization
|
||||
ip_space_name = var.ip_space_name
|
||||
ip_count = var.ip_count
|
||||
}
|
||||
|
||||
@@ -16,12 +16,6 @@ variable "organization" {
|
||||
description = "Имя или UUID организации (vc_org)"
|
||||
}
|
||||
|
||||
# UUID той же организации — нужен ресурсам-модификаторам (они адресуются строго по uid)
|
||||
variable "org_uid" {
|
||||
type = string
|
||||
description = "UUID организации (vc_org) для nubes_vc_org_ip_allocation"
|
||||
}
|
||||
|
||||
# --- Модификаторы (IP на орге + SNAT на эдже) ---
|
||||
|
||||
variable "ip_space_name" {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
| Атрибут | Тип | Описание |
|
||||
|---|---|---|
|
||||
| `org_uid` | string, обязательный | UUID услуги «Организация в Cloud Director» |
|
||||
| `organization` | string, обязательный | Организация: имя из ЛК или её UUID |
|
||||
| `vip_configure` | string (JSON), обязательный | Массив аллокаций: `[{"name":"internet-ipv4-v1","count":"3"}]`. `count` — строка |
|
||||
| `keep_on_destroy` | bool, по умолчанию `false` | Не снимать квоту при `destroy` |
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
```hcl
|
||||
resource "nubes_vc_org_ip_allocation" "this" {
|
||||
org_uid = var.org_uid
|
||||
organization = var.organization
|
||||
|
||||
vip_configure = jsonencode([
|
||||
{ name = "internet-ipv4-v1", count = "3" }
|
||||
@@ -70,6 +70,6 @@ resource "nubes_vc_nsxt_snat" "this" {
|
||||
Оба ресурса импортируются по UUID родительской услуги:
|
||||
|
||||
```bash
|
||||
terraform import nubes_vc_org_ip_allocation.this <org_uid>
|
||||
terraform import nubes_vc_org_ip_allocation.this organ
|
||||
terraform import nubes_vc_nsxt_snat.this <nsxt_uid>
|
||||
```
|
||||
|
||||
@@ -17,8 +17,7 @@ cd tf_examples/fullpipe_chain
|
||||
| Что | Где взять | Пример |
|
||||
|---|---|---|
|
||||
| Токен API | ЛК → Профиль → Токены → «Технический» | `eyJhbGciOi...` |
|
||||
| UUID организации | ЛК → услуга «Организация в Cloud Director» → UUID в карточке услуги | `57eeacd1-dc7f-4a52-b903-7e5f7d3c1164` |
|
||||
| Имя организации | там же — название услуги | `organ` |
|
||||
| Имя организации | ЛК → услуга «Организация в Cloud Director» → название услуги | `organ` |
|
||||
| Сетевой провайдер | ЛК → vDC → «Сетевой провайдер» | `snb1` |
|
||||
| Provider VDC | ЛК → vDC → «Provider VDC» | `Intel Broadwell 2.4` |
|
||||
| Дисковая политика | ЛК → vDC → доступные дисковые политики | `SATA` |
|
||||
@@ -31,9 +30,8 @@ cp terraform.tfvars.example terraform.tfvars
|
||||
```
|
||||
|
||||
```hcl
|
||||
api_token = "eyJhbGciOi..." # токен из шага 2
|
||||
organization = "organ" # имя организации
|
||||
org_uid = "57eeacd1-dc7f-4a52-b903-7e5f7d3c1164" # UUID организации
|
||||
api_token = "eyJhbGciOi..." # токен из шага 2
|
||||
organization = "organ" # имя организации
|
||||
ip_space_name = "internet-ipv4-v1"
|
||||
ip_count = "3"
|
||||
```
|
||||
|
||||
@@ -36,7 +36,7 @@ type OrgIpAllocationResource struct {
|
||||
|
||||
type OrgIpAllocationModel struct {
|
||||
ID types.String `tfsdk:"id"`
|
||||
OrgUID types.String `tfsdk:"org_uid"`
|
||||
Organization types.String `tfsdk:"organization"`
|
||||
VIPConfigure types.String `tfsdk:"vip_configure"`
|
||||
KeepOnDestroy types.Bool `tfsdk:"keep_on_destroy"`
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func (r *OrgIpAllocationResource) Metadata(ctx context.Context, req resource.Met
|
||||
func (r *OrgIpAllocationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: "Аллокация внешних IP (vIPConfigure) на существующей организации Cloud Director. " +
|
||||
"Организация создаётся вручную в ЛК, ресурс адресует её по `org_uid`. " +
|
||||
"Организация создаётся вручную в ЛК, в конфиге указывается её имя или UUID. " +
|
||||
"Операция имеет replace-семантику: массив перезаписывается целиком.",
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
@@ -68,9 +68,10 @@ func (r *OrgIpAllocationResource) Schema(ctx context.Context, req resource.Schem
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"org_uid": schema.StringAttribute{
|
||||
Required: true,
|
||||
MarkdownDescription: "UUID существующей услуги «Организация в Cloud Director».",
|
||||
"organization": schema.StringAttribute{
|
||||
Required: true,
|
||||
MarkdownDescription: "Организация, на которой выделяются внешние IP: имя из ЛК (например `organ`) " +
|
||||
"или её UUID.",
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
@@ -100,12 +101,18 @@ func (r *OrgIpAllocationResource) Create(ctx context.Context, req resource.Creat
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.applyAllocation(ctx, plan.OrgUID, plan.VIPConfigure); err != nil {
|
||||
orgUID, err := r.resolveOrganizationUID(ctx, plan.Organization)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
plan.ID = types.StringValue(strings.TrimSpace(plan.OrgUID.ValueString()))
|
||||
if err := r.applyAllocation(ctx, orgUID, plan.VIPConfigure); err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
plan.ID = types.StringValue(orgUID)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
@@ -116,12 +123,18 @@ func (r *OrgIpAllocationResource) Update(ctx context.Context, req resource.Updat
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.applyAllocation(ctx, plan.OrgUID, plan.VIPConfigure); err != nil {
|
||||
orgUID, err := r.resolveOrganizationUID(ctx, plan.Organization)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
plan.ID = types.StringValue(strings.TrimSpace(plan.OrgUID.ValueString()))
|
||||
if err := r.applyAllocation(ctx, orgUID, plan.VIPConfigure); err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
plan.ID = types.StringValue(orgUID)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
@@ -132,8 +145,13 @@ func (r *OrgIpAllocationResource) Read(ctx context.Context, req resource.ReadReq
|
||||
return
|
||||
}
|
||||
|
||||
orgUID := strings.TrimSpace(state.OrgUID.ValueString())
|
||||
if orgUID == "" || r.client == nil {
|
||||
if strings.TrimSpace(state.Organization.ValueString()) == "" || r.client == nil {
|
||||
return
|
||||
}
|
||||
|
||||
orgUID, err := r.resolveOrganizationUID(ctx, state.Organization)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -181,8 +199,13 @@ func (r *OrgIpAllocationResource) Delete(ctx context.Context, req resource.Delet
|
||||
return
|
||||
}
|
||||
|
||||
orgUID := strings.TrimSpace(state.OrgUID.ValueString())
|
||||
if orgUID == "" || r.client == nil {
|
||||
if strings.TrimSpace(state.Organization.ValueString()) == "" || r.client == nil {
|
||||
return
|
||||
}
|
||||
|
||||
orgUID, err := r.resolveOrganizationUID(ctx, state.Organization)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -267,14 +290,37 @@ func (r *OrgIpAllocationResource) Configure(_ context.Context, req resource.Conf
|
||||
func (r *OrgIpAllocationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
uid := strings.TrimSpace(req.ID)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), uid)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("org_uid"), uid)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("organization"), uid)...)
|
||||
}
|
||||
|
||||
// resolveOrganizationUID принимает имя организации из ЛК или её UUID и возвращает UUID.
|
||||
// Резолв делает клиент — тем же путём, что сгенерированный nubes_vc_vdc
|
||||
// (core.ResolveRefSvcParamValue, сравн. 21_vc_vdc_resource.go).
|
||||
func (r *OrgIpAllocationResource) resolveOrganizationUID(ctx context.Context, organization types.String) (string, error) {
|
||||
if r.client == nil {
|
||||
return "", fmt.Errorf("клиент не инициализирован")
|
||||
}
|
||||
raw := strings.TrimSpace(organization.ValueString())
|
||||
if raw == "" {
|
||||
return "", fmt.Errorf("organization обязателен")
|
||||
}
|
||||
|
||||
resolved, err := r.client.ResolveRefSvcParamValue(ctx, 19, raw)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("не удалось определить организацию %q: %w", raw, err)
|
||||
}
|
||||
resolved = strings.TrimSpace(resolved)
|
||||
if resolved == "" {
|
||||
return "", fmt.Errorf("организация %q не найдена", raw)
|
||||
}
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
// applyAllocation отправляет modify с массивом vIPConfigure целиком.
|
||||
func (r *OrgIpAllocationResource) applyAllocation(ctx context.Context, orgUID types.String, vipConfigure types.String) error {
|
||||
uid := strings.TrimSpace(orgUID.ValueString())
|
||||
func (r *OrgIpAllocationResource) applyAllocation(ctx context.Context, orgUID string, vipConfigure types.String) error {
|
||||
uid := strings.TrimSpace(orgUID)
|
||||
if uid == "" {
|
||||
return fmt.Errorf("org_uid обязателен")
|
||||
return fmt.Errorf("organization обязателен")
|
||||
}
|
||||
if r.client == nil {
|
||||
return fmt.Errorf("клиент не инициализирован")
|
||||
|
||||
Reference in New Issue
Block a user