From de850d70ceb3a833fae9b6934d89490a09a8ad83 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Tue, 30 Apr 2024 08:57:55 -0700 Subject: [PATCH] Add config plan check func for acceptance testing --- CHANGELOG.md | 6 ++++ go.mod | 1 + go.sum | 2 ++ testutil/test.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 92 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53b159c..b9df0ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.25.0 (Apr 30, 2024) + +IMPROVEMENTS: + +* Add plan checking func for acceptance test + ## 1.24.0 (Apr 12, 2024) IMPROVEMENTS: diff --git a/go.mod b/go.mod index 5e5d17d..ff17a07 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.0.0 // indirect + github.com/samber/lo v1.39.0 github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/zclconf/go-cty v1.14.0 // indirect golang.org/x/crypto v0.21.0 // indirect diff --git a/go.sum b/go.sum index e88a70f..a8bdead 100644 --- a/go.sum +++ b/go.sum @@ -130,6 +130,8 @@ github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= diff --git a/testutil/test.go b/testutil/test.go index 6c15c34..ce32cd4 100644 --- a/testutil/test.go +++ b/testutil/test.go @@ -2,21 +2,25 @@ package testutil import ( "bytes" + "context" + "encoding/json" "fmt" "math/rand" "reflect" "strings" "testing" "text/template" - "time" "github.com/go-resty/resty/v2" + tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/samber/lo" ) func RandomInt() int { - rand.Seed(time.Now().UnixNano()) return rand.Intn(10000000) } @@ -82,3 +86,80 @@ func MkNames(name, resource string) (int, string, string) { n := fmt.Sprintf("%s%d", name, id) return id, fmt.Sprintf("%s.%s", resource, n), n } + +var ConfigPlanChecks = resource.ConfigPlanChecks{ + PostApplyPreRefresh: []plancheck.PlanCheck{ + DebugPlan("PostApplyPreRefresh"), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + DebugPlan("PostApplyPostRefresh"), + }, +} + +var _ plancheck.PlanCheck = PlanCheck{} + +type PlanCheck struct { + Stage string +} + +func (p PlanCheck) CheckPlan(ctx context.Context, req plancheck.CheckPlanRequest, resp *plancheck.CheckPlanResponse) { + var err error + + rc, err := json.Marshal(req.Plan.ResourceChanges[0]) + if err != nil { + resp.Error = err + return + } + + pv, err := json.Marshal(req.Plan.PlannedValues) + if err != nil { + resp.Error = err + return + } + + ps, err := json.Marshal(req.Plan.PriorState) + if err != nil { + resp.Error = err + return + } + + rd, err := json.Marshal(req.Plan.ResourceDrift) + if err != nil { + resp.Error = err + return + } + + tflog.Debug(ctx, "CheckPlan", map[string]interface{}{ + "stage": p.Stage, + "req.Plan.ResourceChanges.ResourceDrift": string(rd), + "req.Plan.ResourceChanges": string(rc), + "req.Plan.PlannedValues": string(pv), + "req.Plan.PriorState": string(ps), + }) + + if len(req.Plan.ResourceDrift) > 0 { + drifts := lo.Map(req.Plan.ResourceDrift, func(c *tfjson.ResourceChange, index int) string { + return fmt.Sprintf("Name: %s, Before: %v, After: %v", c.Name, c.Change.Before, c.Change.After) + }) + resp.Error = fmt.Errorf("expected empty plan, but has resouce drifts(s): %v", strings.Join(drifts, ", ")) + return + } + + var errStrings []string + for _, rc := range req.Plan.ResourceChanges { + if !rc.Change.Actions.NoOp() { + errStrings = append(errStrings, fmt.Sprintf("expected empty plan, but %s has planned action(s): %v\n\nbefore: %v\n\nafter: %v\n\nunknown: %v", rc.Address, rc.Change.Actions, rc.Change.Before, rc.Change.After, rc.Change.AfterUnknown)) + } + } + + if len(errStrings) > 0 { + resp.Error = fmt.Errorf(strings.Join(errStrings, "\n")) + return + } +} + +func DebugPlan(stage string) plancheck.PlanCheck { + return PlanCheck{ + Stage: stage, + } +}