Skip to content

✨ VNF-3903 Add rollback and change plan support to load balancer actions - #142

Open
KobayashiNozomi wants to merge 5 commits into
nttcom:masterfrom
KobayashiNozomi:feature/VNF-3903_mlb_day5
Open

✨ VNF-3903 Add rollback and change plan support to load balancer actions#142
KobayashiNozomi wants to merge 5 commits into
nttcom:masterfrom
KobayashiNozomi:feature/VNF-3903_mlb_day5

Conversation

@KobayashiNozomi

@KobayashiNozomi KobayashiNozomi commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

This pull request supports Managed Load Balancer Day 5.

Reference

nttcom/eclcloud#56

Related Pull Requests

Test Results

TestMockedAccMLBV1
$ OS_AUTH_TOKEN=1 make testacc-args TEST=./ecl TESTARGS='-run ^TestMockedAccMLBV1'
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./ecl -v -run ^TestMockedAccMLBV1 -timeout 120m
=== RUN   TestMockedAccMLBV1CertificateDataSource
--- PASS: TestMockedAccMLBV1CertificateDataSource (0.22s)
=== RUN   TestMockedAccMLBV1HealthMonitorDataSource
--- PASS: TestMockedAccMLBV1HealthMonitorDataSource (0.84s)
=== RUN   TestMockedAccMLBV1ListenerDataSource
--- PASS: TestMockedAccMLBV1ListenerDataSource (0.57s)
=== RUN   TestMockedAccMLBV1LoadBalancerDataSource
--- PASS: TestMockedAccMLBV1LoadBalancerDataSource (0.68s)
=== RUN   TestMockedAccMLBV1OperationDataSource
--- PASS: TestMockedAccMLBV1OperationDataSource (0.26s)
=== RUN   TestMockedAccMLBV1PlanDataSource
--- PASS: TestMockedAccMLBV1PlanDataSource (0.89s)
=== RUN   TestMockedAccMLBV1PolicyDataSource
--- PASS: TestMockedAccMLBV1PolicyDataSource (1.09s)
=== RUN   TestMockedAccMLBV1RouteDataSource
--- PASS: TestMockedAccMLBV1RouteDataSource (0.49s)
=== RUN   TestMockedAccMLBV1RuleDataSource
--- PASS: TestMockedAccMLBV1RuleDataSource (0.61s)
=== RUN   TestMockedAccMLBV1SystemUpdateDataSource
--- PASS: TestMockedAccMLBV1SystemUpdateDataSource (0.43s)
=== RUN   TestMockedAccMLBV1TargetGroupDataSource
--- PASS: TestMockedAccMLBV1TargetGroupDataSource (0.39s)
=== RUN   TestMockedAccMLBV1TLSPolicyDataSource
--- PASS: TestMockedAccMLBV1TLSPolicyDataSource (0.21s)
=== RUN   TestMockedAccMLBV1CertificateImport
--- PASS: TestMockedAccMLBV1CertificateImport (0.12s)
=== RUN   TestMockedAccMLBV1HealthMonitorImport
--- PASS: TestMockedAccMLBV1HealthMonitorImport (0.12s)
=== RUN   TestMockedAccMLBV1ListenerImport
--- PASS: TestMockedAccMLBV1ListenerImport (0.12s)
=== RUN   TestMockedAccMLBV1LoadBalancerImport
--- PASS: TestMockedAccMLBV1LoadBalancerImport (5.13s)
=== RUN   TestMockedAccMLBV1PolicyImport
--- PASS: TestMockedAccMLBV1PolicyImport (0.15s)
=== RUN   TestMockedAccMLBV1RouteImport
--- PASS: TestMockedAccMLBV1RouteImport (0.13s)
=== RUN   TestMockedAccMLBV1RuleImport
--- PASS: TestMockedAccMLBV1RuleImport (0.13s)
=== RUN   TestMockedAccMLBV1TargetGroupImport
--- PASS: TestMockedAccMLBV1TargetGroupImport (0.12s)
=== RUN   TestMockedAccMLBV1CertificateResource
--- PASS: TestMockedAccMLBV1CertificateResource (0.19s)
=== RUN   TestMockedAccMLBV1HealthMonitorResource
--- PASS: TestMockedAccMLBV1HealthMonitorResource (0.22s)
=== RUN   TestMockedAccMLBV1ListenerResource
--- PASS: TestMockedAccMLBV1ListenerResource (0.23s)
=== RUN   TestMockedAccMLBV1LoadBalancerActionResource_ApplyConfigurations
--- PASS: TestMockedAccMLBV1LoadBalancerActionResource_ApplyConfigurations (125.15s)
=== RUN   TestMockedAccMLBV1LoadBalancerActionResource_SystemUpdate
--- PASS: TestMockedAccMLBV1LoadBalancerActionResource_SystemUpdate (125.17s)
=== RUN   TestMockedAccMLBV1LoadBalancerActionResource_ApplyConfigurationsAndSystemUpdate
--- PASS: TestMockedAccMLBV1LoadBalancerActionResource_ApplyConfigurationsAndSystemUpdate (125.16s)
=== RUN   TestMockedAccMLBV1LoadBalancerActionResource_NoApplyConfigurations
--- PASS: TestMockedAccMLBV1LoadBalancerActionResource_NoApplyConfigurations (0.10s)
=== RUN   TestMockedAccMLBV1LoadBalancerActionResource_NoSystemUpdate
--- PASS: TestMockedAccMLBV1LoadBalancerActionResource_NoSystemUpdate (0.10s)
=== RUN   TestMockedAccMLBV1LoadBalancerActionResource_SystemUpdateRollback
--- PASS: TestMockedAccMLBV1LoadBalancerActionResource_SystemUpdateRollback (125.14s)
=== RUN   TestMockedAccMLBV1LoadBalancerActionResource_ChangePlan
--- PASS: TestMockedAccMLBV1LoadBalancerActionResource_ChangePlan (125.14s)
=== RUN   TestMockedAccMLBV1LoadBalancerResource
--- PASS: TestMockedAccMLBV1LoadBalancerResource (125.27s)
=== RUN   TestMockedAccMLBV1PolicyResource
--- PASS: TestMockedAccMLBV1PolicyResource (0.27s)
=== RUN   TestMockedAccMLBV1RouteResource
--- PASS: TestMockedAccMLBV1RouteResource (0.23s)
=== RUN   TestMockedAccMLBV1RuleResource
--- PASS: TestMockedAccMLBV1RuleResource (0.22s)
=== RUN   TestMockedAccMLBV1TargetGroupResource
--- PASS: TestMockedAccMLBV1TargetGroupResource (0.22s)
PASS
ok  	github.com/nttcom/terraform-provider-ecl/ecl	765.892s

