From 4b497e61db67c54186ebd62d78e336b8a0c13132 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Thu, 24 Sep 2026 10:52:36 +0300 Subject: [PATCH] =?UTF-8?q?fix(provider):=20nsxt=5Fsnat=20=E2=80=94=20?= =?UTF-8?q?=D0=BD=D0=B5=20=D0=BF=D0=B8=D1=81=D0=B0=D1=82=D1=8C=20null=20?= =?UTF-8?q?=D0=B2=20Required-=D0=B0=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82,?= =?UTF-8?q?=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20API=20=D0=B2=20Delete?= =?UTF-8?q?=20=E2=86=92=20error,=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=BF=D1=83=D1=81=D1=82=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20ip=5Fspace=5Fname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources_core/nsxt_snat_resource.go | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/provider/internal/resources_core/nsxt_snat_resource.go b/provider/internal/resources_core/nsxt_snat_resource.go index 1ffd52b..d545348 100644 --- a/provider/internal/resources_core/nsxt_snat_resource.go +++ b/provider/internal/resources_core/nsxt_snat_resource.go @@ -146,11 +146,10 @@ func (r *NsxtSnatResource) Read(ctx context.Context, req resource.ReadRequest, r return } - // Ключа ipSpaceName нет, пока SNAT ни разу не включали (HAR fresh-create), - // поэтому отсутствие ключа = null. Значение "no-needed" (SNAT выключен) — реальное. - if raw, ok := live["ipSpaceName"]; !ok || strings.TrimSpace(raw) == "" { - state.IpSpaceName = types.StringNull() - } else { + // ВАЖНО: в Required-атрибут нельзя писать null — после apply state обязан совпасть с планом, + // иначе Terraform вернёт "Provider produced inconsistent result after apply". Если ключа ещё нет + // (SNAT ни разу не включали, HAR fresh-create) — оставляем текущее значение state. + if raw, ok := live["ipSpaceName"]; ok && strings.TrimSpace(raw) != "" { state.IpSpaceName = types.StringValue(strings.TrimSpace(raw)) } @@ -180,10 +179,9 @@ func (r *NsxtSnatResource) Delete(ctx context.Context, req resource.DeleteReques remove, err := ShouldRemoveFromState(ctx, r.client, nsxtUID) if err != nil { - resp.Diagnostics.AddWarning( - "SNAT не выключался", - fmt.Sprintf("не удалось проверить существование шлюза %s: %s", nsxtUID, err), - ) + // Реальная ошибка API (не «шлюза нет») — нельзя молча терять SNAT: ресурс уйдёт из state, + // а SNAT останется включённым. + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } if remove { @@ -242,9 +240,11 @@ func (r *NsxtSnatResource) setSnat(ctx context.Context, nsxtUID types.String, ip return fmt.Errorf("клиент не инициализирован") } + // Пустую строку молча подменять нельзя (скрытое поведение + риск вечного diff). + // Выключение SNAT — явное каноническое значение "no-needed". value := strings.TrimSpace(ipSpaceName.ValueString()) if value == "" { - value = noNeededIpSpace + return fmt.Errorf("ip_space_name не может быть пустым: укажите имя ipSpace или %q для выключения SNAT", noNeededIpSpace) } unlock := r.client.LockInstance(uid)