diff --git a/tfbuilder/assertions.go b/tfbuilder/assertions.go index e9a88a2..fbd758f 100644 --- a/tfbuilder/assertions.go +++ b/tfbuilder/assertions.go @@ -5,7 +5,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/plancheck" ) -func CheckReapplyPlanEmpty(builder *Builder) resource.TestStep { +func CheckReapplyPlanEmpty(builder OnlyBuild) resource.TestStep { return resource.TestStep{ // Re-apply the same config and ensure no changes occur Config: builder.Build(), diff --git a/tfbuilder/builder.go b/tfbuilder/builder.go index 8165eee..ba5a5e2 100644 --- a/tfbuilder/builder.go +++ b/tfbuilder/builder.go @@ -26,6 +26,28 @@ type Builder struct { policies map[string]*PolicyBuilder } +type ModifyMeshBuilder interface { + OnlyBuild + AddressableBuilder + AddMesh(mesh *MeshBuilder) *Builder + RemoveMesh(name string) *Builder +} + +type ModifyPolicyBuilder interface { + OnlyBuild + AddressableBuilder + AddPolicy(builder *PolicyBuilder) *Builder + RemovePolicy(name string) *Builder +} + +type AddressableBuilder interface { + ResourceAddress(s string, resource string) string +} + +type OnlyBuild interface { + Build() string +} + func NewBuilder(provider ProviderType, scheme, host string, port int) *Builder { return &Builder{ provider: provider, diff --git a/tfbuilder/cases.go b/tfbuilder/cases.go new file mode 100644 index 0000000..343ee83 --- /dev/null +++ b/tfbuilder/cases.go @@ -0,0 +1,121 @@ +package tfbuilder + +import ( + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func CreateMeshAndModifyFieldsOnIt( + providerFactory map[string]func() (tfprotov6.ProviderServer, error), + builder ModifyMeshBuilder, + mesh *MeshBuilder, +) resource.TestCase { + return resource.TestCase{ + ProtoV6ProviderFactories: providerFactory, + Steps: []resource.TestStep{ + { + Config: builder.AddMesh(mesh).Build(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(builder.ResourceAddress("mesh", mesh.ResourceName), plancheck.ResourceActionCreate), + }, + }, + }, + CheckReapplyPlanEmpty(builder), + { + Config: builder.AddMesh(mesh.AddToSpec("at the end", ` + constraints = { + dataplane_proxy = { + requirements = [ { tags = { key = "a" } } ] + restrictions = [] + } + } + routing = { + default_forbid_mesh_external_service_access = true + } +`)).Build(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(builder.ResourceAddress("mesh", mesh.ResourceName), plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(builder.ResourceAddress("mesh", mesh.ResourceName), tfjsonpath.New("routing").AtMapKey("default_forbid_mesh_external_service_access"), knownvalue.Bool(true)), + }, + }, + }, + { + Config: builder.AddMesh(mesh.RemoveFromSpec(`default_forbid_mesh_external_service_access = true`)).Build(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(builder.ResourceAddress("mesh", mesh.ResourceName), plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(builder.ResourceAddress("mesh", mesh.ResourceName), tfjsonpath.New("routing").AtMapKey("default_forbid_mesh_external_service_access"), knownvalue.Null()), + }, + }, + }, + CheckReapplyPlanEmpty(builder), + { + Config: builder.AddMesh(mesh.UpdateSpec(`requirements = [ { tags = { key = "a" } } ]`, `requirements = []`)).Build(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(builder.ResourceAddress("mesh", mesh.ResourceName), plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(builder.ResourceAddress("mesh", mesh.ResourceName), tfjsonpath.New("constraints").AtMapKey("dataplane_proxy").AtMapKey("requirements"), knownvalue.ListExact([]knownvalue.Check{})), + }, + }, + }, + CheckReapplyPlanEmpty(builder), + { + Config: builder.RemoveMesh(mesh.MeshName).Build(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(builder.ResourceAddress("mesh", mesh.ResourceName), plancheck.ResourceActionDestroy), + }, + }, + }, + CheckReapplyPlanEmpty(builder), + }, + } +} + +func CreatePolicyAndModifyFieldsOnIt( + providerFactory map[string]func() (tfprotov6.ProviderServer, error), + builder ModifyPolicyBuilder, + mtp *PolicyBuilder, +) resource.TestCase { + mtp.WithSpec(AllowAllTrafficPermissionSpec) + + return resource.TestCase{ + ProtoV6ProviderFactories: providerFactory, + Steps: []resource.TestStep{ + { + Config: builder.AddPolicy(mtp).Build(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(builder.ResourceAddress(mtp.ResourceType, mtp.ResourceName), plancheck.ResourceActionCreate), + }, + }, + }, + CheckReapplyPlanEmpty(builder), + { + Config: builder.AddPolicy(mtp.AddToSpec(`kind = "Mesh"`, `proxy_types = ["Sidecar"]`)).Build(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(builder.ResourceAddress(mtp.ResourceType, mtp.ResourceName), plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(builder.ResourceAddress(mtp.ResourceType, mtp.ResourceName), tfjsonpath.New("spec").AtMapKey("from").AtSliceIndex(0).AtMapKey("target_ref").AtMapKey("proxy_types"), knownvalue.ListExact([]knownvalue.Check{knownvalue.StringExact("Sidecar")})), + }, + }, + }, + CheckReapplyPlanEmpty(builder), + { + Config: builder.AddPolicy(mtp.UpdateSpec(`proxy_types = ["Sidecar"]`, `proxy_types = []`)).Build(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(builder.ResourceAddress(mtp.ResourceType, mtp.ResourceName), plancheck.ResourceActionUpdate), + plancheck.ExpectKnownValue(builder.ResourceAddress(mtp.ResourceType, mtp.ResourceName), tfjsonpath.New("spec").AtMapKey("from").AtSliceIndex(0).AtMapKey("target_ref").AtMapKey("proxy_types"), knownvalue.ListExact([]knownvalue.Check{})), + }, + }, + }, + CheckReapplyPlanEmpty(builder), + }, + } +} diff --git a/tfbuilder/spec.go b/tfbuilder/spec.go index b6752cf..e8ed756 100644 --- a/tfbuilder/spec.go +++ b/tfbuilder/spec.go @@ -1,11 +1,10 @@ package tfbuilder import ( - "log" "strings" ) -func addToSpec(spec string, after, newLine string) string { +func addToSpec(spec, after, newLine string) string { lines := strings.Split(spec, "\n") var result []string inserted := false @@ -21,8 +20,9 @@ func addToSpec(spec string, after, newLine string) string { } } + // If "after" not found, just append at end if !inserted { - log.Fatalf("AddToSpec failed: could not find line containing: %q", after) + result = append(result, newLine) } return strings.Join(result, "\n")