@yamashita-hiroto yamashita-hiroto left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

コミット毎に分けて下記対応を実施

@hico-horiuchi hico-horiuchi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VNF-3903 のレビューです。

Day 5 の API 差分は mVNA API リファレンスの x-release: 5 と突き合わせ、対応漏れが無いことを確認しています。

[MUST] change-plan の後に Load Balancer が作り替えられる

ecl/resource_ecl_mlb_load_balancer_v1.goplan_idRequiredForceNew の組み合わせです。
resourceMLBLoadBalancerV1Readd.Set("plan_id", loadBalancer.PlanID) で実機の値を state に書き戻します。
この 2 つにより、change-plan アクションで実機の plan_id が変わると、次の refresh がそれを ForceNew の差分として検出し、Load Balancer が destroy と create をたどります。

VNF-3903 の Lab 検証ログにこの挙動が残っています。

~ plan_id = "b24df6f8-..." -> "19cbbf78-..." # forces replacement
...
ecl_mlb_load_balancer_v1.test: Destruction complete after 2m15s

作り替えは 1 回では収まりません。
config の plan_id が A、change_plan が B のとき、apply ごとに次の循環が起きます。

apply 1: LB を A で作成 → change-plan で実機が B に (state は A)
apply 2: refresh で実機 B を検出 → ForceNew で LB を destroy + create (A に戻る)
         → load_balancer_id が変わるので action も再作成 → change-plan で再び B に
apply 3: 以降 apply 2 と同じ

この循環を避けられる config は、plan_idchange_plan に同じ値を書く場合だけです。
ただしその場合は既存 Load Balancer のプラン変更が起きないため、設定を維持したままプランを変更するという Day 5 の目的を満たせません。

修正方法

plan_id から ForceNew を外すと収束します。

 			"plan_id": &schema.Schema{
 				Type:     schema.TypeString,
 				Required: true,
-				ForceNew: true,
 			},

resourceMLBLoadBalancerV1UpdateAttributesHasChange を見るのは namedescriptiontags だけで、resourceMLBLoadBalancerV1UpdateConfigurations が見るのは syslog_serversinterfaces だけです。
そのため plan_id のみが変わった Update は API を呼ばない no-op になります。
プラン変更は ecl_mlb_load_balancer_action_v1change_plan が担う形になり、各リソースで staged 設定を作ってアクションリソースで apply_configurations を実行する既存の設計とも揃います。
他のコード変更は不要です。

修正後、plan_idchange_plan の両方に B を指定すると次のように収束します。

apply 1: LB は update in-place (no-op) → Read で state は実機値 A のまま
         → action が change-plan を実行 → 実機が B に
plan 2:  refresh で実機 B を読んで state が B に → config B と一致で差分なし
         action も CheckChangePlanRequired で実機 B == B のためスキップ

apply 1 の終了時点では state の A と plan した B がずれるため、Provider produced inconsistent result after apply がログに出ます。
helper/schemalegacy_type_system を立てるので、これはエラーではなく警告に留まります。

動作確認

