From 0e6e6b5a8b146bf4c93502115cff68cf12359f58 Mon Sep 17 00:00:00 2001 From: akinross Date: Wed, 3 Jul 2024 19:01:32 +0200 Subject: [PATCH] [minor_change] Introduction of a provider level flag to prevent creation of objects that are already existing in APIC configuration --- aci/provider.go | 5 + docs/index.md | 10 +- gen/templates/index.md.tmpl | 10 +- gen/templates/provider.go.tmpl | 28 +++ gen/templates/resource.go.tmpl | 41 +++- gen/templates/resource_test.go.tmpl | 204 +++++++++++++++++- internal/provider/provider.go | 36 +++- internal/provider/provider_test.go | 4 +- internal/provider/resource_aci_annotation.go | 33 ++- .../provider/resource_aci_annotation_test.go | 129 +++++++++++ .../resource_aci_endpoint_security_group.go | 28 ++- ...source_aci_endpoint_security_group_test.go | 91 ++++++++ .../provider/resource_aci_endpoint_tag_ip.go | 40 +++- .../resource_aci_endpoint_tag_ip_test.go | 81 +++++++ .../provider/resource_aci_endpoint_tag_mac.go | 40 +++- .../resource_aci_endpoint_tag_mac_test.go | 81 +++++++ ...nal_management_network_instance_profile.go | 40 +++- ...anagement_network_instance_profile_test.go | 71 ++++++ ..._aci_external_management_network_subnet.go | 40 +++- ...external_management_network_subnet_test.go | 75 +++++++ .../resource_aci_l3out_consumer_label.go | 40 +++- .../resource_aci_l3out_consumer_label_test.go | 87 ++++++++ .../resource_aci_l3out_node_sid_profile.go | 40 +++- ...esource_aci_l3out_node_sid_profile_test.go | 79 +++++++ .../resource_aci_l3out_provider_label.go | 40 +++- .../resource_aci_l3out_provider_label_test.go | 83 +++++++ .../resource_aci_l3out_redistribute_policy.go | 40 +++- ...urce_aci_l3out_redistribute_policy_test.go | 69 ++++++ .../resource_aci_netflow_monitor_policy.go | 40 +++- ...esource_aci_netflow_monitor_policy_test.go | 79 +++++++ .../resource_aci_netflow_record_policy.go | 40 +++- ...resource_aci_netflow_record_policy_test.go | 91 ++++++++ .../resource_aci_out_of_band_contract.go | 40 +++- .../resource_aci_out_of_band_contract_test.go | 91 ++++++++ .../resource_aci_pim_route_map_entry.go | 40 +++- .../resource_aci_pim_route_map_entry_test.go | 91 ++++++++ .../resource_aci_pim_route_map_policy.go | 40 +++- .../resource_aci_pim_route_map_policy_test.go | 79 +++++++ ...ource_aci_relation_to_consumed_contract.go | 40 +++- ..._aci_relation_to_consumed_contract_test.go | 133 ++++++++++++ ...lation_to_consumed_out_of_band_contract.go | 40 +++- ...n_to_consumed_out_of_band_contract_test.go | 67 ++++++ ...esource_aci_relation_to_contract_master.go | 40 +++- ...ce_aci_relation_to_contract_master_test.go | 125 +++++++++++ ...ource_aci_relation_to_imported_contract.go | 40 +++- ..._aci_relation_to_imported_contract_test.go | 133 ++++++++++++ ...urce_aci_relation_to_intra_epg_contract.go | 40 +++- ...aci_relation_to_intra_epg_contract_test.go | 125 +++++++++++ ...source_aci_relation_to_netflow_exporter.go | 40 +++- ...e_aci_relation_to_netflow_exporter_test.go | 63 ++++++ ...ource_aci_relation_to_provided_contract.go | 40 +++- ..._aci_relation_to_provided_contract_test.go | 141 ++++++++++++ ...resource_aci_relation_to_taboo_contract.go | 40 +++- ...rce_aci_relation_to_taboo_contract_test.go | 63 ++++++ ...ci_relation_to_vrf_fallback_route_group.go | 40 +++- ...lation_to_vrf_fallback_route_group_test.go | 63 ++++++ .../provider/resource_aci_rest_managed.go | 23 +- .../resource_aci_rest_managed_test.go | 71 +++++- internal/provider/resource_aci_tag.go | 33 ++- internal/provider/resource_aci_tag_test.go | 129 +++++++++++ .../resource_aci_vrf_fallback_route.go | 40 +++- .../resource_aci_vrf_fallback_route_group.go | 40 +++- ...rce_aci_vrf_fallback_route_group_member.go | 40 +++- ...ci_vrf_fallback_route_group_member_test.go | 75 +++++++ ...ource_aci_vrf_fallback_route_group_test.go | 71 ++++++ .../resource_aci_vrf_fallback_route_test.go | 75 +++++++ internal/provider/utils.go | 14 +- 67 files changed, 3962 insertions(+), 118 deletions(-) diff --git a/aci/provider.go b/aci/provider.go index f10b26380..cb28b97ce 100644 --- a/aci/provider.go +++ b/aci/provider.go @@ -69,6 +69,11 @@ func Provider() *schema.Provider { Optional: true, Description: "Global annotation for the provider. This can also be set as the ACI_ANNOTATION environment variable.", }, + "allow_existing_on_create": &schema.Schema{ + Type: schema.TypeBool, + Description: "Allow existing objects to be managed. This can also be set as the ACI_ALLOW_EXISTING_ON_CREATE environment variable.", + Optional: true, + }, }, ResourcesMap: map[string]*schema.Resource{ diff --git a/docs/index.md b/docs/index.md index e1b12155b..6c2efa956 100644 --- a/docs/index.md +++ b/docs/index.md @@ -136,5 +136,13 @@ NOTE: either 'password' OR 'private_key' and 'cert_name' must be provided for th - `validate_relation_dn` (Boolean) Flag to validate if a object with entered relation Dn exists in the APIC. - Default: `true` - Environment variable: `ACI_VAL_REL_DN` -- `annotation` (String) Global annotation for the provider. +- `annotation` (String) Global annotation for the provider. + - Default: `orchestrator:terraform` - Environment variable: `ACI_ANNOTATION` +- `allow_existing_on_create` (Boolean) Global flag to validate existing objects in APIC are allowed to be managed. + - Default: `true` + - Environment variable: `ACI_ALLOW_EXISTING_ON_CREATE` + + ~> The existence of an object in APIC can only be verified when the distinguished name (DN) can be constructed during plan. The verification cannot take place when a `parent_dn` attribute or any of the `naming` attributes ( attributes that are required to construct the DN ) of a resource are unknown ( known after apply ) during plan. An example of a unknown attribute input would be to use a DN reference ( `aci_tenant.example.id` ) to a resource that is being configured in the same plan. The DN of the object cannot be determined prior to apply and thus the existence of the object in APIC cannot be verified during plan. In these cases the verification will be performed during the apply operation. + + -> Disabling this flag will suppress an additional API call during the plan phase for newly defined resources in the configuration. \ No newline at end of file diff --git a/gen/templates/index.md.tmpl b/gen/templates/index.md.tmpl index 1178509da..c18c63a3d 100644 --- a/gen/templates/index.md.tmpl +++ b/gen/templates/index.md.tmpl @@ -119,5 +119,13 @@ NOTE: either 'password' OR 'private_key' and 'cert_name' must be provided for th - `validate_relation_dn` (Boolean) Flag to validate if a object with entered relation Dn exists in the APIC. - Default: `true` - Environment variable: `ACI_VAL_REL_DN` -- `annotation` (String) Global annotation for the provider. +- `annotation` (String) Global annotation for the provider. + - Default: `orchestrator:terraform` - Environment variable: `ACI_ANNOTATION` +- `allow_existing_on_create` (Boolean) Global flag to validate existing objects in APIC are allowed to be managed. + - Default: `true` + - Environment variable: `ACI_ALLOW_EXISTING_ON_CREATE` + + ~> The existence of an object in APIC can only be verified when the distinguished name (DN) can be constructed during plan. The verification cannot take place when a `parent_dn` attribute or any of the `naming` attributes ( attributes that are required to construct the DN ) of a resource are unknown ( known after apply ) during plan. An example of a unknown attribute input would be to use a DN reference ( `aci_tenant.example.id` ) to a resource that is being configured in the same plan. The DN of the object cannot be determined prior to apply and thus the existence of the object in APIC cannot be verified during plan. In these cases the verification will be performed during the apply operation. + + -> Disabling this flag will suppress an additional API call during the plan phase for newly defined resources in the configuration. \ No newline at end of file diff --git a/gen/templates/provider.go.tmpl b/gen/templates/provider.go.tmpl index 1d32ee8f1..8ba238cbb 100644 --- a/gen/templates/provider.go.tmpl +++ b/gen/templates/provider.go.tmpl @@ -27,6 +27,7 @@ import ( ) var globalAnnotation string +var globalAllowExistingOnCreate bool // Ensure AciProvider satisfies various provider interfaces. var _ provider.Provider = &AciProvider{} @@ -52,6 +53,7 @@ type AciProviderModel struct { ValidateRelationDn types.String `tfsdk:"validate_relation_dn"` MaxRetries types.String `tfsdk:"retries"` Annotation types.String `tfsdk:"annotation"` + AllowExistingOnCreate types.Bool `tfsdk:"allow_existing_on_create"` } func (p *AciProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { @@ -123,6 +125,10 @@ func (p *AciProvider) Schema(ctx context.Context, req provider.SchemaRequest, re Description: "Global annotation for the provider. This can also be set as the ACI_ANNOTATION environment variable.", Optional: true, }, + "allow_existing_on_create": schema.BoolAttribute{ + Description: "Allow existing objects to be managed. This can also be set as the ACI_ALLOW_EXISTING_ON_CREATE environment variable.", + Optional: true, + }, }, } } @@ -148,6 +154,7 @@ func (p *AciProvider) Configure(ctx context.Context, req provider.ConfigureReque validateRelationDn := stringToBool(resp, "insecure", getStringAttribute(data.ValidateRelationDn, "ACI_VAL_REL_DN"), true) maxRetries := stringToInt(resp, "retries", getStringAttribute(data.MaxRetries, "ACI_RETRIES"), 2) setGlobalAnnotation(data.Annotation, "ACI_ANNOTATION") + setGlobalAllowExistingOnCreate(resp, data.AllowExistingOnCreate, "ACI_ALLOW_EXISTING_ON_CREATE") if username == "" { resp.Diagnostics.AddError( @@ -224,6 +231,27 @@ func New(version string) func() provider.Provider { } } +func setGlobalAllowExistingOnCreate(resp *provider.ConfigureResponse, attribute basetypes.BoolValue, envKey string) { + + if attribute.IsNull() { + envValue, found := os.LookupEnv(envKey) + if found { + boolValue, err := strconv.ParseBool(envValue) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Invalid input '%s'", envValue), + fmt.Sprintf("A boolean value must be provided for %s", envKey), + ) + } + globalAllowExistingOnCreate = boolValue + } else { + globalAllowExistingOnCreate = true + } + } else { + globalAllowExistingOnCreate = attribute.ValueBool() + } +} + func setGlobalAnnotation(attribute basetypes.StringValue, envKey string) { if attribute.IsNull() { diff --git a/gen/templates/resource.go.tmpl b/gen/templates/resource.go.tmpl index 305ebd4d8..e9d647c2e 100644 --- a/gen/templates/resource.go.tmpl +++ b/gen/templates/resource.go.tmpl @@ -448,18 +448,30 @@ func set{{ .ResourceClassName }}LegacyAttributes(ctx context.Context, diags *dia } {{- end }} } +{{- end }} func (r *{{.ResourceClassName}}Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { if !req.Plan.Raw.IsNull() { - var planData, stateData, configData *{{.ResourceClassName}}ResourceModel + var planData, stateData{{ if .LegacyAttributes}}, configData{{end}} *{{.ResourceClassName}}ResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) - resp.Diagnostics.Append(req.Config.Get(ctx, &configData)...) + {{ if .LegacyAttributes}}resp.Diagnostics.Append(req.Config.Get(ctx, &configData)...){{end}} if resp.Diagnostics.HasError() { return } + if stateData == nil && !globalAllowExistingOnCreate {{if .HasParent }}&& !planData.ParentDn.IsUnknown() {{end}}{{range .Properties}}{{if .IsNaming}}&& !planData.{{ .Name }}.IsUnknown() {{end}}{{end}}{ + var createCheckData *{{.ResourceClassName}}ResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + set{{.ResourceClassName}}Id(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "{{.PkgName}}", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + {{ if .LegacyAttributes}} {{ range .LegacyAttributes}}{{$SetName := .Name}} {{- if and (ne .ReplacedBy.AttributeName "") (eq (getMigrationType .ValueType) "String") (isNewAttributeStringType .ReplacedBy.AttributeName) }} if !configData.{{ .Name }}.IsNull() { @@ -706,11 +718,10 @@ func (r *{{.ResourceClassName}}Resource) ModifyPlan(ctx context.Context, req res {{- end }} } {{ end }} - resp.Diagnostics.Append(resp.Plan.Set(ctx, &planData)...) + {{ end }} } } -{{- end}} func (r *{{.ResourceClassName}}Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_{{.ResourceName}}") @@ -1011,6 +1022,13 @@ func (r *{{.ResourceClassName}}Resource) Create(ctx context.Context, req resourc resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) set{{.ResourceClassName}}Id(ctx, stateData) getAndSet{{.ResourceClassName}}Attributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The {{.PkgName}} object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } {{- end}} var data *{{.ResourceClassName}}ResourceModel @@ -1032,9 +1050,9 @@ func (r *{{.ResourceClassName}}Resource) Create(ctx context.Context, req resourc data.{{ .ResourceClassName }}.ElementsAs(ctx, &{{.PkgName}}Plan, false) stateData.{{ .ResourceClassName }}.ElementsAs(ctx, &{{.PkgName}}State, false) {{- end}} - jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, data{{- range .Children}}, {{.PkgName}}Plan, {{.PkgName}}State{{- end}}) + jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, true, data{{- range .Children}}, {{.PkgName}}Plan, {{.PkgName}}State{{- end}}) {{- else}} - jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, data) + jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, true, data) {{- end}} if resp.Diagnostics.HasError() { @@ -1104,9 +1122,9 @@ func (r *{{.ResourceClassName}}Resource) Update(ctx context.Context, req resourc data.{{ .ResourceClassName }}.ElementsAs(ctx, &{{.PkgName}}Plan, false) stateData.{{ .ResourceClassName }}.ElementsAs(ctx, &{{.PkgName}}State, false) {{- end}} - jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, data{{- range .Children}}, {{.PkgName}}Plan, {{.PkgName}}State{{- end}}) + jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, false, data{{- range .Children}}, {{.PkgName}}Plan, {{.PkgName}}State{{- end}}) {{- else}} - jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, data) + jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, false, data) {{- end}} if resp.Diagnostics.HasError() { @@ -1432,9 +1450,14 @@ func get{{$.ResourceClassName}}{{ .ResourceClassName }}ChildPayloads(ctx context } {{- end}} -func get{{.ResourceClassName}}CreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *{{.ResourceClassName}}ResourceModel{{- range .Children}}, {{.PkgName}}Plan, {{.PkgName}}State []{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel{{- end}}) *container.Container { +func get{{.ResourceClassName}}CreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *{{.ResourceClassName}}ResourceModel{{- range .Children}}, {{.PkgName}}Plan, {{.PkgName}}State []{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel{{- end}}) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } + {{- if .HasChild}} childPayloads := []map[string]interface{}{} {{ range .Children}} diff --git a/gen/templates/resource_test.go.tmpl b/gen/templates/resource_test.go.tmpl index a723ebd54..cb0d893f8 100644 --- a/gen/templates/resource_test.go.tmpl +++ b/gen/templates/resource_test.go.tmpl @@ -6,6 +6,7 @@ package provider import ( "testing" + "regexp" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) @@ -14,6 +15,96 @@ import ( {{- range .parents}}{{$target_classes := .target_classes}} func TestAccResource{{$.resourceClassName}}With{{capitalize .class_name}}(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfig{{$.resourceClassName}}MinDependencyWith{{capitalize .class_name}}AllowExisting, + ExpectNonEmptyPlan: {{.class_in_parent}}, + Check: resource.ComposeAggregateTestCheckFunc( + {{- range $key, $value := $.resource_required}}{{$contains := definedInMap $key $.default}} + {{- if not $contains}} + {{- if eq $key "target_dn" }} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{getTestTargetDn $.targets $.resourceName $value false $target_classes 0}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{getTestTargetDn $.targets $.resourceName $value false $target_classes 0}}"), + {{- else }} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{$value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{$value}}"), + {{- end }} + {{- end}} + {{- end}} + {{- range $key, $value := $.default}} + {{- if (isInterfaceSlice $value)}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "{{len $value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}.#", "{{len $value}}"), + {{- range $index, $subvalue := $value}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$index}}", "{{$subvalue}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}.{{$index}}", "{{$subvalue}}"), + {{- end}} + {{- else}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{$value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{$value}}"), + {{- end}} + {{- end}} + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfig{{$.resourceClassName}}MinDependencyWith{{capitalize .class_name}}AllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfig{{$.resourceClassName}}MinDependencyWith{{capitalize .class_name}}AllowExisting, + ExpectNonEmptyPlan: {{.class_in_parent}}, + Check: resource.ComposeAggregateTestCheckFunc( + {{- range $key, $value := $.resource_required}}{{$contains := definedInMap $key $.default}} + {{- if not $contains}} + {{- if eq $key "target_dn" }} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{getTestTargetDn $.targets $.resourceName $value false $target_classes 0}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{getTestTargetDn $.targets $.resourceName $value false $target_classes 0}}"), + {{- else }} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{$value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{$value}}"), + {{- end }} + {{- end}} + {{- end}} + {{- range $key, $value := $.default}} + {{- if (isInterfaceSlice $value)}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "{{len $value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}.#", "{{len $value}}"), + {{- range $index, $subvalue := $value}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$index}}", "{{$subvalue}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}.{{$index}}", "{{$subvalue}}"), + {{- end}} + {{- else}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{$value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{$value}}"), + {{- end}} + {{- end}} + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -344,6 +435,76 @@ func TestAccResource{{$.resourceClassName}}With{{capitalize .class_name}}(t *tes {{- else}} func TestAccResource{{.resourceClassName}}(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfig{{.resourceClassName}}MinAllowExisting, + Check: resource.ComposeAggregateTestCheckFunc( + {{- range $key, $value := $.resource_required}}{{$contains := definedInMap $key $.default}} + {{- if not $contains}} + {{- if eq $key "target_dn" }} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{getTestTargetDn $.targets $.resourceName $value false nil 0}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{getTestTargetDn $.targets $.resourceName $value false nil 0}}"), + {{- else }} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{$value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{$value}}"), + {{- end }} + {{- end}} + {{- end}} + {{- range $key, $value := $.default}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{$value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{$value}}"), + {{- end}} + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfig{{.resourceClassName}}MinAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfig{{.resourceClassName}}MinAllowExisting, + Check: resource.ComposeAggregateTestCheckFunc( + {{- range $key, $value := $.resource_required}}{{$contains := definedInMap $key $.default}} + {{- if not $contains}} + {{- if eq $key "target_dn" }} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{getTestTargetDn $.targets $.resourceName $value false nil 0}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{getTestTargetDn $.targets $.resourceName $value false nil 0}}"), + {{- else }} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{$value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{$value}}"), + {{- end }} + {{- end}} + {{- end}} + {{- range $key, $value := $.default}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}", "{{$value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test_2", "{{$key}}", "{{$value}}"), + {{- end}} + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -689,6 +850,30 @@ resource "aci_{{$target.target_resource_name}}" "test_{{$index}}" { {{- if .parents}} {{- range .parents}}{{$target_classes := .target_classes}} +const testConfig{{$.resourceClassName}}MinDependencyWith{{capitalize .class_name}}AllowExisting = {{- if $dependencies }} testDependencyConfig{{$.resourceClassName}} + {{- end }} testConfig{{capitalize .class_name}}Min{{- if ne .parent_dependency ""}}DependencyWith{{capitalize .parent_dependency}}{{if and (ne .parent_dependency_name nil) (ne .parent_dependency_name "")}}{{capitalize .parent_dependency_name }}{{- end }}{{- end}} + ` +resource "aci_{{$.resourceName}}" "test" { + parent_dn = {{.parent_dn}} + {{- range $key, $value := $.resource_required}} + {{- if eq $key "target_dn" }} + {{$key}} = {{getTestTargetDn $.targets $.resourceName $value true $target_classes 0}} + {{- else }} + {{$key}} = "{{$value}}" + {{- end }} + {{- end}} +} +resource "aci_{{$.resourceName}}" "test_2" { + parent_dn = {{.parent_dn}} + {{- range $key, $value := $.resource_required}} + {{- if eq $key "target_dn" }} + {{$key}} = {{getTestTargetDn $.targets $.resourceName $value true $target_classes 0}} + {{- else }} + {{$key}} = "{{$value}}" + {{- end }} + {{- end}} + depends_on = [aci_{{$.resourceName}}.test] +} +` + const testConfig{{$.resourceClassName}}MinDependencyWith{{capitalize .class_name}} = {{- if $dependencies }} testDependencyConfig{{$.resourceClassName}} + {{- end }} testConfig{{capitalize .class_name}}Min{{- if ne .parent_dependency ""}}DependencyWith{{capitalize .parent_dependency}}{{if and (ne .parent_dependency_name nil) (ne .parent_dependency_name "")}}{{capitalize .parent_dependency_name }}{{- end }}{{- end}} + ` resource "aci_{{$.resourceName}}" "test" { parent_dn = {{.parent_dn}} @@ -854,11 +1039,26 @@ resource "aci_{{$.resourceName}}" "test" { {{- end}} {{- end}} {{else}} + +const testConfig{{.resourceClassName}}MinAllowExisting = ` +resource "aci_{{$.resourceName}}" "test" { + {{- range $key, $value := $.resource_required}} + {{$key}} = "{{$value}}" + {{- end}} +} +resource "aci_{{$.resourceName}}" "test_2" { + {{- range $key, $value := $.resource_required}} + {{$key}} = "{{$value}}" + {{- end}} + depends_on = [aci_{{$.resourceName}}.test] +} +` + const testConfig{{.resourceClassName}}Min = ` resource "aci_{{$.resourceName}}" "test" { - {{- range $key, $value := $.resource_required}} + {{- range $key, $value := $.resource_required}} {{$key}} = "{{$value}}" - {{- end}} + {{- end}} } ` diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 5b53aedb9..d2244f8da 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -28,6 +28,7 @@ import ( ) var globalAnnotation string +var globalAllowExistingOnCreate bool // Ensure AciProvider satisfies various provider interfaces. var _ provider.Provider = &AciProvider{} @@ -49,10 +50,11 @@ type AciProviderModel struct { // IsInsecure types.Bool `tfsdk:"insecure"` // ValidateRelationDn types.Bool `tfsdk:"validate_relation_dn"` // MaxRetries types.Int64 `tfsdk:"retries"` - IsInsecure types.String `tfsdk:"insecure"` - ValidateRelationDn types.String `tfsdk:"validate_relation_dn"` - MaxRetries types.String `tfsdk:"retries"` - Annotation types.String `tfsdk:"annotation"` + IsInsecure types.String `tfsdk:"insecure"` + ValidateRelationDn types.String `tfsdk:"validate_relation_dn"` + MaxRetries types.String `tfsdk:"retries"` + Annotation types.String `tfsdk:"annotation"` + AllowExistingOnCreate types.Bool `tfsdk:"allow_existing_on_create"` } func (p *AciProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { @@ -124,6 +126,10 @@ func (p *AciProvider) Schema(ctx context.Context, req provider.SchemaRequest, re Description: "Global annotation for the provider. This can also be set as the ACI_ANNOTATION environment variable.", Optional: true, }, + "allow_existing_on_create": schema.BoolAttribute{ + Description: "Allow existing objects to be managed. This can also be set as the ACI_ALLOW_EXISTING_ON_CREATE environment variable.", + Optional: true, + }, }, } } @@ -149,6 +155,7 @@ func (p *AciProvider) Configure(ctx context.Context, req provider.ConfigureReque validateRelationDn := stringToBool(resp, "insecure", getStringAttribute(data.ValidateRelationDn, "ACI_VAL_REL_DN"), true) maxRetries := stringToInt(resp, "retries", getStringAttribute(data.MaxRetries, "ACI_RETRIES"), 2) setGlobalAnnotation(data.Annotation, "ACI_ANNOTATION") + setGlobalAllowExistingOnCreate(resp, data.AllowExistingOnCreate, "ACI_ALLOW_EXISTING_ON_CREATE") if username == "" { resp.Diagnostics.AddError( @@ -271,6 +278,27 @@ func New(version string) func() provider.Provider { } } +func setGlobalAllowExistingOnCreate(resp *provider.ConfigureResponse, attribute basetypes.BoolValue, envKey string) { + + if attribute.IsNull() { + envValue, found := os.LookupEnv(envKey) + if found { + boolValue, err := strconv.ParseBool(envValue) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Invalid input '%s'", envValue), + fmt.Sprintf("A boolean value must be provided for %s", envKey), + ) + } + globalAllowExistingOnCreate = boolValue + } else { + globalAllowExistingOnCreate = true + } + } else { + globalAllowExistingOnCreate = attribute.ValueBool() + } +} + func setGlobalAnnotation(attribute basetypes.StringValue, envKey string) { if attribute.IsNull() { diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 670d93987..b5d691e42 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -62,6 +62,6 @@ func testAccPreCheck(t *testing.T) { } } -func setGlobalAnnotationEnvVariable(t *testing.T, annotation string) { - t.Setenv("ACI_ANNOTATION", annotation) +func setEnvVariable(t *testing.T, key, value string) { + t.Setenv(key, value) } diff --git a/internal/provider/resource_aci_annotation.go b/internal/provider/resource_aci_annotation.go index 72b1f1605..ae3d6217a 100644 --- a/internal/provider/resource_aci_annotation.go +++ b/internal/provider/resource_aci_annotation.go @@ -49,6 +49,29 @@ type TagAnnotationIdentifier struct { Key types.String } +func (r *TagAnnotationResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *TagAnnotationResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Key.IsUnknown() { + var createCheckData *TagAnnotationResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setTagAnnotationId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "tagAnnotation", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *TagAnnotationResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_annotation") resp.TypeName = req.ProviderTypeName + "_annotation" @@ -135,7 +158,7 @@ func (r *TagAnnotationResource) Create(ctx context.Context, req resource.CreateR tflog.Debug(ctx, fmt.Sprintf("Create of resource aci_annotation with id '%s'", data.Id.ValueString())) - jsonPayload := getTagAnnotationCreateJsonPayload(ctx, &resp.Diagnostics, data) + jsonPayload := getTagAnnotationCreateJsonPayload(ctx, &resp.Diagnostics, true, data) if resp.Diagnostics.HasError() { return @@ -192,7 +215,7 @@ func (r *TagAnnotationResource) Update(ctx context.Context, req resource.UpdateR tflog.Debug(ctx, fmt.Sprintf("Update of resource aci_annotation with id '%s'", data.Id.ValueString())) - jsonPayload := getTagAnnotationCreateJsonPayload(ctx, &resp.Diagnostics, data) + jsonPayload := getTagAnnotationCreateJsonPayload(ctx, &resp.Diagnostics, false, data) if resp.Diagnostics.HasError() { return @@ -309,9 +332,13 @@ func setTagAnnotationId(ctx context.Context, data *TagAnnotationResourceModel) { data.Id = types.StringValue(fmt.Sprintf("%s/%s", data.ParentDn.ValueString(), rn)) } -func getTagAnnotationCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *TagAnnotationResourceModel) *container.Container { +func getTagAnnotationCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *TagAnnotationResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } if !data.Key.IsNull() && !data.Key.IsUnknown() { payloadMap["attributes"].(map[string]string)["key"] = data.Key.ValueString() } diff --git a/internal/provider/resource_aci_annotation_test.go b/internal/provider/resource_aci_annotation_test.go index 64e61a2c4..850baef2d 100644 --- a/internal/provider/resource_aci_annotation_test.go +++ b/internal/provider/resource_aci_annotation_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,56 @@ import ( func TestAccResourceTagAnnotationWithFvTenant(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagAnnotationMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_annotation.test", "key", "test_key"), + resource.TestCheckResourceAttr("aci_annotation.test_2", "key", "test_key"), + resource.TestCheckResourceAttr("aci_annotation.test", "value", "test_value"), + resource.TestCheckResourceAttr("aci_annotation.test_2", "value", "test_value"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagAnnotationMinDependencyWithFvTenantAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagAnnotationMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_annotation.test", "key", "test_key"), + resource.TestCheckResourceAttr("aci_annotation.test_2", "key", "test_key"), + resource.TestCheckResourceAttr("aci_annotation.test", "value", "test_value"), + resource.TestCheckResourceAttr("aci_annotation.test_2", "value", "test_value"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -67,6 +118,56 @@ func TestAccResourceTagAnnotationWithFvTenant(t *testing.T) { } func TestAccResourceTagAnnotationWithFvAEPg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagAnnotationMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_annotation.test", "key", "test_key"), + resource.TestCheckResourceAttr("aci_annotation.test_2", "key", "test_key"), + resource.TestCheckResourceAttr("aci_annotation.test", "value", "test_value"), + resource.TestCheckResourceAttr("aci_annotation.test_2", "value", "test_value"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagAnnotationMinDependencyWithFvAEPgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagAnnotationMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_annotation.test", "key", "test_key"), + resource.TestCheckResourceAttr("aci_annotation.test_2", "key", "test_key"), + resource.TestCheckResourceAttr("aci_annotation.test", "value", "test_value"), + resource.TestCheckResourceAttr("aci_annotation.test_2", "value", "test_value"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -121,6 +222,20 @@ func TestAccResourceTagAnnotationWithFvAEPg(t *testing.T) { }) } +const testConfigTagAnnotationMinDependencyWithFvTenantAllowExisting = testConfigFvTenantMin + ` +resource "aci_annotation" "test" { + parent_dn = aci_tenant.test.id + key = "test_key" + value = "test_value" +} +resource "aci_annotation" "test_2" { + parent_dn = aci_tenant.test.id + key = "test_key" + value = "test_value" + depends_on = [aci_annotation.test] +} +` + const testConfigTagAnnotationMinDependencyWithFvTenant = testConfigFvTenantMin + ` resource "aci_annotation" "test" { parent_dn = aci_tenant.test.id @@ -145,6 +260,20 @@ resource "aci_annotation" "test" { } ` +const testConfigTagAnnotationMinDependencyWithFvAEPgAllowExisting = testConfigFvAEPgMin + ` +resource "aci_annotation" "test" { + parent_dn = aci_application_epg.test.id + key = "test_key" + value = "test_value" +} +resource "aci_annotation" "test_2" { + parent_dn = aci_application_epg.test.id + key = "test_key" + value = "test_value" + depends_on = [aci_annotation.test] +} +` + const testConfigTagAnnotationMinDependencyWithFvAEPg = testConfigFvAEPgMin + ` resource "aci_annotation" "test" { parent_dn = aci_application_epg.test.id diff --git a/internal/provider/resource_aci_endpoint_security_group.go b/internal/provider/resource_aci_endpoint_security_group.go index 7fb3baf0c..4a63d4fda 100644 --- a/internal/provider/resource_aci_endpoint_security_group.go +++ b/internal/provider/resource_aci_endpoint_security_group.go @@ -587,6 +587,16 @@ func (r *FvESgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanR return } + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Name.IsUnknown() { + var createCheckData *FvESgResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvESgId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvESg", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + if !configData.MatchT.IsNull() { planData.DeprecatedMatchT = configData.MatchT } else if !configData.DeprecatedMatchT.IsNull() { @@ -906,6 +916,7 @@ func (r *FvESgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanR } resp.Diagnostics.Append(resp.Plan.Set(ctx, &planData)...) + } } @@ -1493,6 +1504,13 @@ func (r *FvESgResource) Create(ctx context.Context, req resource.CreateRequest, resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvESgId(ctx, stateData) getAndSetFvESgAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvESg object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvESgResourceModel @@ -1531,7 +1549,7 @@ func (r *FvESgResource) Create(ctx context.Context, req resource.CreateRequest, var tagTagPlan, tagTagState []TagTagFvESgResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvESgCreateJsonPayload(ctx, &resp.Diagnostics, data, fvRsConsPlan, fvRsConsState, fvRsConsIfPlan, fvRsConsIfState, fvRsIntraEpgPlan, fvRsIntraEpgState, fvRsProvPlan, fvRsProvState, fvRsScopePlan, fvRsScopeState, fvRsSecInheritedPlan, fvRsSecInheritedState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvESgCreateJsonPayload(ctx, &resp.Diagnostics, true, data, fvRsConsPlan, fvRsConsState, fvRsConsIfPlan, fvRsConsIfState, fvRsIntraEpgPlan, fvRsIntraEpgState, fvRsProvPlan, fvRsProvState, fvRsScopePlan, fvRsScopeState, fvRsSecInheritedPlan, fvRsSecInheritedState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -1614,7 +1632,7 @@ func (r *FvESgResource) Update(ctx context.Context, req resource.UpdateRequest, var tagTagPlan, tagTagState []TagTagFvESgResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvESgCreateJsonPayload(ctx, &resp.Diagnostics, data, fvRsConsPlan, fvRsConsState, fvRsConsIfPlan, fvRsConsIfState, fvRsIntraEpgPlan, fvRsIntraEpgState, fvRsProvPlan, fvRsProvState, fvRsScopePlan, fvRsScopeState, fvRsSecInheritedPlan, fvRsSecInheritedState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvESgCreateJsonPayload(ctx, &resp.Diagnostics, false, data, fvRsConsPlan, fvRsConsState, fvRsConsIfPlan, fvRsConsIfState, fvRsIntraEpgPlan, fvRsIntraEpgState, fvRsProvPlan, fvRsProvState, fvRsScopePlan, fvRsScopeState, fvRsSecInheritedPlan, fvRsSecInheritedState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -2219,9 +2237,13 @@ func getFvESgTagTagChildPayloads(ctx context.Context, diags *diag.Diagnostics, d return childPayloads } -func getFvESgCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvESgResourceModel, fvRsConsPlan, fvRsConsState []FvRsConsFvESgResourceModel, fvRsConsIfPlan, fvRsConsIfState []FvRsConsIfFvESgResourceModel, fvRsIntraEpgPlan, fvRsIntraEpgState []FvRsIntraEpgFvESgResourceModel, fvRsProvPlan, fvRsProvState []FvRsProvFvESgResourceModel, fvRsScopePlan, fvRsScopeState []FvRsScopeFvESgResourceModel, fvRsSecInheritedPlan, fvRsSecInheritedState []FvRsSecInheritedFvESgResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvESgResourceModel, tagTagPlan, tagTagState []TagTagFvESgResourceModel) *container.Container { +func getFvESgCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvESgResourceModel, fvRsConsPlan, fvRsConsState []FvRsConsFvESgResourceModel, fvRsConsIfPlan, fvRsConsIfState []FvRsConsIfFvESgResourceModel, fvRsIntraEpgPlan, fvRsIntraEpgState []FvRsIntraEpgFvESgResourceModel, fvRsProvPlan, fvRsProvState []FvRsProvFvESgResourceModel, fvRsScopePlan, fvRsScopeState []FvRsScopeFvESgResourceModel, fvRsSecInheritedPlan, fvRsSecInheritedState []FvRsSecInheritedFvESgResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvESgResourceModel, tagTagPlan, tagTagState []TagTagFvESgResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} FvRsConschildPayloads := getFvESgFvRsConsChildPayloads(ctx, diags, data, fvRsConsPlan, fvRsConsState) diff --git a/internal/provider/resource_aci_endpoint_security_group_test.go b/internal/provider/resource_aci_endpoint_security_group_test.go index 15bcfe030..23472529f 100644 --- a/internal/provider/resource_aci_endpoint_security_group_test.go +++ b/internal/provider/resource_aci_endpoint_security_group_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,84 @@ import ( func TestAccResourceFvESgWithFvAp(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvESgMinDependencyWithFvApAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "admin_state", "no"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "admin_state", "no"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "description", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "exception_tag", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "exception_tag", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "intra_esg_isolation", "unenforced"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "intra_esg_isolation", "unenforced"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "preferred_group_member", "exclude"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "preferred_group_member", "exclude"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvESgMinDependencyWithFvApAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvESgMinDependencyWithFvApAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "admin_state", "no"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "admin_state", "no"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "description", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "exception_tag", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "exception_tag", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "intra_esg_isolation", "unenforced"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "intra_esg_isolation", "unenforced"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "preferred_group_member", "exclude"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test_2", "preferred_group_member", "exclude"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -318,6 +397,18 @@ resource "aci_endpoint_security_group" "test_1" { } ` +const testConfigFvESgMinDependencyWithFvApAllowExisting = testConfigFvApMinDependencyWithFvTenant + ` +resource "aci_endpoint_security_group" "test" { + parent_dn = aci_application_profile.test.id + name = "test_name" +} +resource "aci_endpoint_security_group" "test_2" { + parent_dn = aci_application_profile.test.id + name = "test_name" + depends_on = [aci_endpoint_security_group.test] +} +` + const testConfigFvESgMinDependencyWithFvAp = testConfigFvApMinDependencyWithFvTenant + ` resource "aci_endpoint_security_group" "test" { parent_dn = aci_application_profile.test.id diff --git a/internal/provider/resource_aci_endpoint_tag_ip.go b/internal/provider/resource_aci_endpoint_tag_ip.go index 2e711138e..a2bd5458f 100644 --- a/internal/provider/resource_aci_endpoint_tag_ip.go +++ b/internal/provider/resource_aci_endpoint_tag_ip.go @@ -70,6 +70,29 @@ type FvEpIpTagIdentifier struct { Ip types.String } +func (r *FvEpIpTagResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvEpIpTagResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.CtxName.IsUnknown() && !planData.Ip.IsUnknown() { + var createCheckData *FvEpIpTagResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvEpIpTagId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvEpIpTag", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvEpIpTagResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_endpoint_tag_ip") resp.TypeName = req.ProviderTypeName + "_endpoint_tag_ip" @@ -233,6 +256,13 @@ func (r *FvEpIpTagResource) Create(ctx context.Context, req resource.CreateReque resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvEpIpTagId(ctx, stateData) getAndSetFvEpIpTagAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvEpIpTag object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvEpIpTagResourceModel @@ -253,7 +283,7 @@ func (r *FvEpIpTagResource) Create(ctx context.Context, req resource.CreateReque var tagTagPlan, tagTagState []TagTagFvEpIpTagResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvEpIpTagCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvEpIpTagCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -318,7 +348,7 @@ func (r *FvEpIpTagResource) Update(ctx context.Context, req resource.UpdateReque var tagTagPlan, tagTagState []TagTagFvEpIpTagResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvEpIpTagCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvEpIpTagCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -567,9 +597,13 @@ func getFvEpIpTagTagTagChildPayloads(ctx context.Context, diags *diag.Diagnostic return childPayloads } -func getFvEpIpTagCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvEpIpTagResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvEpIpTagResourceModel, tagTagPlan, tagTagState []TagTagFvEpIpTagResourceModel) *container.Container { +func getFvEpIpTagCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvEpIpTagResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvEpIpTagResourceModel, tagTagPlan, tagTagState []TagTagFvEpIpTagResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvEpIpTagTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_endpoint_tag_ip_test.go b/internal/provider/resource_aci_endpoint_tag_ip_test.go index ca8f2a19f..8ce689db1 100644 --- a/internal/provider/resource_aci_endpoint_tag_ip_test.go +++ b/internal/provider/resource_aci_endpoint_tag_ip_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,72 @@ import ( func TestAccResourceFvEpIpTagWithFvTenant(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvEpIpTagMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "ip", "10.0.0.2"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "ip", "10.0.0.2"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "vrf_name", "test_ctx_name"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "vrf_name", "test_ctx_name"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "id_attribute", "0"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "id_attribute", "0"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "name", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "name_alias", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvEpIpTagMinDependencyWithFvTenantAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvEpIpTagMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "ip", "10.0.0.2"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "ip", "10.0.0.2"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "vrf_name", "test_ctx_name"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "vrf_name", "test_ctx_name"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "id_attribute", "0"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "id_attribute", "0"), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "name", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_ip.test_2", "name_alias", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -164,6 +231,20 @@ func TestAccResourceFvEpIpTagWithFvTenant(t *testing.T) { }) } +const testConfigFvEpIpTagMinDependencyWithFvTenantAllowExisting = testConfigFvTenantMin + ` +resource "aci_endpoint_tag_ip" "test" { + parent_dn = aci_tenant.test.id + ip = "10.0.0.2" + vrf_name = "test_ctx_name" +} +resource "aci_endpoint_tag_ip" "test_2" { + parent_dn = aci_tenant.test.id + ip = "10.0.0.2" + vrf_name = "test_ctx_name" + depends_on = [aci_endpoint_tag_ip.test] +} +` + const testConfigFvEpIpTagMinDependencyWithFvTenant = testConfigFvTenantMin + ` resource "aci_endpoint_tag_ip" "test" { parent_dn = aci_tenant.test.id diff --git a/internal/provider/resource_aci_endpoint_tag_mac.go b/internal/provider/resource_aci_endpoint_tag_mac.go index a405e067d..7b8ec22ee 100644 --- a/internal/provider/resource_aci_endpoint_tag_mac.go +++ b/internal/provider/resource_aci_endpoint_tag_mac.go @@ -70,6 +70,29 @@ type FvEpMacTagIdentifier struct { Mac types.String } +func (r *FvEpMacTagResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvEpMacTagResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.BdName.IsUnknown() && !planData.Mac.IsUnknown() { + var createCheckData *FvEpMacTagResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvEpMacTagId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvEpMacTag", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvEpMacTagResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_endpoint_tag_mac") resp.TypeName = req.ProviderTypeName + "_endpoint_tag_mac" @@ -233,6 +256,13 @@ func (r *FvEpMacTagResource) Create(ctx context.Context, req resource.CreateRequ resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvEpMacTagId(ctx, stateData) getAndSetFvEpMacTagAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvEpMacTag object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvEpMacTagResourceModel @@ -253,7 +283,7 @@ func (r *FvEpMacTagResource) Create(ctx context.Context, req resource.CreateRequ var tagTagPlan, tagTagState []TagTagFvEpMacTagResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvEpMacTagCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvEpMacTagCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -318,7 +348,7 @@ func (r *FvEpMacTagResource) Update(ctx context.Context, req resource.UpdateRequ var tagTagPlan, tagTagState []TagTagFvEpMacTagResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvEpMacTagCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvEpMacTagCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -567,9 +597,13 @@ func getFvEpMacTagTagTagChildPayloads(ctx context.Context, diags *diag.Diagnosti return childPayloads } -func getFvEpMacTagCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvEpMacTagResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvEpMacTagResourceModel, tagTagPlan, tagTagState []TagTagFvEpMacTagResourceModel) *container.Container { +func getFvEpMacTagCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvEpMacTagResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvEpMacTagResourceModel, tagTagPlan, tagTagState []TagTagFvEpMacTagResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvEpMacTagTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_endpoint_tag_mac_test.go b/internal/provider/resource_aci_endpoint_tag_mac_test.go index 5dd5781ef..67f9b51c5 100644 --- a/internal/provider/resource_aci_endpoint_tag_mac_test.go +++ b/internal/provider/resource_aci_endpoint_tag_mac_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,72 @@ import ( func TestAccResourceFvEpMacTagWithFvTenant(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvEpMacTagMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "bd_name", "test_bd_name"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "bd_name", "test_bd_name"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "mac", "00:00:00:00:00:01"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "mac", "00:00:00:00:00:01"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "id_attribute", "0"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "id_attribute", "0"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "name", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "name_alias", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvEpMacTagMinDependencyWithFvTenantAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvEpMacTagMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "bd_name", "test_bd_name"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "bd_name", "test_bd_name"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "mac", "00:00:00:00:00:01"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "mac", "00:00:00:00:00:01"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "id_attribute", "0"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "id_attribute", "0"), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "name", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_endpoint_tag_mac.test_2", "name_alias", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -164,6 +231,20 @@ func TestAccResourceFvEpMacTagWithFvTenant(t *testing.T) { }) } +const testConfigFvEpMacTagMinDependencyWithFvTenantAllowExisting = testConfigFvTenantMin + ` +resource "aci_endpoint_tag_mac" "test" { + parent_dn = aci_tenant.test.id + bd_name = "test_bd_name" + mac = "00:00:00:00:00:01" +} +resource "aci_endpoint_tag_mac" "test_2" { + parent_dn = aci_tenant.test.id + bd_name = "test_bd_name" + mac = "00:00:00:00:00:01" + depends_on = [aci_endpoint_tag_mac.test] +} +` + const testConfigFvEpMacTagMinDependencyWithFvTenant = testConfigFvTenantMin + ` resource "aci_endpoint_tag_mac" "test" { parent_dn = aci_tenant.test.id diff --git a/internal/provider/resource_aci_external_management_network_instance_profile.go b/internal/provider/resource_aci_external_management_network_instance_profile.go index 4d0402787..dd7b2f550 100644 --- a/internal/provider/resource_aci_external_management_network_instance_profile.go +++ b/internal/provider/resource_aci_external_management_network_instance_profile.go @@ -77,6 +77,29 @@ type MgmtInstPIdentifier struct { Name types.String } +func (r *MgmtInstPResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *MgmtInstPResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.Name.IsUnknown() { + var createCheckData *MgmtInstPResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setMgmtInstPId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "mgmtInstP", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *MgmtInstPResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_external_management_network_instance_profile") resp.TypeName = req.ProviderTypeName + "_external_management_network_instance_profile" @@ -265,6 +288,13 @@ func (r *MgmtInstPResource) Create(ctx context.Context, req resource.CreateReque resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setMgmtInstPId(ctx, stateData) getAndSetMgmtInstPAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The mgmtInstP object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *MgmtInstPResourceModel @@ -288,7 +318,7 @@ func (r *MgmtInstPResource) Create(ctx context.Context, req resource.CreateReque var tagTagPlan, tagTagState []TagTagMgmtInstPResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getMgmtInstPCreateJsonPayload(ctx, &resp.Diagnostics, data, mgmtRsOoBConsPlan, mgmtRsOoBConsState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getMgmtInstPCreateJsonPayload(ctx, &resp.Diagnostics, true, data, mgmtRsOoBConsPlan, mgmtRsOoBConsState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -356,7 +386,7 @@ func (r *MgmtInstPResource) Update(ctx context.Context, req resource.UpdateReque var tagTagPlan, tagTagState []TagTagMgmtInstPResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getMgmtInstPCreateJsonPayload(ctx, &resp.Diagnostics, data, mgmtRsOoBConsPlan, mgmtRsOoBConsState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getMgmtInstPCreateJsonPayload(ctx, &resp.Diagnostics, false, data, mgmtRsOoBConsPlan, mgmtRsOoBConsState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -645,9 +675,13 @@ func getMgmtInstPTagTagChildPayloads(ctx context.Context, diags *diag.Diagnostic return childPayloads } -func getMgmtInstPCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *MgmtInstPResourceModel, mgmtRsOoBConsPlan, mgmtRsOoBConsState []MgmtRsOoBConsMgmtInstPResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationMgmtInstPResourceModel, tagTagPlan, tagTagState []TagTagMgmtInstPResourceModel) *container.Container { +func getMgmtInstPCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *MgmtInstPResourceModel, mgmtRsOoBConsPlan, mgmtRsOoBConsState []MgmtRsOoBConsMgmtInstPResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationMgmtInstPResourceModel, tagTagPlan, tagTagState []TagTagMgmtInstPResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} MgmtRsOoBConschildPayloads := getMgmtInstPMgmtRsOoBConsChildPayloads(ctx, diags, data, mgmtRsOoBConsPlan, mgmtRsOoBConsState) diff --git a/internal/provider/resource_aci_external_management_network_instance_profile_test.go b/internal/provider/resource_aci_external_management_network_instance_profile_test.go index 32808d79d..794671ecf 100644 --- a/internal/provider/resource_aci_external_management_network_instance_profile_test.go +++ b/internal/provider/resource_aci_external_management_network_instance_profile_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,66 @@ import ( func TestAccResourceMgmtInstP(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMgmtInstPMinAllowExisting, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "description", ""), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "priority", "unspecified"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMgmtInstPMinAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMgmtInstPMinAllowExisting, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "description", ""), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_external_management_network_instance_profile.test_2", "priority", "unspecified"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -164,6 +225,16 @@ func TestAccResourceMgmtInstP(t *testing.T) { }) } +const testConfigMgmtInstPMinAllowExisting = ` +resource "aci_external_management_network_instance_profile" "test" { + name = "test_name" +} +resource "aci_external_management_network_instance_profile" "test_2" { + name = "test_name" + depends_on = [aci_external_management_network_instance_profile.test] +} +` + const testConfigMgmtInstPMin = ` resource "aci_external_management_network_instance_profile" "test" { name = "test_name" diff --git a/internal/provider/resource_aci_external_management_network_subnet.go b/internal/provider/resource_aci_external_management_network_subnet.go index 846fbe0de..ecb02f8ba 100644 --- a/internal/provider/resource_aci_external_management_network_subnet.go +++ b/internal/provider/resource_aci_external_management_network_subnet.go @@ -68,6 +68,29 @@ type MgmtSubnetIdentifier struct { Ip types.String } +func (r *MgmtSubnetResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *MgmtSubnetResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Ip.IsUnknown() { + var createCheckData *MgmtSubnetResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setMgmtSubnetId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "mgmtSubnet", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *MgmtSubnetResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_external_management_network_subnet") resp.TypeName = req.ProviderTypeName + "_external_management_network_subnet" @@ -223,6 +246,13 @@ func (r *MgmtSubnetResource) Create(ctx context.Context, req resource.CreateRequ resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setMgmtSubnetId(ctx, stateData) getAndSetMgmtSubnetAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The mgmtSubnet object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *MgmtSubnetResourceModel @@ -243,7 +273,7 @@ func (r *MgmtSubnetResource) Create(ctx context.Context, req resource.CreateRequ var tagTagPlan, tagTagState []TagTagMgmtSubnetResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getMgmtSubnetCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getMgmtSubnetCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -308,7 +338,7 @@ func (r *MgmtSubnetResource) Update(ctx context.Context, req resource.UpdateRequ var tagTagPlan, tagTagState []TagTagMgmtSubnetResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getMgmtSubnetCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getMgmtSubnetCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -552,9 +582,13 @@ func getMgmtSubnetTagTagChildPayloads(ctx context.Context, diags *diag.Diagnosti return childPayloads } -func getMgmtSubnetCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *MgmtSubnetResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationMgmtSubnetResourceModel, tagTagPlan, tagTagState []TagTagMgmtSubnetResourceModel) *container.Container { +func getMgmtSubnetCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *MgmtSubnetResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationMgmtSubnetResourceModel, tagTagPlan, tagTagState []TagTagMgmtSubnetResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getMgmtSubnetTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_external_management_network_subnet_test.go b/internal/provider/resource_aci_external_management_network_subnet_test.go index 6bdd85a8f..c89265d94 100644 --- a/internal/provider/resource_aci_external_management_network_subnet_test.go +++ b/internal/provider/resource_aci_external_management_network_subnet_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,68 @@ import ( func TestAccResourceMgmtSubnetWithMgmtInstP(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMgmtSubnetMinDependencyWithMgmtInstPAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "ip", "1.1.1.0/24"), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "ip", "1.1.1.0/24"), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "description", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "name", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "name_alias", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMgmtSubnetMinDependencyWithMgmtInstPAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMgmtSubnetMinDependencyWithMgmtInstPAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "ip", "1.1.1.0/24"), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "ip", "1.1.1.0/24"), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "description", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "name", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_external_management_network_subnet.test_2", "name_alias", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -157,6 +220,18 @@ func TestAccResourceMgmtSubnetWithMgmtInstP(t *testing.T) { }) } +const testConfigMgmtSubnetMinDependencyWithMgmtInstPAllowExisting = testConfigMgmtInstPMin + ` +resource "aci_external_management_network_subnet" "test" { + parent_dn = aci_external_management_network_instance_profile.test.id + ip = "1.1.1.0/24" +} +resource "aci_external_management_network_subnet" "test_2" { + parent_dn = aci_external_management_network_instance_profile.test.id + ip = "1.1.1.0/24" + depends_on = [aci_external_management_network_subnet.test] +} +` + const testConfigMgmtSubnetMinDependencyWithMgmtInstP = testConfigMgmtInstPMin + ` resource "aci_external_management_network_subnet" "test" { parent_dn = aci_external_management_network_instance_profile.test.id diff --git a/internal/provider/resource_aci_l3out_consumer_label.go b/internal/provider/resource_aci_l3out_consumer_label.go index b3c602508..d1b306589 100644 --- a/internal/provider/resource_aci_l3out_consumer_label.go +++ b/internal/provider/resource_aci_l3out_consumer_label.go @@ -73,6 +73,29 @@ type L3extConsLblIdentifier struct { Name types.String } +func (r *L3extConsLblResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *L3extConsLblResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Name.IsUnknown() { + var createCheckData *L3extConsLblResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setL3extConsLblId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "l3extConsLbl", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *L3extConsLblResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_l3out_consumer_label") resp.TypeName = req.ProviderTypeName + "_l3out_consumer_label" @@ -258,6 +281,13 @@ func (r *L3extConsLblResource) Create(ctx context.Context, req resource.CreateRe resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setL3extConsLblId(ctx, stateData) getAndSetL3extConsLblAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The l3extConsLbl object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *L3extConsLblResourceModel @@ -278,7 +308,7 @@ func (r *L3extConsLblResource) Create(ctx context.Context, req resource.CreateRe var tagTagPlan, tagTagState []TagTagL3extConsLblResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getL3extConsLblCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getL3extConsLblCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -343,7 +373,7 @@ func (r *L3extConsLblResource) Update(ctx context.Context, req resource.UpdateRe var tagTagPlan, tagTagState []TagTagL3extConsLblResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getL3extConsLblCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getL3extConsLblCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -596,9 +626,13 @@ func getL3extConsLblTagTagChildPayloads(ctx context.Context, diags *diag.Diagnos return childPayloads } -func getL3extConsLblCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *L3extConsLblResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationL3extConsLblResourceModel, tagTagPlan, tagTagState []TagTagL3extConsLblResourceModel) *container.Container { +func getL3extConsLblCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *L3extConsLblResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationL3extConsLblResourceModel, tagTagPlan, tagTagState []TagTagL3extConsLblResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getL3extConsLblTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_l3out_consumer_label_test.go b/internal/provider/resource_aci_l3out_consumer_label_test.go index 01a8459da..95a2a474b 100644 --- a/internal/provider/resource_aci_l3out_consumer_label_test.go +++ b/internal/provider/resource_aci_l3out_consumer_label_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,80 @@ import ( func TestAccResourceL3extConsLblWithL3extOut(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extConsLblMinDependencyWithL3extOutAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "owner", "infra"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "owner", "infra"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "tag", "yellow-green"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "tag", "yellow-green"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extConsLblMinDependencyWithL3extOutAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extConsLblMinDependencyWithL3extOutAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "owner", "infra"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "owner", "infra"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test", "tag", "yellow-green"), + resource.TestCheckResourceAttr("aci_l3out_consumer_label.test_2", "tag", "yellow-green"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -175,6 +250,18 @@ func TestAccResourceL3extConsLblWithL3extOut(t *testing.T) { }) } +const testConfigL3extConsLblMinDependencyWithL3extOutAllowExisting = testConfigL3extOutMin + ` +resource "aci_l3out_consumer_label" "test" { + parent_dn = aci_l3_outside.test.id + name = "test_name" +} +resource "aci_l3out_consumer_label" "test_2" { + parent_dn = aci_l3_outside.test.id + name = "test_name" + depends_on = [aci_l3out_consumer_label.test] +} +` + const testConfigL3extConsLblMinDependencyWithL3extOut = testConfigL3extOutMin + ` resource "aci_l3out_consumer_label" "test" { parent_dn = aci_l3_outside.test.id diff --git a/internal/provider/resource_aci_l3out_node_sid_profile.go b/internal/provider/resource_aci_l3out_node_sid_profile.go index b64363c56..d3e456686 100644 --- a/internal/provider/resource_aci_l3out_node_sid_profile.go +++ b/internal/provider/resource_aci_l3out_node_sid_profile.go @@ -69,6 +69,29 @@ type MplsNodeSidPIdentifier struct { Sidoffset types.String } +func (r *MplsNodeSidPResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *MplsNodeSidPResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Sidoffset.IsUnknown() { + var createCheckData *MplsNodeSidPResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setMplsNodeSidPId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "mplsNodeSidP", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *MplsNodeSidPResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_l3out_node_sid_profile") resp.TypeName = req.ProviderTypeName + "_l3out_node_sid_profile" @@ -232,6 +255,13 @@ func (r *MplsNodeSidPResource) Create(ctx context.Context, req resource.CreateRe resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setMplsNodeSidPId(ctx, stateData) getAndSetMplsNodeSidPAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The mplsNodeSidP object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *MplsNodeSidPResourceModel @@ -252,7 +282,7 @@ func (r *MplsNodeSidPResource) Create(ctx context.Context, req resource.CreateRe var tagTagPlan, tagTagState []TagTagMplsNodeSidPResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getMplsNodeSidPCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getMplsNodeSidPCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -317,7 +347,7 @@ func (r *MplsNodeSidPResource) Update(ctx context.Context, req resource.UpdateRe var tagTagPlan, tagTagState []TagTagMplsNodeSidPResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getMplsNodeSidPCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getMplsNodeSidPCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -564,9 +594,13 @@ func getMplsNodeSidPTagTagChildPayloads(ctx context.Context, diags *diag.Diagnos return childPayloads } -func getMplsNodeSidPCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *MplsNodeSidPResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationMplsNodeSidPResourceModel, tagTagPlan, tagTagState []TagTagMplsNodeSidPResourceModel) *container.Container { +func getMplsNodeSidPCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *MplsNodeSidPResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationMplsNodeSidPResourceModel, tagTagPlan, tagTagState []TagTagMplsNodeSidPResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getMplsNodeSidPTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_l3out_node_sid_profile_test.go b/internal/provider/resource_aci_l3out_node_sid_profile_test.go index 553f66076..98c5a9867 100644 --- a/internal/provider/resource_aci_l3out_node_sid_profile_test.go +++ b/internal/provider/resource_aci_l3out_node_sid_profile_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,72 @@ import ( func TestAccResourceMplsNodeSidPWithL3extLoopBackIfP(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMplsNodeSidPMinDependencyWithL3extLoopBackIfPAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "segment_id", "1"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "segment_id", "1"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "loopback_address", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "loopback_address", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "name", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "name_alias", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMplsNodeSidPMinDependencyWithL3extLoopBackIfPAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMplsNodeSidPMinDependencyWithL3extLoopBackIfPAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "segment_id", "1"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "segment_id", "1"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "loopback_address", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "loopback_address", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "name", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_node_sid_profile.test_2", "name_alias", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -163,6 +230,18 @@ func TestAccResourceMplsNodeSidPWithL3extLoopBackIfP(t *testing.T) { }) } +const testConfigMplsNodeSidPMinDependencyWithL3extLoopBackIfPAllowExisting = testConfigL3extLoopBackIfPMinDependencyWithL3extRsNodeL3OutAtt + ` +resource "aci_l3out_node_sid_profile" "test" { + parent_dn = aci_l3out_loopback_interface_profile.test.id + segment_id = "1" +} +resource "aci_l3out_node_sid_profile" "test_2" { + parent_dn = aci_l3out_loopback_interface_profile.test.id + segment_id = "1" + depends_on = [aci_l3out_node_sid_profile.test] +} +` + const testConfigMplsNodeSidPMinDependencyWithL3extLoopBackIfP = testConfigL3extLoopBackIfPMinDependencyWithL3extRsNodeL3OutAtt + ` resource "aci_l3out_node_sid_profile" "test" { parent_dn = aci_l3out_loopback_interface_profile.test.id diff --git a/internal/provider/resource_aci_l3out_provider_label.go b/internal/provider/resource_aci_l3out_provider_label.go index fba072364..5a56802db 100644 --- a/internal/provider/resource_aci_l3out_provider_label.go +++ b/internal/provider/resource_aci_l3out_provider_label.go @@ -72,6 +72,29 @@ type L3extProvLblIdentifier struct { Name types.String } +func (r *L3extProvLblResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *L3extProvLblResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Name.IsUnknown() { + var createCheckData *L3extProvLblResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setL3extProvLblId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "l3extProvLbl", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *L3extProvLblResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_l3out_provider_label") resp.TypeName = req.ProviderTypeName + "_l3out_provider_label" @@ -246,6 +269,13 @@ func (r *L3extProvLblResource) Create(ctx context.Context, req resource.CreateRe resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setL3extProvLblId(ctx, stateData) getAndSetL3extProvLblAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The l3extProvLbl object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *L3extProvLblResourceModel @@ -266,7 +296,7 @@ func (r *L3extProvLblResource) Create(ctx context.Context, req resource.CreateRe var tagTagPlan, tagTagState []TagTagL3extProvLblResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getL3extProvLblCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getL3extProvLblCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -331,7 +361,7 @@ func (r *L3extProvLblResource) Update(ctx context.Context, req resource.UpdateRe var tagTagPlan, tagTagState []TagTagL3extProvLblResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getL3extProvLblCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getL3extProvLblCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -581,9 +611,13 @@ func getL3extProvLblTagTagChildPayloads(ctx context.Context, diags *diag.Diagnos return childPayloads } -func getL3extProvLblCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *L3extProvLblResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationL3extProvLblResourceModel, tagTagPlan, tagTagState []TagTagL3extProvLblResourceModel) *container.Container { +func getL3extProvLblCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *L3extProvLblResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationL3extProvLblResourceModel, tagTagPlan, tagTagState []TagTagL3extProvLblResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getL3extProvLblTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_l3out_provider_label_test.go b/internal/provider/resource_aci_l3out_provider_label_test.go index 62a576782..d29e76b5e 100644 --- a/internal/provider/resource_aci_l3out_provider_label_test.go +++ b/internal/provider/resource_aci_l3out_provider_label_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,76 @@ import ( func TestAccResourceL3extProvLblWithL3extOut(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extProvLblMinDependencyWithL3extOutAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "name", "prov_label"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "name", "prov_label"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "tag", "yellow-green"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "tag", "yellow-green"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extProvLblMinDependencyWithL3extOutAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extProvLblMinDependencyWithL3extOutAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "name", "prov_label"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "name", "prov_label"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test", "tag", "yellow-green"), + resource.TestCheckResourceAttr("aci_l3out_provider_label.test_2", "tag", "yellow-green"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -169,6 +240,18 @@ func TestAccResourceL3extProvLblWithL3extOut(t *testing.T) { }) } +const testConfigL3extProvLblMinDependencyWithL3extOutAllowExisting = testConfigL3extOutMinDependencyWithFvTenantInfra + ` +resource "aci_l3out_provider_label" "test" { + parent_dn = aci_l3_outside.test.id + name = "prov_label" +} +resource "aci_l3out_provider_label" "test_2" { + parent_dn = aci_l3_outside.test.id + name = "prov_label" + depends_on = [aci_l3out_provider_label.test] +} +` + const testConfigL3extProvLblMinDependencyWithL3extOut = testConfigL3extOutMinDependencyWithFvTenantInfra + ` resource "aci_l3out_provider_label" "test" { parent_dn = aci_l3_outside.test.id diff --git a/internal/provider/resource_aci_l3out_redistribute_policy.go b/internal/provider/resource_aci_l3out_redistribute_policy.go index 09116d7bc..30c04bdad 100644 --- a/internal/provider/resource_aci_l3out_redistribute_policy.go +++ b/internal/provider/resource_aci_l3out_redistribute_policy.go @@ -69,6 +69,29 @@ type L3extRsRedistributePolIdentifier struct { TnRtctrlProfileName types.String } +func (r *L3extRsRedistributePolResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *L3extRsRedistributePolResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Src.IsUnknown() && !planData.TnRtctrlProfileName.IsUnknown() { + var createCheckData *L3extRsRedistributePolResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setL3extRsRedistributePolId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "l3extRsRedistributePol", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *L3extRsRedistributePolResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_l3out_redistribute_policy") resp.TypeName = req.ProviderTypeName + "_l3out_redistribute_policy" @@ -211,6 +234,13 @@ func (r *L3extRsRedistributePolResource) Create(ctx context.Context, req resourc resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setL3extRsRedistributePolId(ctx, stateData) getAndSetL3extRsRedistributePolAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The l3extRsRedistributePol object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *L3extRsRedistributePolResourceModel @@ -231,7 +261,7 @@ func (r *L3extRsRedistributePolResource) Create(ctx context.Context, req resourc var tagTagPlan, tagTagState []TagTagL3extRsRedistributePolResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getL3extRsRedistributePolCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getL3extRsRedistributePolCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -296,7 +326,7 @@ func (r *L3extRsRedistributePolResource) Update(ctx context.Context, req resourc var tagTagPlan, tagTagState []TagTagL3extRsRedistributePolResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getL3extRsRedistributePolCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getL3extRsRedistributePolCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -534,9 +564,13 @@ func getL3extRsRedistributePolTagTagChildPayloads(ctx context.Context, diags *di return childPayloads } -func getL3extRsRedistributePolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *L3extRsRedistributePolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationL3extRsRedistributePolResourceModel, tagTagPlan, tagTagState []TagTagL3extRsRedistributePolResourceModel) *container.Container { +func getL3extRsRedistributePolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *L3extRsRedistributePolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationL3extRsRedistributePolResourceModel, tagTagPlan, tagTagState []TagTagL3extRsRedistributePolResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getL3extRsRedistributePolTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_l3out_redistribute_policy_test.go b/internal/provider/resource_aci_l3out_redistribute_policy_test.go index dae8b08af..24bd6bbfd 100644 --- a/internal/provider/resource_aci_l3out_redistribute_policy_test.go +++ b/internal/provider/resource_aci_l3out_redistribute_policy_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,60 @@ import ( func TestAccResourceL3extRsRedistributePolWithL3extOut(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extRsRedistributePolMinDependencyWithL3extOutAllowExisting, + ExpectNonEmptyPlan: true, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test", "route_control_profile_name", "test_tn_rtctrl_profile_name"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test_2", "route_control_profile_name", "test_tn_rtctrl_profile_name"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test", "source", "direct"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test_2", "source", "direct"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extRsRedistributePolMinDependencyWithL3extOutAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extRsRedistributePolMinDependencyWithL3extOutAllowExisting, + ExpectNonEmptyPlan: true, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test", "route_control_profile_name", "test_tn_rtctrl_profile_name"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test_2", "route_control_profile_name", "test_tn_rtctrl_profile_name"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test", "source", "direct"), + resource.TestCheckResourceAttr("aci_l3out_redistribute_policy.test_2", "source", "direct"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -146,6 +201,20 @@ func TestAccResourceL3extRsRedistributePolWithL3extOut(t *testing.T) { }) } +const testConfigL3extRsRedistributePolMinDependencyWithL3extOutAllowExisting = testConfigL3extOutMinDependencyWithFvTenant + ` +resource "aci_l3out_redistribute_policy" "test" { + parent_dn = aci_l3_outside.test.id + route_control_profile_name = "test_tn_rtctrl_profile_name" + source = "direct" +} +resource "aci_l3out_redistribute_policy" "test_2" { + parent_dn = aci_l3_outside.test.id + route_control_profile_name = "test_tn_rtctrl_profile_name" + source = "direct" + depends_on = [aci_l3out_redistribute_policy.test] +} +` + const testConfigL3extRsRedistributePolMinDependencyWithL3extOut = testConfigL3extOutMinDependencyWithFvTenant + ` resource "aci_l3out_redistribute_policy" "test" { parent_dn = aci_l3_outside.test.id diff --git a/internal/provider/resource_aci_netflow_monitor_policy.go b/internal/provider/resource_aci_netflow_monitor_policy.go index e3f1251a8..e7ae1c44b 100644 --- a/internal/provider/resource_aci_netflow_monitor_policy.go +++ b/internal/provider/resource_aci_netflow_monitor_policy.go @@ -85,6 +85,29 @@ type NetflowMonitorPolIdentifier struct { Name types.String } +func (r *NetflowMonitorPolResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *NetflowMonitorPolResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Name.IsUnknown() { + var createCheckData *NetflowMonitorPolResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setNetflowMonitorPolId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "netflowMonitorPol", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *NetflowMonitorPolResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_netflow_monitor_policy") resp.TypeName = req.ProviderTypeName + "_netflow_monitor_policy" @@ -308,6 +331,13 @@ func (r *NetflowMonitorPolResource) Create(ctx context.Context, req resource.Cre resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setNetflowMonitorPolId(ctx, stateData) getAndSetNetflowMonitorPolAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The netflowMonitorPol object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *NetflowMonitorPolResourceModel @@ -334,7 +364,7 @@ func (r *NetflowMonitorPolResource) Create(ctx context.Context, req resource.Cre var tagTagPlan, tagTagState []TagTagNetflowMonitorPolResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getNetflowMonitorPolCreateJsonPayload(ctx, &resp.Diagnostics, data, netflowRsMonitorToExporterPlan, netflowRsMonitorToExporterState, netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getNetflowMonitorPolCreateJsonPayload(ctx, &resp.Diagnostics, true, data, netflowRsMonitorToExporterPlan, netflowRsMonitorToExporterState, netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -405,7 +435,7 @@ func (r *NetflowMonitorPolResource) Update(ctx context.Context, req resource.Upd var tagTagPlan, tagTagState []TagTagNetflowMonitorPolResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getNetflowMonitorPolCreateJsonPayload(ctx, &resp.Diagnostics, data, netflowRsMonitorToExporterPlan, netflowRsMonitorToExporterState, netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getNetflowMonitorPolCreateJsonPayload(ctx, &resp.Diagnostics, false, data, netflowRsMonitorToExporterPlan, netflowRsMonitorToExporterState, netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -752,9 +782,13 @@ func getNetflowMonitorPolTagTagChildPayloads(ctx context.Context, diags *diag.Di return childPayloads } -func getNetflowMonitorPolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *NetflowMonitorPolResourceModel, netflowRsMonitorToExporterPlan, netflowRsMonitorToExporterState []NetflowRsMonitorToExporterNetflowMonitorPolResourceModel, netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState []NetflowRsMonitorToRecordNetflowMonitorPolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowMonitorPolResourceModel, tagTagPlan, tagTagState []TagTagNetflowMonitorPolResourceModel) *container.Container { +func getNetflowMonitorPolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *NetflowMonitorPolResourceModel, netflowRsMonitorToExporterPlan, netflowRsMonitorToExporterState []NetflowRsMonitorToExporterNetflowMonitorPolResourceModel, netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState []NetflowRsMonitorToRecordNetflowMonitorPolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowMonitorPolResourceModel, tagTagPlan, tagTagState []TagTagNetflowMonitorPolResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} NetflowRsMonitorToExporterchildPayloads := getNetflowMonitorPolNetflowRsMonitorToExporterChildPayloads(ctx, diags, data, netflowRsMonitorToExporterPlan, netflowRsMonitorToExporterState) diff --git a/internal/provider/resource_aci_netflow_monitor_policy_test.go b/internal/provider/resource_aci_netflow_monitor_policy_test.go index 1c5fae7a0..bd3f8baa0 100644 --- a/internal/provider/resource_aci_netflow_monitor_policy_test.go +++ b/internal/provider/resource_aci_netflow_monitor_policy_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,72 @@ import ( func TestAccResourceNetflowMonitorPolWithFvTenant(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigNetflowMonitorPolMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "name", "netfow_monitor"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "name", "netfow_monitor"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "description", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "owner_tag", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigNetflowMonitorPolMinDependencyWithFvTenantAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigNetflowMonitorPolMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "name", "netfow_monitor"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "name", "netfow_monitor"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "description", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test_2", "owner_tag", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -193,6 +260,18 @@ func TestAccResourceNetflowMonitorPolWithFvTenant(t *testing.T) { }) } +const testConfigNetflowMonitorPolMinDependencyWithFvTenantAllowExisting = testConfigFvTenantMin + ` +resource "aci_netflow_monitor_policy" "test" { + parent_dn = aci_tenant.test.id + name = "netfow_monitor" +} +resource "aci_netflow_monitor_policy" "test_2" { + parent_dn = aci_tenant.test.id + name = "netfow_monitor" + depends_on = [aci_netflow_monitor_policy.test] +} +` + const testConfigNetflowMonitorPolMinDependencyWithFvTenant = testConfigFvTenantMin + ` resource "aci_netflow_monitor_policy" "test" { parent_dn = aci_tenant.test.id diff --git a/internal/provider/resource_aci_netflow_record_policy.go b/internal/provider/resource_aci_netflow_record_policy.go index 15d098672..f4713fb24 100644 --- a/internal/provider/resource_aci_netflow_record_policy.go +++ b/internal/provider/resource_aci_netflow_record_policy.go @@ -74,6 +74,29 @@ type NetflowRecordPolIdentifier struct { Name types.String } +func (r *NetflowRecordPolResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *NetflowRecordPolResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Name.IsUnknown() { + var createCheckData *NetflowRecordPolResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setNetflowRecordPolId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "netflowRecordPol", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *NetflowRecordPolResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_netflow_record_policy") resp.TypeName = req.ProviderTypeName + "_netflow_record_policy" @@ -269,6 +292,13 @@ func (r *NetflowRecordPolResource) Create(ctx context.Context, req resource.Crea resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setNetflowRecordPolId(ctx, stateData) getAndSetNetflowRecordPolAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The netflowRecordPol object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *NetflowRecordPolResourceModel @@ -289,7 +319,7 @@ func (r *NetflowRecordPolResource) Create(ctx context.Context, req resource.Crea var tagTagPlan, tagTagState []TagTagNetflowRecordPolResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getNetflowRecordPolCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getNetflowRecordPolCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -354,7 +384,7 @@ func (r *NetflowRecordPolResource) Update(ctx context.Context, req resource.Upda var tagTagPlan, tagTagState []TagTagNetflowRecordPolResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getNetflowRecordPolCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getNetflowRecordPolCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -617,9 +647,13 @@ func getNetflowRecordPolTagTagChildPayloads(ctx context.Context, diags *diag.Dia return childPayloads } -func getNetflowRecordPolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *NetflowRecordPolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowRecordPolResourceModel, tagTagPlan, tagTagState []TagTagNetflowRecordPolResourceModel) *container.Container { +func getNetflowRecordPolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *NetflowRecordPolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowRecordPolResourceModel, tagTagPlan, tagTagState []TagTagNetflowRecordPolResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getNetflowRecordPolTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_netflow_record_policy_test.go b/internal/provider/resource_aci_netflow_record_policy_test.go index 217e377c9..190b9c239 100644 --- a/internal/provider/resource_aci_netflow_record_policy_test.go +++ b/internal/provider/resource_aci_netflow_record_policy_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,84 @@ import ( func TestAccResourceNetflowRecordPolWithFvTenant(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigNetflowRecordPolMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "name", "netfow_record"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "name", "netfow_record"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "collect_parameters.#", "1"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "collect_parameters.#", "1"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "collect_parameters.0", "src-intf"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "collect_parameters.0", "src-intf"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "description", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "match_parameters.#", "0"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "match_parameters.#", "0"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "owner_tag", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigNetflowRecordPolMinDependencyWithFvTenantAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigNetflowRecordPolMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "name", "netfow_record"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "name", "netfow_record"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "collect_parameters.#", "1"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "collect_parameters.#", "1"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "collect_parameters.0", "src-intf"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "collect_parameters.0", "src-intf"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "description", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "match_parameters.#", "0"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "match_parameters.#", "0"), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_netflow_record_policy.test_2", "owner_tag", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -184,6 +263,18 @@ func TestAccResourceNetflowRecordPolWithFvTenant(t *testing.T) { }) } +const testConfigNetflowRecordPolMinDependencyWithFvTenantAllowExisting = testConfigFvTenantMin + ` +resource "aci_netflow_record_policy" "test" { + parent_dn = aci_tenant.test.id + name = "netfow_record" +} +resource "aci_netflow_record_policy" "test_2" { + parent_dn = aci_tenant.test.id + name = "netfow_record" + depends_on = [aci_netflow_record_policy.test] +} +` + const testConfigNetflowRecordPolMinDependencyWithFvTenant = testConfigFvTenantMin + ` resource "aci_netflow_record_policy" "test" { parent_dn = aci_tenant.test.id diff --git a/internal/provider/resource_aci_out_of_band_contract.go b/internal/provider/resource_aci_out_of_band_contract.go index f5599299e..04a39b7de 100644 --- a/internal/provider/resource_aci_out_of_band_contract.go +++ b/internal/provider/resource_aci_out_of_band_contract.go @@ -74,6 +74,29 @@ type VzOOBBrCPIdentifier struct { Name types.String } +func (r *VzOOBBrCPResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *VzOOBBrCPResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.Name.IsUnknown() { + var createCheckData *VzOOBBrCPResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setVzOOBBrCPId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "vzOOBBrCP", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *VzOOBBrCPResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_out_of_band_contract") resp.TypeName = req.ProviderTypeName + "_out_of_band_contract" @@ -273,6 +296,13 @@ func (r *VzOOBBrCPResource) Create(ctx context.Context, req resource.CreateReque resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setVzOOBBrCPId(ctx, stateData) getAndSetVzOOBBrCPAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The vzOOBBrCP object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *VzOOBBrCPResourceModel @@ -293,7 +323,7 @@ func (r *VzOOBBrCPResource) Create(ctx context.Context, req resource.CreateReque var tagTagPlan, tagTagState []TagTagVzOOBBrCPResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getVzOOBBrCPCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getVzOOBBrCPCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -358,7 +388,7 @@ func (r *VzOOBBrCPResource) Update(ctx context.Context, req resource.UpdateReque var tagTagPlan, tagTagState []TagTagVzOOBBrCPResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getVzOOBBrCPCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getVzOOBBrCPCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -600,9 +630,13 @@ func getVzOOBBrCPTagTagChildPayloads(ctx context.Context, diags *diag.Diagnostic return childPayloads } -func getVzOOBBrCPCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *VzOOBBrCPResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationVzOOBBrCPResourceModel, tagTagPlan, tagTagState []TagTagVzOOBBrCPResourceModel) *container.Container { +func getVzOOBBrCPCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *VzOOBBrCPResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationVzOOBBrCPResourceModel, tagTagPlan, tagTagState []TagTagVzOOBBrCPResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getVzOOBBrCPTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_out_of_band_contract_test.go b/internal/provider/resource_aci_out_of_band_contract_test.go index baabe7f5c..c7070319b 100644 --- a/internal/provider/resource_aci_out_of_band_contract_test.go +++ b/internal/provider/resource_aci_out_of_band_contract_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,86 @@ import ( func TestAccResourceVzOOBBrCP(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigVzOOBBrCPMinAllowExisting, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "description", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "intent", "install"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "intent", "install"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "scope", "context"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "scope", "context"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "target_dscp", "unspecified"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "target_dscp", "unspecified"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigVzOOBBrCPMinAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigVzOOBBrCPMinAllowExisting, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "description", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "intent", "install"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "intent", "install"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "scope", "context"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "scope", "context"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test", "target_dscp", "unspecified"), + resource.TestCheckResourceAttr("aci_out_of_band_contract.test_2", "target_dscp", "unspecified"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -190,6 +271,16 @@ func TestAccResourceVzOOBBrCP(t *testing.T) { }) } +const testConfigVzOOBBrCPMinAllowExisting = ` +resource "aci_out_of_band_contract" "test" { + name = "test_name" +} +resource "aci_out_of_band_contract" "test_2" { + name = "test_name" + depends_on = [aci_out_of_band_contract.test] +} +` + const testConfigVzOOBBrCPMin = ` resource "aci_out_of_band_contract" "test" { name = "test_name" diff --git a/internal/provider/resource_aci_pim_route_map_entry.go b/internal/provider/resource_aci_pim_route_map_entry.go index 1a05ea464..956f28cbc 100644 --- a/internal/provider/resource_aci_pim_route_map_entry.go +++ b/internal/provider/resource_aci_pim_route_map_entry.go @@ -74,6 +74,29 @@ type PimRouteMapEntryIdentifier struct { Order types.String } +func (r *PimRouteMapEntryResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *PimRouteMapEntryResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Order.IsUnknown() { + var createCheckData *PimRouteMapEntryResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setPimRouteMapEntryId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "pimRouteMapEntry", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *PimRouteMapEntryResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_pim_route_map_entry") resp.TypeName = req.ProviderTypeName + "_pim_route_map_entry" @@ -264,6 +287,13 @@ func (r *PimRouteMapEntryResource) Create(ctx context.Context, req resource.Crea resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setPimRouteMapEntryId(ctx, stateData) getAndSetPimRouteMapEntryAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The pimRouteMapEntry object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *PimRouteMapEntryResourceModel @@ -284,7 +314,7 @@ func (r *PimRouteMapEntryResource) Create(ctx context.Context, req resource.Crea var tagTagPlan, tagTagState []TagTagPimRouteMapEntryResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getPimRouteMapEntryCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getPimRouteMapEntryCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -349,7 +379,7 @@ func (r *PimRouteMapEntryResource) Update(ctx context.Context, req resource.Upda var tagTagPlan, tagTagState []TagTagPimRouteMapEntryResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getPimRouteMapEntryCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getPimRouteMapEntryCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -605,9 +635,13 @@ func getPimRouteMapEntryTagTagChildPayloads(ctx context.Context, diags *diag.Dia return childPayloads } -func getPimRouteMapEntryCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *PimRouteMapEntryResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationPimRouteMapEntryResourceModel, tagTagPlan, tagTagState []TagTagPimRouteMapEntryResourceModel) *container.Container { +func getPimRouteMapEntryCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *PimRouteMapEntryResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationPimRouteMapEntryResourceModel, tagTagPlan, tagTagState []TagTagPimRouteMapEntryResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getPimRouteMapEntryTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_pim_route_map_entry_test.go b/internal/provider/resource_aci_pim_route_map_entry_test.go index 162dade9a..fe3c34a3f 100644 --- a/internal/provider/resource_aci_pim_route_map_entry_test.go +++ b/internal/provider/resource_aci_pim_route_map_entry_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,84 @@ import ( func TestAccResourcePimRouteMapEntryWithPimRouteMapPol(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigPimRouteMapEntryMinDependencyWithPimRouteMapPolAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "order", "1"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "order", "1"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "action", "permit"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "action", "permit"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "description", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "group_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "group_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "name", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "rendezvous_point_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "rendezvous_point_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "source_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "source_ip", "0.0.0.0"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigPimRouteMapEntryMinDependencyWithPimRouteMapPolAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigPimRouteMapEntryMinDependencyWithPimRouteMapPolAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "order", "1"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "order", "1"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "action", "permit"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "action", "permit"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "description", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "group_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "group_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "name", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "rendezvous_point_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "rendezvous_point_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test", "source_ip", "0.0.0.0"), + resource.TestCheckResourceAttr("aci_pim_route_map_entry.test_2", "source_ip", "0.0.0.0"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -181,6 +260,18 @@ func TestAccResourcePimRouteMapEntryWithPimRouteMapPol(t *testing.T) { }) } +const testConfigPimRouteMapEntryMinDependencyWithPimRouteMapPolAllowExisting = testConfigPimRouteMapPolMinDependencyWithFvTenant + ` +resource "aci_pim_route_map_entry" "test" { + parent_dn = aci_pim_route_map_policy.test.id + order = "1" +} +resource "aci_pim_route_map_entry" "test_2" { + parent_dn = aci_pim_route_map_policy.test.id + order = "1" + depends_on = [aci_pim_route_map_entry.test] +} +` + const testConfigPimRouteMapEntryMinDependencyWithPimRouteMapPol = testConfigPimRouteMapPolMinDependencyWithFvTenant + ` resource "aci_pim_route_map_entry" "test" { parent_dn = aci_pim_route_map_policy.test.id diff --git a/internal/provider/resource_aci_pim_route_map_policy.go b/internal/provider/resource_aci_pim_route_map_policy.go index c63bb3e79..ca399eace 100644 --- a/internal/provider/resource_aci_pim_route_map_policy.go +++ b/internal/provider/resource_aci_pim_route_map_policy.go @@ -69,6 +69,29 @@ type PimRouteMapPolIdentifier struct { Name types.String } +func (r *PimRouteMapPolResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *PimRouteMapPolResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Name.IsUnknown() { + var createCheckData *PimRouteMapPolResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setPimRouteMapPolId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "pimRouteMapPol", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *PimRouteMapPolResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_pim_route_map_policy") resp.TypeName = req.ProviderTypeName + "_pim_route_map_policy" @@ -232,6 +255,13 @@ func (r *PimRouteMapPolResource) Create(ctx context.Context, req resource.Create resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setPimRouteMapPolId(ctx, stateData) getAndSetPimRouteMapPolAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The pimRouteMapPol object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *PimRouteMapPolResourceModel @@ -252,7 +282,7 @@ func (r *PimRouteMapPolResource) Create(ctx context.Context, req resource.Create var tagTagPlan, tagTagState []TagTagPimRouteMapPolResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getPimRouteMapPolCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getPimRouteMapPolCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -317,7 +347,7 @@ func (r *PimRouteMapPolResource) Update(ctx context.Context, req resource.Update var tagTagPlan, tagTagState []TagTagPimRouteMapPolResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getPimRouteMapPolCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getPimRouteMapPolCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -564,9 +594,13 @@ func getPimRouteMapPolTagTagChildPayloads(ctx context.Context, diags *diag.Diagn return childPayloads } -func getPimRouteMapPolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *PimRouteMapPolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationPimRouteMapPolResourceModel, tagTagPlan, tagTagState []TagTagPimRouteMapPolResourceModel) *container.Container { +func getPimRouteMapPolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *PimRouteMapPolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationPimRouteMapPolResourceModel, tagTagPlan, tagTagState []TagTagPimRouteMapPolResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getPimRouteMapPolTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_pim_route_map_policy_test.go b/internal/provider/resource_aci_pim_route_map_policy_test.go index aff79ff65..57779c35b 100644 --- a/internal/provider/resource_aci_pim_route_map_policy_test.go +++ b/internal/provider/resource_aci_pim_route_map_policy_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,72 @@ import ( func TestAccResourcePimRouteMapPolWithFvTenant(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigPimRouteMapPolMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "description", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "owner_tag", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigPimRouteMapPolMinDependencyWithFvTenantAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigPimRouteMapPolMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "name", "test_name"), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "name", "test_name"), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "description", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "name_alias", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "owner_key", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "owner_key", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test", "owner_tag", ""), + resource.TestCheckResourceAttr("aci_pim_route_map_policy.test_2", "owner_tag", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -163,6 +230,18 @@ func TestAccResourcePimRouteMapPolWithFvTenant(t *testing.T) { }) } +const testConfigPimRouteMapPolMinDependencyWithFvTenantAllowExisting = testConfigFvTenantMin + ` +resource "aci_pim_route_map_policy" "test" { + parent_dn = aci_tenant.test.id + name = "test_name" +} +resource "aci_pim_route_map_policy" "test_2" { + parent_dn = aci_tenant.test.id + name = "test_name" + depends_on = [aci_pim_route_map_policy.test] +} +` + const testConfigPimRouteMapPolMinDependencyWithFvTenant = testConfigFvTenantMin + ` resource "aci_pim_route_map_policy" "test" { parent_dn = aci_tenant.test.id diff --git a/internal/provider/resource_aci_relation_to_consumed_contract.go b/internal/provider/resource_aci_relation_to_consumed_contract.go index b1a187caa..477300086 100644 --- a/internal/provider/resource_aci_relation_to_consumed_contract.go +++ b/internal/provider/resource_aci_relation_to_consumed_contract.go @@ -68,6 +68,29 @@ type FvRsConsIdentifier struct { TnVzBrCPName types.String } +func (r *FvRsConsResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvRsConsResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.TnVzBrCPName.IsUnknown() { + var createCheckData *FvRsConsResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvRsConsId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvRsCons", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvRsConsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_relation_to_consumed_contract") resp.TypeName = req.ProviderTypeName + "_relation_to_consumed_contract" @@ -210,6 +233,13 @@ func (r *FvRsConsResource) Create(ctx context.Context, req resource.CreateReques resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvRsConsId(ctx, stateData) getAndSetFvRsConsAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvRsCons object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvRsConsResourceModel @@ -230,7 +260,7 @@ func (r *FvRsConsResource) Create(ctx context.Context, req resource.CreateReques var tagTagPlan, tagTagState []TagTagFvRsConsResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsConsCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsConsCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -295,7 +325,7 @@ func (r *FvRsConsResource) Update(ctx context.Context, req resource.UpdateReques var tagTagPlan, tagTagState []TagTagFvRsConsResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsConsCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsConsCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -533,9 +563,13 @@ func getFvRsConsTagTagChildPayloads(ctx context.Context, diags *diag.Diagnostics return childPayloads } -func getFvRsConsCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvRsConsResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsConsResourceModel, tagTagPlan, tagTagState []TagTagFvRsConsResourceModel) *container.Container { +func getFvRsConsCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvRsConsResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsConsResourceModel, tagTagPlan, tagTagState []TagTagFvRsConsResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvRsConsTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_relation_to_consumed_contract_test.go b/internal/provider/resource_aci_relation_to_consumed_contract_test.go index dbe03dfa9..3cdc34eb8 100644 --- a/internal/provider/resource_aci_relation_to_consumed_contract_test.go +++ b/internal/provider/resource_aci_relation_to_consumed_contract_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,60 @@ import ( func TestAccResourceFvRsConsWithFvAEPg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsMinDependencyWithFvAEPgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -146,6 +201,60 @@ func TestAccResourceFvRsConsWithFvAEPg(t *testing.T) { } func TestAccResourceFvRsConsWithFvESg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsMinDependencyWithFvESgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -279,6 +388,18 @@ func TestAccResourceFvRsConsWithFvESg(t *testing.T) { }) } +const testConfigFvRsConsMinDependencyWithFvAEPgAllowExisting = testConfigFvAEPgMinDependencyWithFvAp + ` +resource "aci_relation_to_consumed_contract" "test" { + parent_dn = aci_application_epg.test.id + contract_name = "test_tn_vz_br_cp_name" +} +resource "aci_relation_to_consumed_contract" "test_2" { + parent_dn = aci_application_epg.test.id + contract_name = "test_tn_vz_br_cp_name" + depends_on = [aci_relation_to_consumed_contract.test] +} +` + const testConfigFvRsConsMinDependencyWithFvAEPg = testConfigFvAEPgMinDependencyWithFvAp + ` resource "aci_relation_to_consumed_contract" "test" { parent_dn = aci_application_epg.test.id @@ -365,6 +486,18 @@ resource "aci_relation_to_consumed_contract" "test" { } ` +const testConfigFvRsConsMinDependencyWithFvESgAllowExisting = testConfigFvESgMinDependencyWithFvAp + ` +resource "aci_relation_to_consumed_contract" "test" { + parent_dn = aci_endpoint_security_group.test.id + contract_name = "test_tn_vz_br_cp_name" +} +resource "aci_relation_to_consumed_contract" "test_2" { + parent_dn = aci_endpoint_security_group.test.id + contract_name = "test_tn_vz_br_cp_name" + depends_on = [aci_relation_to_consumed_contract.test] +} +` + const testConfigFvRsConsMinDependencyWithFvESg = testConfigFvESgMinDependencyWithFvAp + ` resource "aci_relation_to_consumed_contract" "test" { parent_dn = aci_endpoint_security_group.test.id diff --git a/internal/provider/resource_aci_relation_to_consumed_out_of_band_contract.go b/internal/provider/resource_aci_relation_to_consumed_out_of_band_contract.go index 66db21ecd..916065adf 100644 --- a/internal/provider/resource_aci_relation_to_consumed_out_of_band_contract.go +++ b/internal/provider/resource_aci_relation_to_consumed_out_of_band_contract.go @@ -68,6 +68,29 @@ type MgmtRsOoBConsIdentifier struct { TnVzOOBBrCPName types.String } +func (r *MgmtRsOoBConsResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *MgmtRsOoBConsResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.TnVzOOBBrCPName.IsUnknown() { + var createCheckData *MgmtRsOoBConsResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setMgmtRsOoBConsId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "mgmtRsOoBCons", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *MgmtRsOoBConsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_relation_to_consumed_out_of_band_contract") resp.TypeName = req.ProviderTypeName + "_relation_to_consumed_out_of_band_contract" @@ -210,6 +233,13 @@ func (r *MgmtRsOoBConsResource) Create(ctx context.Context, req resource.CreateR resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setMgmtRsOoBConsId(ctx, stateData) getAndSetMgmtRsOoBConsAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The mgmtRsOoBCons object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *MgmtRsOoBConsResourceModel @@ -230,7 +260,7 @@ func (r *MgmtRsOoBConsResource) Create(ctx context.Context, req resource.CreateR var tagTagPlan, tagTagState []TagTagMgmtRsOoBConsResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getMgmtRsOoBConsCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getMgmtRsOoBConsCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -295,7 +325,7 @@ func (r *MgmtRsOoBConsResource) Update(ctx context.Context, req resource.UpdateR var tagTagPlan, tagTagState []TagTagMgmtRsOoBConsResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getMgmtRsOoBConsCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getMgmtRsOoBConsCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -533,9 +563,13 @@ func getMgmtRsOoBConsTagTagChildPayloads(ctx context.Context, diags *diag.Diagno return childPayloads } -func getMgmtRsOoBConsCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *MgmtRsOoBConsResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationMgmtRsOoBConsResourceModel, tagTagPlan, tagTagState []TagTagMgmtRsOoBConsResourceModel) *container.Container { +func getMgmtRsOoBConsCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *MgmtRsOoBConsResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationMgmtRsOoBConsResourceModel, tagTagPlan, tagTagState []TagTagMgmtRsOoBConsResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getMgmtRsOoBConsTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_relation_to_consumed_out_of_band_contract_test.go b/internal/provider/resource_aci_relation_to_consumed_out_of_band_contract_test.go index 14bf7e258..411ac7cec 100644 --- a/internal/provider/resource_aci_relation_to_consumed_out_of_band_contract_test.go +++ b/internal/provider/resource_aci_relation_to_consumed_out_of_band_contract_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,60 @@ import ( func TestAccResourceMgmtRsOoBConsWithMgmtInstP(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMgmtRsOoBConsMinDependencyWithMgmtInstPAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test", "out_of_band_contract_name", "test_tn_vz_oob_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test_2", "out_of_band_contract_name", "test_tn_vz_oob_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMgmtRsOoBConsMinDependencyWithMgmtInstPAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigMgmtRsOoBConsMinDependencyWithMgmtInstPAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test", "out_of_band_contract_name", "test_tn_vz_oob_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test_2", "out_of_band_contract_name", "test_tn_vz_oob_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_consumed_out_of_band_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -145,6 +200,18 @@ func TestAccResourceMgmtRsOoBConsWithMgmtInstP(t *testing.T) { }) } +const testConfigMgmtRsOoBConsMinDependencyWithMgmtInstPAllowExisting = testConfigMgmtInstPMin + ` +resource "aci_relation_to_consumed_out_of_band_contract" "test" { + parent_dn = aci_external_management_network_instance_profile.test.id + out_of_band_contract_name = "test_tn_vz_oob_br_cp_name" +} +resource "aci_relation_to_consumed_out_of_band_contract" "test_2" { + parent_dn = aci_external_management_network_instance_profile.test.id + out_of_band_contract_name = "test_tn_vz_oob_br_cp_name" + depends_on = [aci_relation_to_consumed_out_of_band_contract.test] +} +` + const testConfigMgmtRsOoBConsMinDependencyWithMgmtInstP = testConfigMgmtInstPMin + ` resource "aci_relation_to_consumed_out_of_band_contract" "test" { parent_dn = aci_external_management_network_instance_profile.test.id diff --git a/internal/provider/resource_aci_relation_to_contract_master.go b/internal/provider/resource_aci_relation_to_contract_master.go index 811bd36fe..e132e9ecb 100644 --- a/internal/provider/resource_aci_relation_to_contract_master.go +++ b/internal/provider/resource_aci_relation_to_contract_master.go @@ -65,6 +65,29 @@ type FvRsSecInheritedIdentifier struct { TDn types.String } +func (r *FvRsSecInheritedResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvRsSecInheritedResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.TDn.IsUnknown() { + var createCheckData *FvRsSecInheritedResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvRsSecInheritedId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvRsSecInherited", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvRsSecInheritedResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_relation_to_contract_master") resp.TypeName = req.ProviderTypeName + "_relation_to_contract_master" @@ -196,6 +219,13 @@ func (r *FvRsSecInheritedResource) Create(ctx context.Context, req resource.Crea resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvRsSecInheritedId(ctx, stateData) getAndSetFvRsSecInheritedAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvRsSecInherited object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvRsSecInheritedResourceModel @@ -216,7 +246,7 @@ func (r *FvRsSecInheritedResource) Create(ctx context.Context, req resource.Crea var tagTagPlan, tagTagState []TagTagFvRsSecInheritedResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsSecInheritedCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsSecInheritedCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -281,7 +311,7 @@ func (r *FvRsSecInheritedResource) Update(ctx context.Context, req resource.Upda var tagTagPlan, tagTagState []TagTagFvRsSecInheritedResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsSecInheritedCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsSecInheritedCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -516,9 +546,13 @@ func getFvRsSecInheritedTagTagChildPayloads(ctx context.Context, diags *diag.Dia return childPayloads } -func getFvRsSecInheritedCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvRsSecInheritedResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsSecInheritedResourceModel, tagTagPlan, tagTagState []TagTagFvRsSecInheritedResourceModel) *container.Container { +func getFvRsSecInheritedCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvRsSecInheritedResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsSecInheritedResourceModel, tagTagPlan, tagTagState []TagTagFvRsSecInheritedResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvRsSecInheritedTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_relation_to_contract_master_test.go b/internal/provider/resource_aci_relation_to_contract_master_test.go index ef51debad..f379b5888 100644 --- a/internal/provider/resource_aci_relation_to_contract_master_test.go +++ b/internal/provider/resource_aci_relation_to_contract_master_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,56 @@ import ( func TestAccResourceFvRsSecInheritedWithFvAEPg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsSecInheritedMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test", "target_dn", "uni/tn-test_tenant/ap-test_ap/epg-epg_2"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test_2", "target_dn", "uni/tn-test_tenant/ap-test_ap/epg-epg_2"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsSecInheritedMinDependencyWithFvAEPgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsSecInheritedMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test", "target_dn", "uni/tn-test_tenant/ap-test_ap/epg-epg_2"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test_2", "target_dn", "uni/tn-test_tenant/ap-test_ap/epg-epg_2"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -140,6 +191,56 @@ func TestAccResourceFvRsSecInheritedWithFvAEPg(t *testing.T) { } func TestAccResourceFvRsSecInheritedWithFvESg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsSecInheritedMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test", "target_dn", "uni/tn-test_tenant/ap-test_ap/esg-esg_0"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test_2", "target_dn", "uni/tn-test_tenant/ap-test_ap/esg-esg_0"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsSecInheritedMinDependencyWithFvESgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsSecInheritedMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test", "target_dn", "uni/tn-test_tenant/ap-test_ap/esg-esg_0"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test_2", "target_dn", "uni/tn-test_tenant/ap-test_ap/esg-esg_0"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_contract_master.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -286,6 +387,18 @@ resource "aci_application_epg" "test_3" { } ` +const testConfigFvRsSecInheritedMinDependencyWithFvAEPgAllowExisting = testDependencyConfigFvRsSecInherited + testConfigFvAEPgMinDependencyWithFvAp + ` +resource "aci_relation_to_contract_master" "test" { + parent_dn = aci_application_epg.test.id + target_dn = aci_application_epg.test_2.id +} +resource "aci_relation_to_contract_master" "test_2" { + parent_dn = aci_application_epg.test.id + target_dn = aci_application_epg.test_2.id + depends_on = [aci_relation_to_contract_master.test] +} +` + const testConfigFvRsSecInheritedMinDependencyWithFvAEPg = testDependencyConfigFvRsSecInherited + testConfigFvAEPgMinDependencyWithFvAp + ` resource "aci_relation_to_contract_master" "test" { parent_dn = aci_application_epg.test.id @@ -370,6 +483,18 @@ resource "aci_relation_to_contract_master" "test" { } ` +const testConfigFvRsSecInheritedMinDependencyWithFvESgAllowExisting = testDependencyConfigFvRsSecInherited + testConfigFvESgMinDependencyWithFvAp + ` +resource "aci_relation_to_contract_master" "test" { + parent_dn = aci_endpoint_security_group.test.id + target_dn = aci_endpoint_security_group.test_0.id +} +resource "aci_relation_to_contract_master" "test_2" { + parent_dn = aci_endpoint_security_group.test.id + target_dn = aci_endpoint_security_group.test_0.id + depends_on = [aci_relation_to_contract_master.test] +} +` + const testConfigFvRsSecInheritedMinDependencyWithFvESg = testDependencyConfigFvRsSecInherited + testConfigFvESgMinDependencyWithFvAp + ` resource "aci_relation_to_contract_master" "test" { parent_dn = aci_endpoint_security_group.test.id diff --git a/internal/provider/resource_aci_relation_to_imported_contract.go b/internal/provider/resource_aci_relation_to_imported_contract.go index c05dd5e3d..0678a09e6 100644 --- a/internal/provider/resource_aci_relation_to_imported_contract.go +++ b/internal/provider/resource_aci_relation_to_imported_contract.go @@ -68,6 +68,29 @@ type FvRsConsIfIdentifier struct { TnVzCPIfName types.String } +func (r *FvRsConsIfResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvRsConsIfResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.TnVzCPIfName.IsUnknown() { + var createCheckData *FvRsConsIfResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvRsConsIfId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvRsConsIf", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvRsConsIfResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_relation_to_imported_contract") resp.TypeName = req.ProviderTypeName + "_relation_to_imported_contract" @@ -210,6 +233,13 @@ func (r *FvRsConsIfResource) Create(ctx context.Context, req resource.CreateRequ resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvRsConsIfId(ctx, stateData) getAndSetFvRsConsIfAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvRsConsIf object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvRsConsIfResourceModel @@ -230,7 +260,7 @@ func (r *FvRsConsIfResource) Create(ctx context.Context, req resource.CreateRequ var tagTagPlan, tagTagState []TagTagFvRsConsIfResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsConsIfCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsConsIfCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -295,7 +325,7 @@ func (r *FvRsConsIfResource) Update(ctx context.Context, req resource.UpdateRequ var tagTagPlan, tagTagState []TagTagFvRsConsIfResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsConsIfCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsConsIfCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -533,9 +563,13 @@ func getFvRsConsIfTagTagChildPayloads(ctx context.Context, diags *diag.Diagnosti return childPayloads } -func getFvRsConsIfCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvRsConsIfResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsConsIfResourceModel, tagTagPlan, tagTagState []TagTagFvRsConsIfResourceModel) *container.Container { +func getFvRsConsIfCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvRsConsIfResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsConsIfResourceModel, tagTagPlan, tagTagState []TagTagFvRsConsIfResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvRsConsIfTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_relation_to_imported_contract_test.go b/internal/provider/resource_aci_relation_to_imported_contract_test.go index 2958d7b10..bc4a10651 100644 --- a/internal/provider/resource_aci_relation_to_imported_contract_test.go +++ b/internal/provider/resource_aci_relation_to_imported_contract_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,60 @@ import ( func TestAccResourceFvRsConsIfWithFvAEPg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsIfMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "imported_contract_name", "test_tn_vz_cp_if_name"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "imported_contract_name", "test_tn_vz_cp_if_name"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsIfMinDependencyWithFvAEPgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsIfMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "imported_contract_name", "test_tn_vz_cp_if_name"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "imported_contract_name", "test_tn_vz_cp_if_name"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -146,6 +201,60 @@ func TestAccResourceFvRsConsIfWithFvAEPg(t *testing.T) { } func TestAccResourceFvRsConsIfWithFvESg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsIfMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "imported_contract_name", "test_tn_vz_cp_if_name"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "imported_contract_name", "test_tn_vz_cp_if_name"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsIfMinDependencyWithFvESgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsConsIfMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "imported_contract_name", "test_tn_vz_cp_if_name"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "imported_contract_name", "test_tn_vz_cp_if_name"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_imported_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -279,6 +388,18 @@ func TestAccResourceFvRsConsIfWithFvESg(t *testing.T) { }) } +const testConfigFvRsConsIfMinDependencyWithFvAEPgAllowExisting = testConfigFvAEPgMinDependencyWithFvAp + ` +resource "aci_relation_to_imported_contract" "test" { + parent_dn = aci_application_epg.test.id + imported_contract_name = "test_tn_vz_cp_if_name" +} +resource "aci_relation_to_imported_contract" "test_2" { + parent_dn = aci_application_epg.test.id + imported_contract_name = "test_tn_vz_cp_if_name" + depends_on = [aci_relation_to_imported_contract.test] +} +` + const testConfigFvRsConsIfMinDependencyWithFvAEPg = testConfigFvAEPgMinDependencyWithFvAp + ` resource "aci_relation_to_imported_contract" "test" { parent_dn = aci_application_epg.test.id @@ -365,6 +486,18 @@ resource "aci_relation_to_imported_contract" "test" { } ` +const testConfigFvRsConsIfMinDependencyWithFvESgAllowExisting = testConfigFvESgMinDependencyWithFvAp + ` +resource "aci_relation_to_imported_contract" "test" { + parent_dn = aci_endpoint_security_group.test.id + imported_contract_name = "test_tn_vz_cp_if_name" +} +resource "aci_relation_to_imported_contract" "test_2" { + parent_dn = aci_endpoint_security_group.test.id + imported_contract_name = "test_tn_vz_cp_if_name" + depends_on = [aci_relation_to_imported_contract.test] +} +` + const testConfigFvRsConsIfMinDependencyWithFvESg = testConfigFvESgMinDependencyWithFvAp + ` resource "aci_relation_to_imported_contract" "test" { parent_dn = aci_endpoint_security_group.test.id diff --git a/internal/provider/resource_aci_relation_to_intra_epg_contract.go b/internal/provider/resource_aci_relation_to_intra_epg_contract.go index 91e7267e6..a3b8908f4 100644 --- a/internal/provider/resource_aci_relation_to_intra_epg_contract.go +++ b/internal/provider/resource_aci_relation_to_intra_epg_contract.go @@ -65,6 +65,29 @@ type FvRsIntraEpgIdentifier struct { TnVzBrCPName types.String } +func (r *FvRsIntraEpgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvRsIntraEpgResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.TnVzBrCPName.IsUnknown() { + var createCheckData *FvRsIntraEpgResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvRsIntraEpgId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvRsIntraEpg", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvRsIntraEpgResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_relation_to_intra_epg_contract") resp.TypeName = req.ProviderTypeName + "_relation_to_intra_epg_contract" @@ -196,6 +219,13 @@ func (r *FvRsIntraEpgResource) Create(ctx context.Context, req resource.CreateRe resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvRsIntraEpgId(ctx, stateData) getAndSetFvRsIntraEpgAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvRsIntraEpg object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvRsIntraEpgResourceModel @@ -216,7 +246,7 @@ func (r *FvRsIntraEpgResource) Create(ctx context.Context, req resource.CreateRe var tagTagPlan, tagTagState []TagTagFvRsIntraEpgResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsIntraEpgCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsIntraEpgCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -281,7 +311,7 @@ func (r *FvRsIntraEpgResource) Update(ctx context.Context, req resource.UpdateRe var tagTagPlan, tagTagState []TagTagFvRsIntraEpgResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsIntraEpgCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsIntraEpgCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -516,9 +546,13 @@ func getFvRsIntraEpgTagTagChildPayloads(ctx context.Context, diags *diag.Diagnos return childPayloads } -func getFvRsIntraEpgCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvRsIntraEpgResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsIntraEpgResourceModel, tagTagPlan, tagTagState []TagTagFvRsIntraEpgResourceModel) *container.Container { +func getFvRsIntraEpgCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvRsIntraEpgResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsIntraEpgResourceModel, tagTagPlan, tagTagState []TagTagFvRsIntraEpgResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvRsIntraEpgTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_relation_to_intra_epg_contract_test.go b/internal/provider/resource_aci_relation_to_intra_epg_contract_test.go index bb3e1f3e3..5eb0c76b1 100644 --- a/internal/provider/resource_aci_relation_to_intra_epg_contract_test.go +++ b/internal/provider/resource_aci_relation_to_intra_epg_contract_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,56 @@ import ( func TestAccResourceFvRsIntraEpgWithFvAEPg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsIntraEpgMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsIntraEpgMinDependencyWithFvAEPgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsIntraEpgMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -140,6 +191,56 @@ func TestAccResourceFvRsIntraEpgWithFvAEPg(t *testing.T) { } func TestAccResourceFvRsIntraEpgWithFvESg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsIntraEpgMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsIntraEpgMinDependencyWithFvESgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsIntraEpgMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_intra_epg_contract.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -267,6 +368,18 @@ func TestAccResourceFvRsIntraEpgWithFvESg(t *testing.T) { }) } +const testConfigFvRsIntraEpgMinDependencyWithFvAEPgAllowExisting = testConfigFvAEPgMinDependencyWithFvAp + ` +resource "aci_relation_to_intra_epg_contract" "test" { + parent_dn = aci_application_epg.test.id + contract_name = "test_tn_vz_br_cp_name" +} +resource "aci_relation_to_intra_epg_contract" "test_2" { + parent_dn = aci_application_epg.test.id + contract_name = "test_tn_vz_br_cp_name" + depends_on = [aci_relation_to_intra_epg_contract.test] +} +` + const testConfigFvRsIntraEpgMinDependencyWithFvAEPg = testConfigFvAEPgMinDependencyWithFvAp + ` resource "aci_relation_to_intra_epg_contract" "test" { parent_dn = aci_application_epg.test.id @@ -351,6 +464,18 @@ resource "aci_relation_to_intra_epg_contract" "test" { } ` +const testConfigFvRsIntraEpgMinDependencyWithFvESgAllowExisting = testConfigFvESgMinDependencyWithFvAp + ` +resource "aci_relation_to_intra_epg_contract" "test" { + parent_dn = aci_endpoint_security_group.test.id + contract_name = "test_tn_vz_br_cp_name" +} +resource "aci_relation_to_intra_epg_contract" "test_2" { + parent_dn = aci_endpoint_security_group.test.id + contract_name = "test_tn_vz_br_cp_name" + depends_on = [aci_relation_to_intra_epg_contract.test] +} +` + const testConfigFvRsIntraEpgMinDependencyWithFvESg = testConfigFvESgMinDependencyWithFvAp + ` resource "aci_relation_to_intra_epg_contract" "test" { parent_dn = aci_endpoint_security_group.test.id diff --git a/internal/provider/resource_aci_relation_to_netflow_exporter.go b/internal/provider/resource_aci_relation_to_netflow_exporter.go index 858087501..4cf6e8cd2 100644 --- a/internal/provider/resource_aci_relation_to_netflow_exporter.go +++ b/internal/provider/resource_aci_relation_to_netflow_exporter.go @@ -65,6 +65,29 @@ type NetflowRsMonitorToExporterIdentifier struct { TnNetflowExporterPolName types.String } +func (r *NetflowRsMonitorToExporterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *NetflowRsMonitorToExporterResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.TnNetflowExporterPolName.IsUnknown() { + var createCheckData *NetflowRsMonitorToExporterResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setNetflowRsMonitorToExporterId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "netflowRsMonitorToExporter", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *NetflowRsMonitorToExporterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_relation_to_netflow_exporter") resp.TypeName = req.ProviderTypeName + "_relation_to_netflow_exporter" @@ -196,6 +219,13 @@ func (r *NetflowRsMonitorToExporterResource) Create(ctx context.Context, req res resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setNetflowRsMonitorToExporterId(ctx, stateData) getAndSetNetflowRsMonitorToExporterAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The netflowRsMonitorToExporter object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *NetflowRsMonitorToExporterResourceModel @@ -216,7 +246,7 @@ func (r *NetflowRsMonitorToExporterResource) Create(ctx context.Context, req res var tagTagPlan, tagTagState []TagTagNetflowRsMonitorToExporterResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getNetflowRsMonitorToExporterCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getNetflowRsMonitorToExporterCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -281,7 +311,7 @@ func (r *NetflowRsMonitorToExporterResource) Update(ctx context.Context, req res var tagTagPlan, tagTagState []TagTagNetflowRsMonitorToExporterResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getNetflowRsMonitorToExporterCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getNetflowRsMonitorToExporterCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -516,9 +546,13 @@ func getNetflowRsMonitorToExporterTagTagChildPayloads(ctx context.Context, diags return childPayloads } -func getNetflowRsMonitorToExporterCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *NetflowRsMonitorToExporterResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowRsMonitorToExporterResourceModel, tagTagPlan, tagTagState []TagTagNetflowRsMonitorToExporterResourceModel) *container.Container { +func getNetflowRsMonitorToExporterCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *NetflowRsMonitorToExporterResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowRsMonitorToExporterResourceModel, tagTagPlan, tagTagState []TagTagNetflowRsMonitorToExporterResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getNetflowRsMonitorToExporterTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_relation_to_netflow_exporter_test.go b/internal/provider/resource_aci_relation_to_netflow_exporter_test.go index d913e16f1..aaeaeb3aa 100644 --- a/internal/provider/resource_aci_relation_to_netflow_exporter_test.go +++ b/internal/provider/resource_aci_relation_to_netflow_exporter_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,56 @@ import ( func TestAccResourceNetflowRsMonitorToExporterWithNetflowMonitorPol(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigNetflowRsMonitorToExporterMinDependencyWithNetflowMonitorPolAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_netflow_exporter.test", "netflow_exporter_policy_name", "test_tn_netflow_exporter_pol_name"), + resource.TestCheckResourceAttr("aci_relation_to_netflow_exporter.test_2", "netflow_exporter_policy_name", "test_tn_netflow_exporter_pol_name"), + resource.TestCheckResourceAttr("aci_relation_to_netflow_exporter.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_netflow_exporter.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigNetflowRsMonitorToExporterMinDependencyWithNetflowMonitorPolAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigNetflowRsMonitorToExporterMinDependencyWithNetflowMonitorPolAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_netflow_exporter.test", "netflow_exporter_policy_name", "test_tn_netflow_exporter_pol_name"), + resource.TestCheckResourceAttr("aci_relation_to_netflow_exporter.test_2", "netflow_exporter_policy_name", "test_tn_netflow_exporter_pol_name"), + resource.TestCheckResourceAttr("aci_relation_to_netflow_exporter.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_netflow_exporter.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -139,6 +190,18 @@ func TestAccResourceNetflowRsMonitorToExporterWithNetflowMonitorPol(t *testing.T }) } +const testConfigNetflowRsMonitorToExporterMinDependencyWithNetflowMonitorPolAllowExisting = testConfigNetflowMonitorPolMinDependencyWithFvTenant + ` +resource "aci_relation_to_netflow_exporter" "test" { + parent_dn = aci_netflow_monitor_policy.test.id + netflow_exporter_policy_name = "test_tn_netflow_exporter_pol_name" +} +resource "aci_relation_to_netflow_exporter" "test_2" { + parent_dn = aci_netflow_monitor_policy.test.id + netflow_exporter_policy_name = "test_tn_netflow_exporter_pol_name" + depends_on = [aci_relation_to_netflow_exporter.test] +} +` + const testConfigNetflowRsMonitorToExporterMinDependencyWithNetflowMonitorPol = testConfigNetflowMonitorPolMinDependencyWithFvTenant + ` resource "aci_relation_to_netflow_exporter" "test" { parent_dn = aci_netflow_monitor_policy.test.id diff --git a/internal/provider/resource_aci_relation_to_provided_contract.go b/internal/provider/resource_aci_relation_to_provided_contract.go index 4b4fb3f16..a15589ca8 100644 --- a/internal/provider/resource_aci_relation_to_provided_contract.go +++ b/internal/provider/resource_aci_relation_to_provided_contract.go @@ -69,6 +69,29 @@ type FvRsProvIdentifier struct { TnVzBrCPName types.String } +func (r *FvRsProvResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvRsProvResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.TnVzBrCPName.IsUnknown() { + var createCheckData *FvRsProvResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvRsProvId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvRsProv", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvRsProvResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_relation_to_provided_contract") resp.TypeName = req.ProviderTypeName + "_relation_to_provided_contract" @@ -222,6 +245,13 @@ func (r *FvRsProvResource) Create(ctx context.Context, req resource.CreateReques resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvRsProvId(ctx, stateData) getAndSetFvRsProvAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvRsProv object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvRsProvResourceModel @@ -242,7 +272,7 @@ func (r *FvRsProvResource) Create(ctx context.Context, req resource.CreateReques var tagTagPlan, tagTagState []TagTagFvRsProvResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsProvCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsProvCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -307,7 +337,7 @@ func (r *FvRsProvResource) Update(ctx context.Context, req resource.UpdateReques var tagTagPlan, tagTagState []TagTagFvRsProvResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsProvCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsProvCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -548,9 +578,13 @@ func getFvRsProvTagTagChildPayloads(ctx context.Context, diags *diag.Diagnostics return childPayloads } -func getFvRsProvCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvRsProvResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsProvResourceModel, tagTagPlan, tagTagState []TagTagFvRsProvResourceModel) *container.Container { +func getFvRsProvCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvRsProvResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsProvResourceModel, tagTagPlan, tagTagState []TagTagFvRsProvResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvRsProvTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_relation_to_provided_contract_test.go b/internal/provider/resource_aci_relation_to_provided_contract_test.go index fee30eb2c..30fd9c429 100644 --- a/internal/provider/resource_aci_relation_to_provided_contract_test.go +++ b/internal/provider/resource_aci_relation_to_provided_contract_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,64 @@ import ( func TestAccResourceFvRsProvWithFvAEPg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsProvMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsProvMinDependencyWithFvAEPgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsProvMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -152,6 +211,64 @@ func TestAccResourceFvRsProvWithFvAEPg(t *testing.T) { } func TestAccResourceFvRsProvWithFvESg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsProvMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsProvMinDependencyWithFvESgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsProvMinDependencyWithFvESgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "contract_name", "test_tn_vz_br_cp_name"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "match_criteria", "AtleastOne"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test", "priority", "unspecified"), + resource.TestCheckResourceAttr("aci_relation_to_provided_contract.test_2", "priority", "unspecified"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -291,6 +408,18 @@ func TestAccResourceFvRsProvWithFvESg(t *testing.T) { }) } +const testConfigFvRsProvMinDependencyWithFvAEPgAllowExisting = testConfigFvAEPgMinDependencyWithFvAp + ` +resource "aci_relation_to_provided_contract" "test" { + parent_dn = aci_application_epg.test.id + contract_name = "test_tn_vz_br_cp_name" +} +resource "aci_relation_to_provided_contract" "test_2" { + parent_dn = aci_application_epg.test.id + contract_name = "test_tn_vz_br_cp_name" + depends_on = [aci_relation_to_provided_contract.test] +} +` + const testConfigFvRsProvMinDependencyWithFvAEPg = testConfigFvAEPgMinDependencyWithFvAp + ` resource "aci_relation_to_provided_contract" "test" { parent_dn = aci_application_epg.test.id @@ -379,6 +508,18 @@ resource "aci_relation_to_provided_contract" "test" { } ` +const testConfigFvRsProvMinDependencyWithFvESgAllowExisting = testConfigFvESgMinDependencyWithFvAp + ` +resource "aci_relation_to_provided_contract" "test" { + parent_dn = aci_endpoint_security_group.test.id + contract_name = "test_tn_vz_br_cp_name" +} +resource "aci_relation_to_provided_contract" "test_2" { + parent_dn = aci_endpoint_security_group.test.id + contract_name = "test_tn_vz_br_cp_name" + depends_on = [aci_relation_to_provided_contract.test] +} +` + const testConfigFvRsProvMinDependencyWithFvESg = testConfigFvESgMinDependencyWithFvAp + ` resource "aci_relation_to_provided_contract" "test" { parent_dn = aci_endpoint_security_group.test.id diff --git a/internal/provider/resource_aci_relation_to_taboo_contract.go b/internal/provider/resource_aci_relation_to_taboo_contract.go index 0e48b2b76..c4433a5d0 100644 --- a/internal/provider/resource_aci_relation_to_taboo_contract.go +++ b/internal/provider/resource_aci_relation_to_taboo_contract.go @@ -65,6 +65,29 @@ type FvRsProtByIdentifier struct { TnVzTabooName types.String } +func (r *FvRsProtByResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvRsProtByResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.TnVzTabooName.IsUnknown() { + var createCheckData *FvRsProtByResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvRsProtById(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvRsProtBy", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvRsProtByResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_relation_to_taboo_contract") resp.TypeName = req.ProviderTypeName + "_relation_to_taboo_contract" @@ -196,6 +219,13 @@ func (r *FvRsProtByResource) Create(ctx context.Context, req resource.CreateRequ resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvRsProtById(ctx, stateData) getAndSetFvRsProtByAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvRsProtBy object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvRsProtByResourceModel @@ -216,7 +246,7 @@ func (r *FvRsProtByResource) Create(ctx context.Context, req resource.CreateRequ var tagTagPlan, tagTagState []TagTagFvRsProtByResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsProtByCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsProtByCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -281,7 +311,7 @@ func (r *FvRsProtByResource) Update(ctx context.Context, req resource.UpdateRequ var tagTagPlan, tagTagState []TagTagFvRsProtByResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvRsProtByCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvRsProtByCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -516,9 +546,13 @@ func getFvRsProtByTagTagChildPayloads(ctx context.Context, diags *diag.Diagnosti return childPayloads } -func getFvRsProtByCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvRsProtByResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsProtByResourceModel, tagTagPlan, tagTagState []TagTagFvRsProtByResourceModel) *container.Container { +func getFvRsProtByCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvRsProtByResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvRsProtByResourceModel, tagTagPlan, tagTagState []TagTagFvRsProtByResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvRsProtByTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_relation_to_taboo_contract_test.go b/internal/provider/resource_aci_relation_to_taboo_contract_test.go index ef2270671..1d8e491e8 100644 --- a/internal/provider/resource_aci_relation_to_taboo_contract_test.go +++ b/internal/provider/resource_aci_relation_to_taboo_contract_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,56 @@ import ( func TestAccResourceFvRsProtByWithFvAEPg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsProtByMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_taboo_contract.test", "taboo_contract_name", "test_tn_vz_taboo_name"), + resource.TestCheckResourceAttr("aci_relation_to_taboo_contract.test_2", "taboo_contract_name", "test_tn_vz_taboo_name"), + resource.TestCheckResourceAttr("aci_relation_to_taboo_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_taboo_contract.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsProtByMinDependencyWithFvAEPgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvRsProtByMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_taboo_contract.test", "taboo_contract_name", "test_tn_vz_taboo_name"), + resource.TestCheckResourceAttr("aci_relation_to_taboo_contract.test_2", "taboo_contract_name", "test_tn_vz_taboo_name"), + resource.TestCheckResourceAttr("aci_relation_to_taboo_contract.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_taboo_contract.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -139,6 +190,18 @@ func TestAccResourceFvRsProtByWithFvAEPg(t *testing.T) { }) } +const testConfigFvRsProtByMinDependencyWithFvAEPgAllowExisting = testConfigFvAEPgMinDependencyWithFvAp + ` +resource "aci_relation_to_taboo_contract" "test" { + parent_dn = aci_application_epg.test.id + taboo_contract_name = "test_tn_vz_taboo_name" +} +resource "aci_relation_to_taboo_contract" "test_2" { + parent_dn = aci_application_epg.test.id + taboo_contract_name = "test_tn_vz_taboo_name" + depends_on = [aci_relation_to_taboo_contract.test] +} +` + const testConfigFvRsProtByMinDependencyWithFvAEPg = testConfigFvAEPgMinDependencyWithFvAp + ` resource "aci_relation_to_taboo_contract" "test" { parent_dn = aci_application_epg.test.id diff --git a/internal/provider/resource_aci_relation_to_vrf_fallback_route_group.go b/internal/provider/resource_aci_relation_to_vrf_fallback_route_group.go index 9f8e530f5..547144f9a 100644 --- a/internal/provider/resource_aci_relation_to_vrf_fallback_route_group.go +++ b/internal/provider/resource_aci_relation_to_vrf_fallback_route_group.go @@ -65,6 +65,29 @@ type L3extRsOutToFBRGroupIdentifier struct { TDn types.String } +func (r *L3extRsOutToFBRGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *L3extRsOutToFBRGroupResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.TDn.IsUnknown() { + var createCheckData *L3extRsOutToFBRGroupResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setL3extRsOutToFBRGroupId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "l3extRsOutToFBRGroup", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *L3extRsOutToFBRGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_relation_to_vrf_fallback_route_group") resp.TypeName = req.ProviderTypeName + "_relation_to_vrf_fallback_route_group" @@ -196,6 +219,13 @@ func (r *L3extRsOutToFBRGroupResource) Create(ctx context.Context, req resource. resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setL3extRsOutToFBRGroupId(ctx, stateData) getAndSetL3extRsOutToFBRGroupAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The l3extRsOutToFBRGroup object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *L3extRsOutToFBRGroupResourceModel @@ -216,7 +246,7 @@ func (r *L3extRsOutToFBRGroupResource) Create(ctx context.Context, req resource. var tagTagPlan, tagTagState []TagTagL3extRsOutToFBRGroupResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getL3extRsOutToFBRGroupCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getL3extRsOutToFBRGroupCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -281,7 +311,7 @@ func (r *L3extRsOutToFBRGroupResource) Update(ctx context.Context, req resource. var tagTagPlan, tagTagState []TagTagL3extRsOutToFBRGroupResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getL3extRsOutToFBRGroupCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getL3extRsOutToFBRGroupCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -516,9 +546,13 @@ func getL3extRsOutToFBRGroupTagTagChildPayloads(ctx context.Context, diags *diag return childPayloads } -func getL3extRsOutToFBRGroupCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *L3extRsOutToFBRGroupResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationL3extRsOutToFBRGroupResourceModel, tagTagPlan, tagTagState []TagTagL3extRsOutToFBRGroupResourceModel) *container.Container { +func getL3extRsOutToFBRGroupCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *L3extRsOutToFBRGroupResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationL3extRsOutToFBRGroupResourceModel, tagTagPlan, tagTagState []TagTagL3extRsOutToFBRGroupResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getL3extRsOutToFBRGroupTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_relation_to_vrf_fallback_route_group_test.go b/internal/provider/resource_aci_relation_to_vrf_fallback_route_group_test.go index 8d92aec87..70ee887db 100644 --- a/internal/provider/resource_aci_relation_to_vrf_fallback_route_group_test.go +++ b/internal/provider/resource_aci_relation_to_vrf_fallback_route_group_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,56 @@ import ( func TestAccResourceL3extRsOutToFBRGroupWithL3extOut(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extRsOutToFBRGroupMinDependencyWithL3extOutAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_vrf_fallback_route_group.test", "target_dn", "uni/tn-test_tenant/ctx-test_vrf/fbrg-vrf_fallback_route_group_0"), + resource.TestCheckResourceAttr("aci_relation_to_vrf_fallback_route_group.test_2", "target_dn", "uni/tn-test_tenant/ctx-test_vrf/fbrg-vrf_fallback_route_group_0"), + resource.TestCheckResourceAttr("aci_relation_to_vrf_fallback_route_group.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_vrf_fallback_route_group.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extRsOutToFBRGroupMinDependencyWithL3extOutAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigL3extRsOutToFBRGroupMinDependencyWithL3extOutAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_relation_to_vrf_fallback_route_group.test", "target_dn", "uni/tn-test_tenant/ctx-test_vrf/fbrg-vrf_fallback_route_group_0"), + resource.TestCheckResourceAttr("aci_relation_to_vrf_fallback_route_group.test_2", "target_dn", "uni/tn-test_tenant/ctx-test_vrf/fbrg-vrf_fallback_route_group_0"), + resource.TestCheckResourceAttr("aci_relation_to_vrf_fallback_route_group.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_relation_to_vrf_fallback_route_group.test_2", "annotation", "orchestrator:terraform"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -146,6 +197,18 @@ resource "aci_vrf_fallback_route_group" "test_0" { } ` +const testConfigL3extRsOutToFBRGroupMinDependencyWithL3extOutAllowExisting = testDependencyConfigL3extRsOutToFBRGroup + testConfigL3extOutMinDependencyWithFvTenant + ` +resource "aci_relation_to_vrf_fallback_route_group" "test" { + parent_dn = aci_l3_outside.test.id + target_dn = aci_vrf_fallback_route_group.test_0.id +} +resource "aci_relation_to_vrf_fallback_route_group" "test_2" { + parent_dn = aci_l3_outside.test.id + target_dn = aci_vrf_fallback_route_group.test_0.id + depends_on = [aci_relation_to_vrf_fallback_route_group.test] +} +` + const testConfigL3extRsOutToFBRGroupMinDependencyWithL3extOut = testDependencyConfigL3extRsOutToFBRGroup + testConfigL3extOutMinDependencyWithFvTenant + ` resource "aci_relation_to_vrf_fallback_route_group" "test" { parent_dn = aci_l3_outside.test.id diff --git a/internal/provider/resource_aci_rest_managed.go b/internal/provider/resource_aci_rest_managed.go index f886c7fd7..f34e0a72c 100644 --- a/internal/provider/resource_aci_rest_managed.go +++ b/internal/provider/resource_aci_rest_managed.go @@ -85,8 +85,9 @@ func (r *AciRestManagedResource) Metadata(ctx context.Context, req resource.Meta func (r *AciRestManagedResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { tflog.Debug(ctx, "Start plan modification of resource: aci_rest_managed") if !req.Plan.Raw.IsNull() { - var planData *AciRestManagedResourceModel + var planData, stateData *AciRestManagedResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) // modify the plan when annotation is not a property of class if ContainsString(NoAnnotationClasses, planData.ClassName.ValueString()) { @@ -100,6 +101,15 @@ func (r *AciRestManagedResource) ModifyPlan(ctx context.Context, req resource.Mo } } + if stateData == nil && !globalAllowExistingOnCreate && !planData.Dn.IsUnknown() { + var createCheckData *AciRestManagedResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + CheckDn(ctx, &resp.Diagnostics, r.client, createCheckData.ClassName.ValueString(), createCheckData.Dn.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + resp.Plan.Set(ctx, planData) } tflog.Debug(ctx, "End plan modification of resource: aci_rest_managed") @@ -230,13 +240,13 @@ func (r *AciRestManagedResource) Create(ctx context.Context, req resource.Create return } - data.Id = types.StringValue(data.Dn.ValueString()) + data.Id = data.Dn tflog.Debug(ctx, fmt.Sprintf("Create of resource aci_rest_managed with id '%s'", data.Id.ValueString())) var childPlan, childState []ChildAciRestManagedResourceModel data.Child.ElementsAs(ctx, &childPlan, false) - jsonPayload := getAciRestManagedCreateJsonPayload(ctx, &resp.Diagnostics, data, childPlan, childState) + jsonPayload := getAciRestManagedCreateJsonPayload(ctx, &resp.Diagnostics, true, data, childPlan, childState) if resp.Diagnostics.HasError() { return @@ -320,7 +330,7 @@ func (r *AciRestManagedResource) Update(ctx context.Context, req resource.Update var childPlan, childState []ChildAciRestManagedResourceModel data.Child.ElementsAs(ctx, &childPlan, false) stateData.Child.ElementsAs(ctx, &childState, false) - jsonPayload := getAciRestManagedCreateJsonPayload(ctx, &resp.Diagnostics, data, childPlan, childState) + jsonPayload := getAciRestManagedCreateJsonPayload(ctx, &resp.Diagnostics, false, data, childPlan, childState) if resp.Diagnostics.HasError() { return @@ -709,9 +719,12 @@ func getAciRestManagedChildPayloads(ctx context.Context, diags *diag.Diagnostics return childPayloads } -func getAciRestManagedCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *AciRestManagedResourceModel, childPlan, childState []ChildAciRestManagedResourceModel) *container.Container { +func getAciRestManagedCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *AciRestManagedResourceModel, childPlan, childState []ChildAciRestManagedResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := getAciRestManagedChildPayloads(ctx, diags, data, childPlan, childState) if diags.HasError() { diff --git a/internal/provider/resource_aci_rest_managed_test.go b/internal/provider/resource_aci_rest_managed_test.go index fe172dc8f..9d6c6d425 100644 --- a/internal/provider/resource_aci_rest_managed_test.go +++ b/internal/provider/resource_aci_rest_managed_test.go @@ -544,6 +544,53 @@ func TestAccAciRestManaged_tenantChildren(t *testing.T) { } +func TestAccAciRestManaged_globalAllowExistingOnCreate(t *testing.T) { + name := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccAciRestManagedConfig_globalAllowExisting(name), + ExpectNonEmptyPlan: false, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("aci_rest_managed.fvTenant", "dn", "uni/tn-"+name), + resource.TestCheckResourceAttr("aci_rest_managed.fvTenant_2", "dn", "uni/tn-"+name), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccAciRestManagedConfig_globalAllowExisting(name), + ExpectError: regexp.MustCompile("object already exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccAciRestManagedConfig_globalAllowExisting(name), + ExpectNonEmptyPlan: false, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("aci_rest_managed.fvTenant", "dn", "uni/tn-"+name), + resource.TestCheckResourceAttr("aci_rest_managed.fvTenant_2", "dn", "uni/tn-"+name), + ), + }, + }, + }) +} + func TestAccAciRestManaged_globalAnnotation(t *testing.T) { name := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) @@ -564,7 +611,7 @@ func TestAccAciRestManaged_globalAnnotation(t *testing.T) { }, }) - setGlobalAnnotationEnvVariable(t, "orchestrator:from_env") + setEnvVariable(t, "ACI_ANNOTATION", "orchestrator:from_env") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -629,7 +676,7 @@ func TestAccAciRestManaged_globalAnnotation(t *testing.T) { }, }, }) - setGlobalAnnotationEnvVariable(t, "") + setEnvVariable(t, "ACI_ANNOTATION", "") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -969,6 +1016,26 @@ func testAccAciRestManagedConfig_tagTag(name string) string { `, name) } +func testAccAciRestManagedConfig_globalAllowExisting(name string) string { + return fmt.Sprintf(` + resource "aci_rest_managed" "fvTenant" { + dn = "uni/tn-%[1]s" + class_name = "fvTenant" + content = { + name = "%[1]s" + } + } + resource "aci_rest_managed" "fvTenant_2" { + dn = "uni/tn-%[1]s" + class_name = "fvTenant" + content = { + name = "%[1]s" + } + depends_on = [aci_rest_managed.fvTenant] + } + `, name) +} + func testAccAciRestManagedConfig_globalAnnotation(name string) string { return fmt.Sprintf(` resource "aci_rest_managed" "fvTenant" { diff --git a/internal/provider/resource_aci_tag.go b/internal/provider/resource_aci_tag.go index 839aa221e..9566da4e5 100644 --- a/internal/provider/resource_aci_tag.go +++ b/internal/provider/resource_aci_tag.go @@ -49,6 +49,29 @@ type TagTagIdentifier struct { Key types.String } +func (r *TagTagResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *TagTagResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Key.IsUnknown() { + var createCheckData *TagTagResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setTagTagId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "tagTag", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *TagTagResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_tag") resp.TypeName = req.ProviderTypeName + "_tag" @@ -135,7 +158,7 @@ func (r *TagTagResource) Create(ctx context.Context, req resource.CreateRequest, tflog.Debug(ctx, fmt.Sprintf("Create of resource aci_tag with id '%s'", data.Id.ValueString())) - jsonPayload := getTagTagCreateJsonPayload(ctx, &resp.Diagnostics, data) + jsonPayload := getTagTagCreateJsonPayload(ctx, &resp.Diagnostics, true, data) if resp.Diagnostics.HasError() { return @@ -192,7 +215,7 @@ func (r *TagTagResource) Update(ctx context.Context, req resource.UpdateRequest, tflog.Debug(ctx, fmt.Sprintf("Update of resource aci_tag with id '%s'", data.Id.ValueString())) - jsonPayload := getTagTagCreateJsonPayload(ctx, &resp.Diagnostics, data) + jsonPayload := getTagTagCreateJsonPayload(ctx, &resp.Diagnostics, false, data) if resp.Diagnostics.HasError() { return @@ -309,9 +332,13 @@ func setTagTagId(ctx context.Context, data *TagTagResourceModel) { data.Id = types.StringValue(fmt.Sprintf("%s/%s", data.ParentDn.ValueString(), rn)) } -func getTagTagCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *TagTagResourceModel) *container.Container { +func getTagTagCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *TagTagResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } if !data.Key.IsNull() && !data.Key.IsUnknown() { payloadMap["attributes"].(map[string]string)["key"] = data.Key.ValueString() } diff --git a/internal/provider/resource_aci_tag_test.go b/internal/provider/resource_aci_tag_test.go index 61187552c..e616256b0 100644 --- a/internal/provider/resource_aci_tag_test.go +++ b/internal/provider/resource_aci_tag_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,56 @@ import ( func TestAccResourceTagTagWithFvTenant(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagTagMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_tag.test", "key", "test_key"), + resource.TestCheckResourceAttr("aci_tag.test_2", "key", "test_key"), + resource.TestCheckResourceAttr("aci_tag.test", "value", "test_value"), + resource.TestCheckResourceAttr("aci_tag.test_2", "value", "test_value"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagTagMinDependencyWithFvTenantAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagTagMinDependencyWithFvTenantAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_tag.test", "key", "test_key"), + resource.TestCheckResourceAttr("aci_tag.test_2", "key", "test_key"), + resource.TestCheckResourceAttr("aci_tag.test", "value", "test_value"), + resource.TestCheckResourceAttr("aci_tag.test_2", "value", "test_value"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -67,6 +118,56 @@ func TestAccResourceTagTagWithFvTenant(t *testing.T) { } func TestAccResourceTagTagWithFvAEPg(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagTagMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_tag.test", "key", "test_key"), + resource.TestCheckResourceAttr("aci_tag.test_2", "key", "test_key"), + resource.TestCheckResourceAttr("aci_tag.test", "value", "test_value"), + resource.TestCheckResourceAttr("aci_tag.test_2", "value", "test_value"), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagTagMinDependencyWithFvAEPgAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigTagTagMinDependencyWithFvAEPgAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_tag.test", "key", "test_key"), + resource.TestCheckResourceAttr("aci_tag.test_2", "key", "test_key"), + resource.TestCheckResourceAttr("aci_tag.test", "value", "test_value"), + resource.TestCheckResourceAttr("aci_tag.test_2", "value", "test_value"), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -121,6 +222,20 @@ func TestAccResourceTagTagWithFvAEPg(t *testing.T) { }) } +const testConfigTagTagMinDependencyWithFvTenantAllowExisting = testConfigFvTenantMin + ` +resource "aci_tag" "test" { + parent_dn = aci_tenant.test.id + key = "test_key" + value = "test_value" +} +resource "aci_tag" "test_2" { + parent_dn = aci_tenant.test.id + key = "test_key" + value = "test_value" + depends_on = [aci_tag.test] +} +` + const testConfigTagTagMinDependencyWithFvTenant = testConfigFvTenantMin + ` resource "aci_tag" "test" { parent_dn = aci_tenant.test.id @@ -145,6 +260,20 @@ resource "aci_tag" "test" { } ` +const testConfigTagTagMinDependencyWithFvAEPgAllowExisting = testConfigFvAEPgMin + ` +resource "aci_tag" "test" { + parent_dn = aci_application_epg.test.id + key = "test_key" + value = "test_value" +} +resource "aci_tag" "test_2" { + parent_dn = aci_application_epg.test.id + key = "test_key" + value = "test_value" + depends_on = [aci_tag.test] +} +` + const testConfigTagTagMinDependencyWithFvAEPg = testConfigFvAEPgMin + ` resource "aci_tag" "test" { parent_dn = aci_application_epg.test.id diff --git a/internal/provider/resource_aci_vrf_fallback_route.go b/internal/provider/resource_aci_vrf_fallback_route.go index 1993f8c5d..b0872e97a 100644 --- a/internal/provider/resource_aci_vrf_fallback_route.go +++ b/internal/provider/resource_aci_vrf_fallback_route.go @@ -68,6 +68,29 @@ type FvFBRouteIdentifier struct { FbrPrefix types.String } +func (r *FvFBRouteResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvFBRouteResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.FbrPrefix.IsUnknown() { + var createCheckData *FvFBRouteResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvFBRouteId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvFBRoute", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvFBRouteResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_vrf_fallback_route") resp.TypeName = req.ProviderTypeName + "_vrf_fallback_route" @@ -223,6 +246,13 @@ func (r *FvFBRouteResource) Create(ctx context.Context, req resource.CreateReque resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvFBRouteId(ctx, stateData) getAndSetFvFBRouteAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvFBRoute object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvFBRouteResourceModel @@ -243,7 +273,7 @@ func (r *FvFBRouteResource) Create(ctx context.Context, req resource.CreateReque var tagTagPlan, tagTagState []TagTagFvFBRouteResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvFBRouteCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvFBRouteCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -308,7 +338,7 @@ func (r *FvFBRouteResource) Update(ctx context.Context, req resource.UpdateReque var tagTagPlan, tagTagState []TagTagFvFBRouteResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvFBRouteCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvFBRouteCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -552,9 +582,13 @@ func getFvFBRouteTagTagChildPayloads(ctx context.Context, diags *diag.Diagnostic return childPayloads } -func getFvFBRouteCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvFBRouteResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvFBRouteResourceModel, tagTagPlan, tagTagState []TagTagFvFBRouteResourceModel) *container.Container { +func getFvFBRouteCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvFBRouteResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvFBRouteResourceModel, tagTagPlan, tagTagState []TagTagFvFBRouteResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvFBRouteTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_vrf_fallback_route_group.go b/internal/provider/resource_aci_vrf_fallback_route_group.go index 81717de26..8b97b195f 100644 --- a/internal/provider/resource_aci_vrf_fallback_route_group.go +++ b/internal/provider/resource_aci_vrf_fallback_route_group.go @@ -89,6 +89,29 @@ type FvFBRGroupIdentifier struct { Name types.String } +func (r *FvFBRGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvFBRGroupResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.Name.IsUnknown() { + var createCheckData *FvFBRGroupResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvFBRGroupId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvFBRGroup", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvFBRGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_vrf_fallback_route_group") resp.TypeName = req.ProviderTypeName + "_vrf_fallback_route_group" @@ -341,6 +364,13 @@ func (r *FvFBRGroupResource) Create(ctx context.Context, req resource.CreateRequ resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvFBRGroupId(ctx, stateData) getAndSetFvFBRGroupAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvFBRGroup object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvFBRGroupResourceModel @@ -367,7 +397,7 @@ func (r *FvFBRGroupResource) Create(ctx context.Context, req resource.CreateRequ var tagTagPlan, tagTagState []TagTagFvFBRGroupResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvFBRGroupCreateJsonPayload(ctx, &resp.Diagnostics, data, fvFBRMemberPlan, fvFBRMemberState, fvFBRoutePlan, fvFBRouteState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvFBRGroupCreateJsonPayload(ctx, &resp.Diagnostics, true, data, fvFBRMemberPlan, fvFBRMemberState, fvFBRoutePlan, fvFBRouteState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -438,7 +468,7 @@ func (r *FvFBRGroupResource) Update(ctx context.Context, req resource.UpdateRequ var tagTagPlan, tagTagState []TagTagFvFBRGroupResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvFBRGroupCreateJsonPayload(ctx, &resp.Diagnostics, data, fvFBRMemberPlan, fvFBRMemberState, fvFBRoutePlan, fvFBRouteState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvFBRGroupCreateJsonPayload(ctx, &resp.Diagnostics, false, data, fvFBRMemberPlan, fvFBRMemberState, fvFBRoutePlan, fvFBRouteState, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -827,9 +857,13 @@ func getFvFBRGroupTagTagChildPayloads(ctx context.Context, diags *diag.Diagnosti return childPayloads } -func getFvFBRGroupCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvFBRGroupResourceModel, fvFBRMemberPlan, fvFBRMemberState []FvFBRMemberFvFBRGroupResourceModel, fvFBRoutePlan, fvFBRouteState []FvFBRouteFvFBRGroupResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvFBRGroupResourceModel, tagTagPlan, tagTagState []TagTagFvFBRGroupResourceModel) *container.Container { +func getFvFBRGroupCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvFBRGroupResourceModel, fvFBRMemberPlan, fvFBRMemberState []FvFBRMemberFvFBRGroupResourceModel, fvFBRoutePlan, fvFBRouteState []FvFBRouteFvFBRGroupResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvFBRGroupResourceModel, tagTagPlan, tagTagState []TagTagFvFBRGroupResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} FvFBRMemberchildPayloads := getFvFBRGroupFvFBRMemberChildPayloads(ctx, diags, data, fvFBRMemberPlan, fvFBRMemberState) diff --git a/internal/provider/resource_aci_vrf_fallback_route_group_member.go b/internal/provider/resource_aci_vrf_fallback_route_group_member.go index c78c3486b..0f7472b8e 100644 --- a/internal/provider/resource_aci_vrf_fallback_route_group_member.go +++ b/internal/provider/resource_aci_vrf_fallback_route_group_member.go @@ -68,6 +68,29 @@ type FvFBRMemberIdentifier struct { RnhAddr types.String } +func (r *FvFBRMemberResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Plan.Raw.IsNull() { + var planData, stateData *FvFBRMemberResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) + resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + + if resp.Diagnostics.HasError() { + return + } + + if stateData == nil && !globalAllowExistingOnCreate && !planData.ParentDn.IsUnknown() && !planData.RnhAddr.IsUnknown() { + var createCheckData *FvFBRMemberResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &createCheckData)...) + setFvFBRMemberId(ctx, createCheckData) + CheckDn(ctx, &resp.Diagnostics, r.client, "fvFBRMember", createCheckData.Id.ValueString()) + if resp.Diagnostics.HasError() { + return + } + } + + } +} + func (r *FvFBRMemberResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { tflog.Debug(ctx, "Start metadata of resource: aci_vrf_fallback_route_group_member") resp.TypeName = req.ProviderTypeName + "_vrf_fallback_route_group_member" @@ -223,6 +246,13 @@ func (r *FvFBRMemberResource) Create(ctx context.Context, req resource.CreateReq resp.Diagnostics.Append(req.Plan.Get(ctx, &stateData)...) setFvFBRMemberId(ctx, stateData) getAndSetFvFBRMemberAttributes(ctx, &resp.Diagnostics, r.client, stateData) + if !globalAllowExistingOnCreate && !stateData.Id.IsNull() { + resp.Diagnostics.AddError( + "Object Already Exists", + fmt.Sprintf("The fvFBRMember object with DN '%s' already exists.", stateData.Id.ValueString()), + ) + return + } var data *FvFBRMemberResourceModel @@ -243,7 +273,7 @@ func (r *FvFBRMemberResource) Create(ctx context.Context, req resource.CreateReq var tagTagPlan, tagTagState []TagTagFvFBRMemberResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvFBRMemberCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvFBRMemberCreateJsonPayload(ctx, &resp.Diagnostics, true, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -308,7 +338,7 @@ func (r *FvFBRMemberResource) Update(ctx context.Context, req resource.UpdateReq var tagTagPlan, tagTagState []TagTagFvFBRMemberResourceModel data.TagTag.ElementsAs(ctx, &tagTagPlan, false) stateData.TagTag.ElementsAs(ctx, &tagTagState, false) - jsonPayload := getFvFBRMemberCreateJsonPayload(ctx, &resp.Diagnostics, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) + jsonPayload := getFvFBRMemberCreateJsonPayload(ctx, &resp.Diagnostics, false, data, tagAnnotationPlan, tagAnnotationState, tagTagPlan, tagTagState) if resp.Diagnostics.HasError() { return @@ -552,9 +582,13 @@ func getFvFBRMemberTagTagChildPayloads(ctx context.Context, diags *diag.Diagnost return childPayloads } -func getFvFBRMemberCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, data *FvFBRMemberResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvFBRMemberResourceModel, tagTagPlan, tagTagState []TagTagFvFBRMemberResourceModel) *container.Container { +func getFvFBRMemberCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvFBRMemberResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvFBRMemberResourceModel, tagTagPlan, tagTagState []TagTagFvFBRMemberResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} + + if createType && !globalAllowExistingOnCreate { + payloadMap["attributes"].(map[string]string)["status"] = "created" + } childPayloads := []map[string]interface{}{} TagAnnotationchildPayloads := getFvFBRMemberTagAnnotationChildPayloads(ctx, diags, data, tagAnnotationPlan, tagAnnotationState) diff --git a/internal/provider/resource_aci_vrf_fallback_route_group_member_test.go b/internal/provider/resource_aci_vrf_fallback_route_group_member_test.go index 109e716b9..46bc50d5c 100644 --- a/internal/provider/resource_aci_vrf_fallback_route_group_member_test.go +++ b/internal/provider/resource_aci_vrf_fallback_route_group_member_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,68 @@ import ( func TestAccResourceFvFBRMemberWithFvFBRGroup(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvFBRMemberMinDependencyWithFvFBRGroupAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "fallback_member", "2.2.2.3"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "fallback_member", "2.2.2.3"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "name", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "name_alias", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvFBRMemberMinDependencyWithFvFBRGroupAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvFBRMemberMinDependencyWithFvFBRGroupAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "fallback_member", "2.2.2.3"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "fallback_member", "2.2.2.3"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "name", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group_member.test_2", "name_alias", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -157,6 +220,18 @@ func TestAccResourceFvFBRMemberWithFvFBRGroup(t *testing.T) { }) } +const testConfigFvFBRMemberMinDependencyWithFvFBRGroupAllowExisting = testConfigFvFBRGroupMinDependencyWithFvCtx + ` +resource "aci_vrf_fallback_route_group_member" "test" { + parent_dn = aci_vrf_fallback_route_group.test.id + fallback_member = "2.2.2.3" +} +resource "aci_vrf_fallback_route_group_member" "test_2" { + parent_dn = aci_vrf_fallback_route_group.test.id + fallback_member = "2.2.2.3" + depends_on = [aci_vrf_fallback_route_group_member.test] +} +` + const testConfigFvFBRMemberMinDependencyWithFvFBRGroup = testConfigFvFBRGroupMinDependencyWithFvCtx + ` resource "aci_vrf_fallback_route_group_member" "test" { parent_dn = aci_vrf_fallback_route_group.test.id diff --git a/internal/provider/resource_aci_vrf_fallback_route_group_test.go b/internal/provider/resource_aci_vrf_fallback_route_group_test.go index 4c3cd0797..68c4e8243 100644 --- a/internal/provider/resource_aci_vrf_fallback_route_group_test.go +++ b/internal/provider/resource_aci_vrf_fallback_route_group_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,64 @@ import ( func TestAccResourceFvFBRGroupWithFvCtx(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvFBRGroupMinDependencyWithFvCtxAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "name", "fallback_route_group"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test_2", "name", "fallback_route_group"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test_2", "name_alias", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvFBRGroupMinDependencyWithFvCtxAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvFBRGroupMinDependencyWithFvCtxAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "name", "fallback_route_group"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test_2", "name", "fallback_route_group"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test_2", "name_alias", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -207,6 +266,18 @@ func TestAccResourceFvFBRGroupWithFvCtx(t *testing.T) { }) } +const testConfigFvFBRGroupMinDependencyWithFvCtxAllowExisting = testConfigFvCtxMinDependencyWithFvTenant + ` +resource "aci_vrf_fallback_route_group" "test" { + parent_dn = aci_vrf.test.id + name = "fallback_route_group" +} +resource "aci_vrf_fallback_route_group" "test_2" { + parent_dn = aci_vrf.test.id + name = "fallback_route_group" + depends_on = [aci_vrf_fallback_route_group.test] +} +` + const testConfigFvFBRGroupMinDependencyWithFvCtx = testConfigFvCtxMinDependencyWithFvTenant + ` resource "aci_vrf_fallback_route_group" "test" { parent_dn = aci_vrf.test.id diff --git a/internal/provider/resource_aci_vrf_fallback_route_test.go b/internal/provider/resource_aci_vrf_fallback_route_test.go index 86abe798e..4eb2a64ce 100644 --- a/internal/provider/resource_aci_vrf_fallback_route_test.go +++ b/internal/provider/resource_aci_vrf_fallback_route_test.go @@ -5,6 +5,7 @@ package provider import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -12,6 +13,68 @@ import ( func TestAccResourceFvFBRouteWithFvFBRGroup(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvFBRouteMinDependencyWithFvFBRGroupAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "prefix_address", "2.2.2.3/24"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "prefix_address", "2.2.2.3/24"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "name", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "name_alias", ""), + ), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "false") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvFBRouteMinDependencyWithFvFBRGroupAllowExisting, + ExpectError: regexp.MustCompile("Object Already Exists"), + }, + }, + }) + + setEnvVariable(t, "ACI_ALLOW_EXISTING_ON_CREATE", "true") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create with minimum config and verify default APIC values + { + Config: testConfigFvFBRouteMinDependencyWithFvFBRGroupAllowExisting, + ExpectNonEmptyPlan: false, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "prefix_address", "2.2.2.3/24"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "prefix_address", "2.2.2.3/24"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "annotation", "orchestrator:terraform"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "description", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "name", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "name", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test", "name_alias", ""), + resource.TestCheckResourceAttr("aci_vrf_fallback_route.test_2", "name_alias", ""), + ), + }, + }, + }) + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -157,6 +220,18 @@ func TestAccResourceFvFBRouteWithFvFBRGroup(t *testing.T) { }) } +const testConfigFvFBRouteMinDependencyWithFvFBRGroupAllowExisting = testConfigFvFBRGroupMinDependencyWithFvCtx + ` +resource "aci_vrf_fallback_route" "test" { + parent_dn = aci_vrf_fallback_route_group.test.id + prefix_address = "2.2.2.3/24" +} +resource "aci_vrf_fallback_route" "test_2" { + parent_dn = aci_vrf_fallback_route_group.test.id + prefix_address = "2.2.2.3/24" + depends_on = [aci_vrf_fallback_route.test] +} +` + const testConfigFvFBRouteMinDependencyWithFvFBRGroup = testConfigFvFBRGroupMinDependencyWithFvCtx + ` resource "aci_vrf_fallback_route" "test" { parent_dn = aci_vrf_fallback_route_group.test.id diff --git a/internal/provider/utils.go b/internal/provider/utils.go index 080f85d76..b30d6633e 100644 --- a/internal/provider/utils.go +++ b/internal/provider/utils.go @@ -30,14 +30,12 @@ func GetMOName(dn string) string { return splittedDn[0] } -func CheckDn(ctx context.Context, client *client.Client, dn string, diags *diag.Diagnostics) { - tflog.Debug(ctx, fmt.Sprintf("validate relation dn: %s", dn)) - _, err := client.Get(dn) - if err != nil { - tflog.Error(ctx, fmt.Sprintf("failed validate relation dn: %s", dn)) +func CheckDn(ctx context.Context, diags *diag.Diagnostics, client *client.Client, classname, dn string) { + requestData := DoRestRequest(ctx, diags, client, fmt.Sprintf("api/mo/%s.json", dn), "GET", nil) + if requestData.Search("imdata").Search(classname).Data() != nil { diags.AddError( - "Relation target dn validation failed", - fmt.Sprintf("The relation target dn is not found: %s", dn), + "Object Already Exists", + fmt.Sprintf("The %s object with DN '%s' already exists.", classname, dn), ) } } @@ -70,7 +68,7 @@ func DoRestRequestEscapeHtml(ctx context.Context, diags *diag.Diagnostics, clien if errCode != "1" && errCode != "103" && errCode != "107" && errCode != "120" { diags.AddError( fmt.Sprintf("The %s rest request failed", strings.ToLower(method)), - fmt.Sprintf("Code: %d Response: %s, err: %s. Please report this issue to the provider developers.", restResponse.StatusCode, cont.Data().(map[string]interface{})["imdata"], err), + fmt.Sprintf("Code: %d Response: %s, err: %s.", restResponse.StatusCode, cont.Data().(map[string]interface{})["imdata"], err), ) return nil }