configuration_statusCREATE_STAGED のままだと validation で弾かれるため、先に apply_configurations を完了させてください。

  1. plan_id = Aapply_configurations = true で apply し、configuration_statusACTIVE になるまで待つ
  2. plan_id を B に変更し、あわせて change_plan = B を指定して terraform plan を実行する
  3. apply して change-plan が動き、destroy のログが出ないことを確認する
  4. 再度 terraform plan を実行し、plan_id の差分が出ないことを確認する
  5. rollback は system_update を適用してから rollback = "true" を指定して apply し、revisioncurrent_revision に戻ることを確認する

2 で -/+ destroy and then create replacement ではなく ~ update in-place になること が今回の修正の証跡です。

プラン変更でインターフェース数が変わると interfacesMinItemsMaxItems に引っかかります。
Lab 検証と同じく、インターフェース数が等しいプラン同士 (50M_HA_4IF から 200M_HA_4IF) を選んでください。

ドキュメントへの追記

website/docs/r/mlb_load_balancer_action_v1.html.markdownchange_plan の説明に、次の 2 点を注意書きとして追記してください。

  • change_plan でプランを変更したときは ecl_mlb_load_balancer_v1 側の plan_id も同じ値に更新する
  • 更新しない場合、次回の terraform planplan_id の差分として検出される

[IMO] system_update の rollback の typo を検出する

system_updateschema.TypeMapElem: &schema.Resource{...} を与えた形です。
legacy SDK は TypeMap の Elem に渡した *schema.Resource を無視するため、任意のキーと文字列値が通ります。
その結果、typo したキーによって失敗の仕方が分かれます。

  • system_update_idsystemUpdateMap["system_update_id"].(string) が nil への型アサーションで panic するため気付ける
  • rollbackif v, ok := systemUpdateMap["rollback"]; ok && v.(string) != "" が偽になって rollback = false となり、ロールバックのつもりでシステムアップデートが走る

本筋は TypeListMaxItems: 1 への変更です。
ただし HCL の書き方が system_update = { ... } から system_update { ... } に変わり、state migration も必要になります。
既存ユーザーの config が壊れるため、この変更は選びにくいと考えます。

schema を変えずに済む緩和策として、ValidateFunc で未知のキーを弾く方法があります。
vendor 済みの helper/schemavalidateMap はマップ全体を ValidateFunc に渡すので、TypeMap でも機能します。

"system_update": &schema.Schema{
	Type:     schema.TypeMap,
	Optional: true,
	Elem:     &schema.Resource{ /* 既存のまま */ },
	ValidateFunc: func(v interface{}, k string) ([]string, []error) {
		var errs []error
		for key := range v.(map[string]interface{}) {
			if key != "system_update_id" && key != "rollback" {
				errs = append(errs, fmt.Errorf("%s: unsupported key %q", k, key))
			}
		}
		return nil, errs
	},
},
  • メリット:破壊的変更なしに typo を plan 時点で弾ける。検査するのはキー名だけなので、値が unknown でも動く
  • デメリット:キーを追加するときに ValidateFunc の更新も必要になる

[NR] certificate リソースにも同じ形が残っている

certificateCertFileSchemaForResourcecertificateKeyFileSchemaForResourceTypeMapElem: &schema.Resource{...} を与えた形です。
ssl_keypassphraseif passphrase, ok := file["passphrase"].(string); ok で受けており、typo すると rollback と同じく静かに無視されます。

この PR のスコープ外なので、別 Issue を切って対応するのが良いと考えます。

resource_ecl_mlb_rule_v1.goconditionsTypeList なので、SDK 側でキー名が検証されます。
tags は任意のキーを受けるのが意図された設計です。
どちらも対象外です。

[MUST] change-plan のテストを追加する

  • _NoChangePlan:Load Balancer の plan_idchange_plan と一致し、アクションを投げないケース
  • _ApplyConfigurationsAndChangePlan:既存の _ApplyConfigurationsAndSystemUpdate に相当するケース

Read が no-op であるため、change-plan の冪等性は CheckChangePlanRequired だけが担保しています。
_NoApplyConfigurations_NoSystemUpdate と同じ粒度で押さえてください。

[IMO] ドキュメントに change_plan の Example Usage を追加する

website/docs/r/mlb_load_balancer_action_v1.html.markdown の変更は Argument Reference への追記だけです。
change_plan は新しいトップレベルの引数で、上記の注意書きのとおり ecl_mlb_load_balancer_v1plan_id と同じ値を書く必要があります。
両方を並べた Example Usage があると、この対応関係が伝わります。

rollback は既に例のある system_update ブロックにキーが 1 つ増えるだけなので、追記は不要と考えます。

[NITS] mock test の HCL の桁を揃える

testAccMLBV1LoadBalancerActionSystemUpdateRollback= の位置がずれています。
Go の文字列リテラル内なので make fmtcheck では検出されません。

   system_update = {
     system_update_id = "31746df7-92f9-4b5e-ad05-59f6684a54eb"
-    rollback          = true
+    rollback         = true
   }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants