diff --git a/docs/data-sources/access_interface_override.md b/docs/data-sources/access_interface_override.md index 98d1d9a31..deb3c0574 100644 --- a/docs/data-sources/access_interface_override.md +++ b/docs/data-sources/access_interface_override.md @@ -61,11 +61,11 @@ data "aci_access_interface_override" "example" { * `owner_key` (ownerKey) - (string) The key for enabling clients to own their data for entity correlation. * `owner_tag` (ownerTag) - (string) A tag for enabling clients to add their own data. For example, to indicate who created this object. -* `relation_to_host_path` - (list) A list of Relation To Host Path (ACI object [infraRsHPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraRsHPathAtt/overview)) pointing to (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). +* `relation_to_host_path` - (map) A map of Relation To Host Path (ACI object [infraRsHPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraRsHPathAtt/overview)) pointing to Fabric Path Endpoint (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). * `annotation` (annotation) - (string) The annotation of the Relation To Host Path object. * `target_dn` (tDn) - (string) The distinguished name of the target. -* `relation_to_access_interface_policy_group` - (list) A list of Relation To Access Interface Policy Group (ACI object [infraRsPathToAccBaseGrp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraRsPathToAccBaseGrp/overview)) pointing to (ACI Object [infraAccBaseGrp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraAccBaseGrp/overview)). +* `relation_to_access_interface_policy_group` - (map) A map of Relation To Access Interface Policy Group (ACI object [infraRsPathToAccBaseGrp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraRsPathToAccBaseGrp/overview)) pointing to Access Interface Policy Group (ACI Object [infraAccBaseGrp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraAccBaseGrp/overview)). * `annotation` (annotation) - (string) The annotation of the Relation To Access Interface Policy Group object. * `target_dn` (tDn) - (string) The distinguished name of the target. diff --git a/docs/data-sources/application_epg.md b/docs/data-sources/application_epg.md index 1fd0684ee..f058b8ec9 100644 --- a/docs/data-sources/application_epg.md +++ b/docs/data-sources/application_epg.md @@ -63,7 +63,7 @@ data "aci_application_epg" "example_application_profile" { * `priority` (prio) - (string) The Quality of Service (QoS) priority class ID. QoS refers to the capability of a network to provide better service to selected network traffic over various technologies. The primary goal of QoS is to provide priority including dedicated bandwidth, controlled jitter and latency (required by some real-time and interactive traffic), and improved loss characteristics. You can configure the bandwidth of each QoS level using QoS profiles. * `admin_state` (shutdown) - (string) Withdraw AEPg Configuration from all Nodes in the Fabric. -* `epg_useg_block_statement` - (list) A list of EPG uSeg Block Statement (ACI object [fvCrtrn](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCrtrn/overview)). This attribute is supported in ACI versions: 1.1(1j) and later. +* `epg_useg_block_statement` - (map) A map of EPG uSeg Block Statement (ACI object [fvCrtrn](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCrtrn/overview)). This attribute is supported in ACI versions: 1.1(1j) and later. * `annotation` (annotation) - (string) The annotation of the EPG uSeg Block Statement object. * `description` (descr) - (string) The description of the EPG uSeg Block Statement object. * `match` (match) - (string) The Matching Rule Type of the EPG uSeg Block Statement object. @@ -74,11 +74,11 @@ data "aci_application_epg" "example_application_profile" { * `precedence` (prec) - (string) The precedence of the EPG uSeg Block Statement object. * `scope` (scope) - (string) The scope of the EPG uSeg Block Statement object. -* `relation_to_application_epg_monitoring_policy` - (list) A list of Relation To Application EPG Monitoring Policy (ACI object [fvRsAEPgMonPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsAEPgMonPol/overview)) pointing to Monitoring Policy (ACI Object [monEPGPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/monEPGPol/overview)). +* `relation_to_application_epg_monitoring_policy` - (map) A map of Relation To Application EPG Monitoring Policy (ACI object [fvRsAEPgMonPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsAEPgMonPol/overview)) pointing to Monitoring Policy (ACI Object [monEPGPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/monEPGPol/overview)). * `annotation` (annotation) - (string) The annotation of the Relation To Application EPG Monitoring Policy object. * `monitoring_policy_name` (tnMonEPGPolName) - (string) The name of the monitoring policy. -* `relation_to_bridge_domain` - (list) A list of Relation To Bridge Domain (ACI object [fvRsBd](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsBd/overview)) pointing to Bridge Domain (ACI Object [fvBD](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvBD/overview)). +* `relation_to_bridge_domain` - (map) A map of Relation To Bridge Domain (ACI object [fvRsBd](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsBd/overview)) pointing to Bridge Domain (ACI Object [fvBD](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvBD/overview)). * `annotation` (annotation) - (string) The annotation of the Relation To Bridge Domain object. * `bridge_domain_name` (tnFvBDName) - (string) The name of the bridge domain associated with this EPG. @@ -92,7 +92,7 @@ data "aci_application_epg" "example_application_profile" { * `priority` (prio) - (string) The Quality of Service (QoS) priority class ID. QoS refers to the capability of a network to provide better service to selected network traffic over various technologies. The primary goal of QoS is to provide priority including dedicated bandwidth, controlled jitter and latency (required by some real-time and interactive traffic), and improved loss characteristics. You can configure the bandwidth of each QoS level using QoS profiles. * `imported_contract_name` (tnVzCPIfName) - (string) The contract interface name. -* `relation_to_custom_qos_policy` - (list) A list of Relation To Custom Qos Policy (ACI object [fvRsCustQosPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsCustQosPol/overview)) pointing to Custom Qos Policy (ACI Object [qosCustomPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/qosCustomPol/overview)). +* `relation_to_custom_qos_policy` - (map) A map of Relation To Custom Qos Policy (ACI object [fvRsCustQosPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsCustQosPol/overview)) pointing to Custom Qos Policy (ACI Object [qosCustomPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/qosCustomPol/overview)). * `annotation` (annotation) - (string) The annotation of the Relation To Custom Qos Policy object. * `custom_qos_policy_name` (tnQosCustomPolName) - (string) The Custom QoS traffic policy name. @@ -129,11 +129,11 @@ data "aci_application_epg" "example_application_profile" { * `untagged` (untagged) - (string) The untagged status of the Relation To Domain object. * `vnet_only` (vnetOnly) - (string) The VNET only status of the Relation To Domain object. -* `relation_to_data_plane_policing_policy` - (list) A list of Relation To Data Plane Policing Policy (ACI object [fvRsDppPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsDppPol/overview)) pointing to Data Plane Policing Policy (ACI Object [qosDppPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/qosDppPol/overview)). This attribute is supported in ACI versions: 3.0(1k) and later. +* `relation_to_data_plane_policing_policy` - (map) A map of Relation To Data Plane Policing Policy (ACI object [fvRsDppPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsDppPol/overview)) pointing to Data Plane Policing Policy (ACI Object [qosDppPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/qosDppPol/overview)). This attribute is supported in ACI versions: 3.0(1k) and later. * `annotation` (annotation) - (string) The annotation of the Relation To Data Plane Policing Policy object. * `data_plane_policing_policy_name` (tnQosDppPolName) - (string) Name. -* `relation_to_fibre_channel_paths` - (list) A list of Relation To Fibre Channel Paths (ACI object [fvRsFcPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsFcPathAtt/overview)) pointing to (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). This attribute is supported in ACI versions: 2.0(1m) and later. +* `relation_to_fibre_channel_paths` - (list) A list of Relation To Fibre Channel Paths (ACI object [fvRsFcPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsFcPathAtt/overview)) pointing to Fabric Path Endpoint (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). This attribute is supported in ACI versions: 2.0(1m) and later. * `annotation` (annotation) - (string) The annotation of the Relation To Fibre Channel Path object. * `description` (descr) - (string) The description of the Relation To Fibre Channel Path object. * `target_dn` (tDn) - (string) The distinguished name of the target. @@ -152,7 +152,7 @@ data "aci_application_epg" "example_application_profile" { * `mode` (mode) - (string) The static association mode with the path of the Relation To Static Leaf object. * `target_dn` (tDn) - (string) The distinguished name of the target of this static binding. -* `relation_to_static_paths` - (list) A list of Relation To Static Paths (ACI object [fvRsPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsPathAtt/overview)) pointing to (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). +* `relation_to_static_paths` - (list) A list of Relation To Static Paths (ACI object [fvRsPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsPathAtt/overview)) pointing to Fabric Path Endpoint (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). * `annotation` (annotation) - (string) The annotation of the Relation To Static Path object. * `description` (descr) - (string) The description of the Relation To Static Path object. * `encapsulation` (encap) - (string) The VLAN encapsulation of the Relation To Static Path object. @@ -175,7 +175,7 @@ data "aci_application_epg" "example_application_profile" { * `annotation` (annotation) - (string) The annotation of the Relation To Contract Master object. * `target_dn` (tDn) - (string) The distinguished name of the target. -* `relation_to_trust_control_policy` - (list) A list of Relation To Trust Control Policy (ACI object [fvRsTrustCtrl](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsTrustCtrl/overview)) pointing to Trust Control Policy (ACI Object [fhsTrustCtrlPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fhsTrustCtrlPol/overview)). This attribute is supported in ACI versions: 3.0(1k) and later. +* `relation_to_trust_control_policy` - (map) A map of Relation To Trust Control Policy (ACI object [fvRsTrustCtrl](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsTrustCtrl/overview)) pointing to Trust Control Policy (ACI Object [fhsTrustCtrlPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fhsTrustCtrlPol/overview)). This attribute is supported in ACI versions: 3.0(1k) and later. * `annotation` (annotation) - (string) The annotation of the Relation To Trust Control Policy object. * `trust_control_policy_name` (tnFhsTrustCtrlPolName) - (string) Name. diff --git a/docs/data-sources/endpoint_security_group.md b/docs/data-sources/endpoint_security_group.md index 126134313..ff4d898e8 100644 --- a/docs/data-sources/endpoint_security_group.md +++ b/docs/data-sources/endpoint_security_group.md @@ -78,7 +78,7 @@ data "aci_endpoint_security_group" "example_application_profile" { * `priority` (prio) - (string) The Quality of Service (QoS) priority class ID. QoS refers to the capability of a network to provide better service to selected network traffic over various technologies. The primary goal of QoS is to provide priority including dedicated bandwidth, controlled jitter and latency (required by some real-time and interactive traffic), and improved loss characteristics. You can configure the bandwidth of each QoS level using QoS profiles. * `contract_name` (tnVzBrCPName) - (string) The provider contract name. -* `relation_to_vrf` - (list) A list of Relation To VRF (ACI object [fvRsScope](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsScope/overview)) pointing to VRF (ACI Object [fvCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCtx/overview)). +* `relation_to_vrf` - (map) A map of Relation To VRF (ACI object [fvRsScope](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsScope/overview)) pointing to VRF (ACI Object [fvCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCtx/overview)). * `annotation` (annotation) - (string) The annotation of the Relation To VRF object. * `vrf_name` (tnFvCtxName) - (string) The name of the VRF object. diff --git a/docs/data-sources/netflow_exporter_policy.md b/docs/data-sources/netflow_exporter_policy.md index f03edd29d..a1d5a265f 100644 --- a/docs/data-sources/netflow_exporter_policy.md +++ b/docs/data-sources/netflow_exporter_policy.md @@ -71,7 +71,7 @@ data "aci_netflow_exporter_policy" "example_tenant" { * `source_ip_address` (srcAddr) - (string) The source IP address. * `version` (ver) - (string) The NetFlow Exporter Version of the NetFlow Exporter Policy object. -* `relation_to_vrf` - (list) A list of Relation From NetFlow Exporter To VRF (ACI object [netflowRsExporterToCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRsExporterToCtx/overview)) pointing to VRF (ACI Object [fvCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCtx/overview)). +* `relation_to_vrf` - (map) A map of Relation From NetFlow Exporter To VRF (ACI object [netflowRsExporterToCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRsExporterToCtx/overview)) pointing to VRF (ACI Object [fvCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCtx/overview)). * `annotation` (annotation) - (string) The annotation of the Relation From NetFlow Exporter To VRF object. * `target_dn` (tDn) - (string) The distinguished name of the target. diff --git a/docs/data-sources/netflow_monitor_policy.md b/docs/data-sources/netflow_monitor_policy.md index 74046ecf9..718547255 100644 --- a/docs/data-sources/netflow_monitor_policy.md +++ b/docs/data-sources/netflow_monitor_policy.md @@ -69,7 +69,7 @@ data "aci_netflow_monitor_policy" "example_tenant" { * `annotation` (annotation) - (string) The annotation of the Relation To NetFlow Exporter object. * `netflow_exporter_policy_name` (tnNetflowExporterPolName) - (string) Name. -* `relation_to_netflow_record` - (list) A list of Relation To NetFlow Record (ACI object [netflowRsMonitorToRecord](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRsMonitorToRecord/overview)) pointing to NetFlow Record Policy (ACI Object [netflowRecordPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRecordPol/overview)). +* `relation_to_netflow_record` - (map) A map of Relation To NetFlow Record (ACI object [netflowRsMonitorToRecord](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRsMonitorToRecord/overview)) pointing to NetFlow Record Policy (ACI Object [netflowRecordPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRecordPol/overview)). * `annotation` (annotation) - (string) The annotation of the Relation To NetFlow Record object. * `netflow_record_policy_name` (tnNetflowRecordPolName) - (string) Name. diff --git a/docs/data-sources/vrf_fallback_route_group.md b/docs/data-sources/vrf_fallback_route_group.md index 4e8aeb357..fcd7829e2 100644 --- a/docs/data-sources/vrf_fallback_route_group.md +++ b/docs/data-sources/vrf_fallback_route_group.md @@ -59,7 +59,7 @@ data "aci_vrf_fallback_route_group" "example_vrf" { * `name_alias` (nameAlias) - (string) The name alias of the VRF Fallback Route Group Member object. * `fallback_member` (rnhAddr) - (string) The address of the VRF Fallback Route Group Member object. -* `vrf_fallback_routes` - (list) A list of VRF Fallback Routes (ACI object [fvFBRoute](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvFBRoute/overview)). +* `vrf_fallback_route` - (map) A map of VRF Fallback Route (ACI object [fvFBRoute](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvFBRoute/overview)). * `annotation` (annotation) - (string) The annotation of the VRF Fallback Route object. * `description` (descr) - (string) The description of the VRF Fallback Route object. * `prefix_address` (fbrPrefix) - (string) The prefix address of the VRF Fallback Route object. diff --git a/docs/resources/access_interface_override.md b/docs/resources/access_interface_override.md index 3410f421a..5aabf0170 100644 --- a/docs/resources/access_interface_override.md +++ b/docs/resources/access_interface_override.md @@ -54,18 +54,14 @@ resource "aci_access_interface_override" "full_example" { name_alias = "name_alias_1" owner_key = "owner_key_1" owner_tag = "owner_tag_1" - relation_to_host_path = [ - { - annotation = "annotation_1" - target_dn = "topology/pod-1/paths-101/pathep-[eth1/1]" - } - ] - relation_to_access_interface_policy_group = [ - { - annotation = "annotation_1" - target_dn = "uni/infra/funcprof/accportgrp-access_interface_policy_group" - } - ] + relation_to_host_path = { + annotation = "annotation_1" + target_dn = "topology/pod-1/paths-101/pathep-[eth1/1]" + } + relation_to_access_interface_policy_group = { + annotation = "annotation_1" + target_dn = "uni/infra/funcprof/accportgrp-access_interface_policy_group" + } annotations = [ { key = "key_0" @@ -108,8 +104,7 @@ All examples for the Access Interface Override resource can be found in the [exa * `owner_key` (ownerKey) - (string) The key for enabling clients to own their data for entity correlation. * `owner_tag` (ownerTag) - (string) A tag for enabling clients to add their own data. For example, to indicate who created this object. -* `relation_to_host_path` - (list) A list of Relation To Host Path (ACI object [infraRsHPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraRsHPathAtt/overview)) pointing to (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). - - Max Items: 1 +* `relation_to_host_path` - (map) A map of Relation To Host Path (ACI object [infraRsHPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraRsHPathAtt/overview)) pointing to Fabric Path Endpoint (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). #### Required #### @@ -120,8 +115,7 @@ All examples for the Access Interface Override resource can be found in the [exa * `annotation` (annotation) - (string) The annotation of the Relation To Host Path object. - Default: `orchestrator:terraform` -* `relation_to_access_interface_policy_group` - (list) A list of Relation To Access Interface Policy Group (ACI object [infraRsPathToAccBaseGrp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraRsPathToAccBaseGrp/overview)) pointing to (ACI Object [infraAccBaseGrp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraAccBaseGrp/overview)). - - Max Items: 1 +* `relation_to_access_interface_policy_group` - (map) A map of Relation To Access Interface Policy Group (ACI object [infraRsPathToAccBaseGrp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraRsPathToAccBaseGrp/overview)) pointing to Access Interface Policy Group (ACI Object [infraAccBaseGrp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/infraAccBaseGrp/overview)) which can be configured using the [aci_access_interface_policy_group](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/access_interface_policy_group) resource. #### Optional #### diff --git a/docs/resources/application_epg.md b/docs/resources/application_epg.md index e488dac11..73c074192 100644 --- a/docs/resources/application_epg.md +++ b/docs/resources/application_epg.md @@ -62,31 +62,25 @@ resource "aci_application_epg" "full_example_application_profile" { preferred_group_member = "exclude" priority = "level1" admin_state = "no" - epg_useg_block_statement = [ - { - annotation = "annotation_1" - description = "description_1" - match = "all" - name = "criterion" - name_alias = "name_alias_1" - owner_key = "owner_key_1" - owner_tag = "owner_tag_1" - precedence = "1" - scope = "scope-bd" - } - ] - relation_to_application_epg_monitoring_policy = [ - { - annotation = "annotation_1" - monitoring_policy_name = aci_monitoring_policy.example.name - } - ] - relation_to_bridge_domain = [ - { - annotation = "annotation_1" - bridge_domain_name = aci_bridge_domain.example.name - } - ] + epg_useg_block_statement = { + annotation = "annotation_1" + description = "description_1" + match = "all" + name = "criterion" + name_alias = "name_alias_1" + owner_key = "owner_key_1" + owner_tag = "owner_tag_1" + precedence = "1" + scope = "scope-bd" + } + relation_to_application_epg_monitoring_policy = { + annotation = "annotation_1" + monitoring_policy_name = aci_monitoring_policy.example.name + } + relation_to_bridge_domain = { + annotation = "annotation_1" + bridge_domain_name = aci_bridge_domain.example.name + } relation_to_consumed_contracts = [ { annotation = "annotation_1" @@ -101,12 +95,10 @@ resource "aci_application_epg" "full_example_application_profile" { imported_contract_name = aci_imported_contract.example.name } ] - relation_to_custom_qos_policy = [ - { - annotation = "annotation_1" - custom_qos_policy_name = aci_custom_qos_policy.example.name - } - ] + relation_to_custom_qos_policy = { + annotation = "annotation_1" + custom_qos_policy_name = aci_custom_qos_policy.example.name + } relation_to_domains = [ { annotation = "annotation_1" @@ -137,12 +129,10 @@ resource "aci_application_epg" "full_example_application_profile" { vnet_only = "no" } ] - relation_to_data_plane_policing_policy = [ - { - annotation = "annotation_1" - data_plane_policing_policy_name = aci_data_plane_policing_policy.example.name - } - ] + relation_to_data_plane_policing_policy = { + annotation = "annotation_1" + data_plane_policing_policy_name = aci_data_plane_policing_policy.example.name + } relation_to_fibre_channel_paths = [ { annotation = "annotation_1" @@ -199,12 +189,10 @@ resource "aci_application_epg" "full_example_application_profile" { target_dn = aci_application_epg.test_application_epg_0.id } ] - relation_to_trust_control_policy = [ - { - annotation = "annotation_1" - trust_control_policy_name = aci_trust_control_policy.example.name - } - ] + relation_to_trust_control_policy = { + annotation = "annotation_1" + trust_control_policy_name = aci_trust_control_policy.example.name + } annotations = [ { key = "key_0" @@ -271,8 +259,7 @@ All examples for the Application EPG resource can be found in the [examples](htt - Default: `no` - Valid Values: `no`, `yes`. -* `epg_useg_block_statement` - (list) A list of EPG uSeg Block Statement (ACI object [fvCrtrn](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCrtrn/overview)). EPG uSeg Block Statement can also be configured using a separate [aci_](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/) resource. This attribute is supported in ACI versions: 1.1(1j) and later. - - Max Items: 1 +* `epg_useg_block_statement` - (map) A map of EPG uSeg Block Statement (ACI object [fvCrtrn](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCrtrn/overview)). EPG uSeg Block Statement can also be configured using a separate [aci_epg_useg_block_statement](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/epg_useg_block_statement) resource. This attribute is supported in ACI versions: 1.1(1j) and later. #### Optional #### @@ -292,8 +279,7 @@ All examples for the Application EPG resource can be found in the [examples](htt - Default: `scope-bd` - Valid Values: `scope-bd`, `scope-vrf`. -* `relation_to_application_epg_monitoring_policy` - (list) A list of Relation To Application EPG Monitoring Policy (ACI object [fvRsAEPgMonPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsAEPgMonPol/overview)) pointing to Monitoring Policy (ACI Object [monEPGPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/monEPGPol/overview)) which can be configured using the [aci_monitoring_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/monitoring_policy) resource. - - Max Items: 1 +* `relation_to_application_epg_monitoring_policy` - (map) A map of Relation To Application EPG Monitoring Policy (ACI object [fvRsAEPgMonPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsAEPgMonPol/overview)) pointing to Monitoring Policy (ACI Object [monEPGPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/monEPGPol/overview)) which can be configured using the [aci_monitoring_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/monitoring_policy) resource. #### Optional #### @@ -302,8 +288,7 @@ All examples for the Application EPG resource can be found in the [examples](htt - Default: `orchestrator:terraform` * `monitoring_policy_name` (tnMonEPGPolName) - (string) The name of the monitoring policy. -* `relation_to_bridge_domain` - (list) A list of Relation To Bridge Domain (ACI object [fvRsBd](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsBd/overview)) pointing to Bridge Domain (ACI Object [fvBD](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvBD/overview)) which can be configured using the [aci_bridge_domain](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/bridge_domain) resource. - - Max Items: 1 +* `relation_to_bridge_domain` - (map) A map of Relation To Bridge Domain (ACI object [fvRsBd](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsBd/overview)) pointing to Bridge Domain (ACI Object [fvBD](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvBD/overview)) which can be configured using the [aci_bridge_domain](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/bridge_domain) resource. #### Optional #### @@ -340,8 +325,7 @@ All examples for the Application EPG resource can be found in the [examples](htt - Default: `unspecified` - Valid Values: `level1`, `level2`, `level3`, `level4`, `level5`, `level6`, `unspecified`. -* `relation_to_custom_qos_policy` - (list) A list of Relation To Custom Qos Policy (ACI object [fvRsCustQosPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsCustQosPol/overview)) pointing to Custom Qos Policy (ACI Object [qosCustomPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/qosCustomPol/overview)) which can be configured using the [aci_custom_qos_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/custom_qos_policy) resource. - - Max Items: 1 +* `relation_to_custom_qos_policy` - (map) A map of Relation To Custom Qos Policy (ACI object [fvRsCustQosPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsCustQosPol/overview)) pointing to Custom Qos Policy (ACI Object [qosCustomPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/qosCustomPol/overview)) which can be configured using the [aci_custom_qos_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/custom_qos_policy) resource. #### Optional #### @@ -418,8 +402,7 @@ All examples for the Application EPG resource can be found in the [examples](htt - Default: `no` - Valid Values: `no`, `yes`. -* `relation_to_data_plane_policing_policy` - (list) A list of Relation To Data Plane Policing Policy (ACI object [fvRsDppPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsDppPol/overview)) pointing to Data Plane Policing Policy (ACI Object [qosDppPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/qosDppPol/overview)) which can be configured using the [aci_data_plane_policing_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/data_plane_policing_policy) resource. This attribute is supported in ACI versions: 3.0(1k) and later. - - Max Items: 1 +* `relation_to_data_plane_policing_policy` - (map) A map of Relation To Data Plane Policing Policy (ACI object [fvRsDppPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsDppPol/overview)) pointing to Data Plane Policing Policy (ACI Object [qosDppPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/qosDppPol/overview)) which can be configured using the [aci_data_plane_policing_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/data_plane_policing_policy) resource. This attribute is supported in ACI versions: 3.0(1k) and later. #### Optional #### @@ -428,7 +411,7 @@ All examples for the Application EPG resource can be found in the [examples](htt - Default: `orchestrator:terraform` * `data_plane_policing_policy_name` (tnQosDppPolName) - (string) Name. -* `relation_to_fibre_channel_paths` - (list) A list of Relation To Fibre Channel Paths (ACI object [fvRsFcPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsFcPathAtt/overview)) pointing to (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). This attribute is supported in ACI versions: 2.0(1m) and later. +* `relation_to_fibre_channel_paths` - (list) A list of Relation To Fibre Channel Paths (ACI object [fvRsFcPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsFcPathAtt/overview)) pointing to Fabric Path Endpoint (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). This attribute is supported in ACI versions: 2.0(1m) and later. #### Required #### @@ -475,7 +458,7 @@ All examples for the Application EPG resource can be found in the [examples](htt - Default: `regular` - Valid Values: `native`, `regular`, `untagged`. -* `relation_to_static_paths` - (list) A list of Relation To Static Paths (ACI object [fvRsPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsPathAtt/overview)) pointing to (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). +* `relation_to_static_paths` - (list) A list of Relation To Static Paths (ACI object [fvRsPathAtt](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsPathAtt/overview)) pointing to Fabric Path Endpoint (ACI Object [fabricPathEp](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fabricPathEp/overview)). #### Required #### @@ -534,8 +517,7 @@ All examples for the Application EPG resource can be found in the [examples](htt * `annotation` (annotation) - (string) The annotation of the Relation To Contract Master object. - Default: `orchestrator:terraform` -* `relation_to_trust_control_policy` - (list) A list of Relation To Trust Control Policy (ACI object [fvRsTrustCtrl](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsTrustCtrl/overview)) pointing to Trust Control Policy (ACI Object [fhsTrustCtrlPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fhsTrustCtrlPol/overview)) which can be configured using the [aci_trust_control_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/trust_control_policy) resource. This attribute is supported in ACI versions: 3.0(1k) and later. - - Max Items: 1 +* `relation_to_trust_control_policy` - (map) A map of Relation To Trust Control Policy (ACI object [fvRsTrustCtrl](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsTrustCtrl/overview)) pointing to Trust Control Policy (ACI Object [fhsTrustCtrlPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fhsTrustCtrlPol/overview)) which can be configured using the [aci_trust_control_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/trust_control_policy) resource. This attribute is supported in ACI versions: 3.0(1k) and later. #### Optional #### diff --git a/docs/resources/endpoint_security_group.md b/docs/resources/endpoint_security_group.md index 26a0412f1..8cf9b225c 100644 --- a/docs/resources/endpoint_security_group.md +++ b/docs/resources/endpoint_security_group.md @@ -85,12 +85,10 @@ resource "aci_endpoint_security_group" "full_example_application_profile" { contract_name = aci_contract.example.name } ] - relation_to_vrf = [ - { - annotation = "annotation_1" - vrf_name = aci_vrf.example.name - } - ] + relation_to_vrf = { + annotation = "annotation_1" + vrf_name = aci_vrf.example.name + } relation_to_contract_masters = [ { annotation = "annotation_1" @@ -204,8 +202,7 @@ All examples for the Endpoint Security Group resource can be found in the [examp - Default: `unspecified` - Valid Values: `level1`, `level2`, `level3`, `level4`, `level5`, `level6`, `unspecified`. -* `relation_to_vrf` - (list) A list of Relation To VRF (ACI object [fvRsScope](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsScope/overview)) pointing to VRF (ACI Object [fvCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCtx/overview)) which can be configured using the [aci_vrf](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/vrf) resource. - - Max Items: 1 +* `relation_to_vrf` - (map) A map of Relation To VRF (ACI object [fvRsScope](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvRsScope/overview)) pointing to VRF (ACI Object [fvCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCtx/overview)) which can be configured using the [aci_vrf](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/vrf) resource. #### Optional #### diff --git a/docs/resources/netflow_exporter_policy.md b/docs/resources/netflow_exporter_policy.md index d284984da..cfe4486cc 100644 --- a/docs/resources/netflow_exporter_policy.md +++ b/docs/resources/netflow_exporter_policy.md @@ -66,18 +66,14 @@ resource "aci_netflow_exporter_policy" "full_example_tenant" { source_ip_type = "custom-src-ip" source_ip_address = "1.1.1.1/10" version = "v9" - relation_to_vrf = [ - { - annotation = "annotation_1" - target_dn = aci_vrf.example.id - } - ] - relation_to_epg = [ - { - annotation = "annotation_1" - target_dn = aci_application_epg.example.id - } - ] + relation_to_vrf = { + annotation = "annotation_1" + target_dn = aci_vrf.example.id + } + relation_to_epg = { + annotation = "annotation_1" + target_dn = aci_application_epg.example.id + } annotations = [ { key = "key_0" @@ -138,8 +134,7 @@ All examples for the NetFlow Exporter Policy resource can be found in the [examp - Default: `v9` - Valid Values: `cisco-v1`, `v5`, `v9`. -* `relation_to_vrf` - (list) A list of Relation From NetFlow Exporter To VRF (ACI object [netflowRsExporterToCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRsExporterToCtx/overview)) pointing to VRF (ACI Object [fvCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCtx/overview)) which can be configured using the [aci_vrf](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/vrf) resource. - - Max Items: 1 +* `relation_to_vrf` - (map) A map of Relation From NetFlow Exporter To VRF (ACI object [netflowRsExporterToCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRsExporterToCtx/overview)) pointing to VRF (ACI Object [fvCtx](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvCtx/overview)) which can be configured using the [aci_vrf](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/vrf) resource. #### Optional #### diff --git a/docs/resources/netflow_monitor_policy.md b/docs/resources/netflow_monitor_policy.md index 6338ed7f9..46af0d7ff 100644 --- a/docs/resources/netflow_monitor_policy.md +++ b/docs/resources/netflow_monitor_policy.md @@ -64,12 +64,10 @@ resource "aci_netflow_monitor_policy" "full_example_tenant" { netflow_exporter_policy_name = aci_netflow_exporter_policy.example.name } ] - relation_to_netflow_record = [ - { - annotation = "annotation_1" - netflow_record_policy_name = aci_netflow_record_policy.example.name - } - ] + relation_to_netflow_record = { + annotation = "annotation_1" + netflow_record_policy_name = aci_netflow_record_policy.example.name + } annotations = [ { key = "key_0" @@ -124,8 +122,7 @@ All examples for the NetFlow Monitor Policy resource can be found in the [exampl * `annotation` (annotation) - (string) The annotation of the Relation To NetFlow Exporter object. - Default: `orchestrator:terraform` -* `relation_to_netflow_record` - (list) A list of Relation To NetFlow Record (ACI object [netflowRsMonitorToRecord](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRsMonitorToRecord/overview)) pointing to NetFlow Record Policy (ACI Object [netflowRecordPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRecordPol/overview)) which can be configured using the [aci_netflow_record_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/netflow_record_policy) resource. - - Max Items: 1 +* `relation_to_netflow_record` - (map) A map of Relation To NetFlow Record (ACI object [netflowRsMonitorToRecord](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRsMonitorToRecord/overview)) pointing to NetFlow Record Policy (ACI Object [netflowRecordPol](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/netflowRecordPol/overview)) which can be configured using the [aci_netflow_record_policy](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/netflow_record_policy) resource. #### Optional #### diff --git a/docs/resources/vrf_fallback_route_group.md b/docs/resources/vrf_fallback_route_group.md index b8bd77d8b..0c6eece17 100644 --- a/docs/resources/vrf_fallback_route_group.md +++ b/docs/resources/vrf_fallback_route_group.md @@ -61,15 +61,13 @@ resource "aci_vrf_fallback_route_group" "full_example_vrf" { fallback_member = "2.2.2.2" } ] - vrf_fallback_routes = [ - { - annotation = "annotation_1" - description = "description_1" - prefix_address = "2.2.2.2/24" - name = "name_1" - name_alias = "name_alias_1" - } - ] + vrf_fallback_route = { + annotation = "annotation_1" + description = "description_1" + prefix_address = "2.2.2.2/24" + name = "name_1" + name_alias = "name_alias_1" + } annotations = [ { key = "key_0" @@ -121,8 +119,7 @@ All examples for the VRF Fallback Route Group resource can be found in the [exam * `name` (name) - (string) The name of the VRF Fallback Route Group Member object. * `name_alias` (nameAlias) - (string) The name alias of the VRF Fallback Route Group Member object. -* `vrf_fallback_routes` - (list) A list of VRF Fallback Routes (ACI object [fvFBRoute](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvFBRoute/overview)). VRF Fallback Routes can also be configured using a separate [aci_vrf_fallback_route](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/vrf_fallback_route) resource. - - Max Items: 1 +* `vrf_fallback_route` - (map) A map of VRF Fallback Route (ACI object [fvFBRoute](https://pubhub.devnetcloud.com/media/model-doc-latest/docs/app/index.html#/objects/fvFBRoute/overview)). VRF Fallback Route can also be configured using a separate [aci_vrf_fallback_route](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/vrf_fallback_route) resource. #### Required #### diff --git a/examples/resources/aci_access_interface_override/resource-all-attributes.tf b/examples/resources/aci_access_interface_override/resource-all-attributes.tf index 142c2d6de..234ac4369 100644 --- a/examples/resources/aci_access_interface_override/resource-all-attributes.tf +++ b/examples/resources/aci_access_interface_override/resource-all-attributes.tf @@ -6,18 +6,14 @@ resource "aci_access_interface_override" "full_example" { name_alias = "name_alias_1" owner_key = "owner_key_1" owner_tag = "owner_tag_1" - relation_to_host_path = [ - { - annotation = "annotation_1" - target_dn = "topology/pod-1/paths-101/pathep-[eth1/1]" - } - ] - relation_to_access_interface_policy_group = [ - { - annotation = "annotation_1" - target_dn = "uni/infra/funcprof/accportgrp-access_interface_policy_group" - } - ] + relation_to_host_path = { + annotation = "annotation_1" + target_dn = "topology/pod-1/paths-101/pathep-[eth1/1]" + } + relation_to_access_interface_policy_group = { + annotation = "annotation_1" + target_dn = "uni/infra/funcprof/accportgrp-access_interface_policy_group" + } annotations = [ { key = "key_0" diff --git a/examples/resources/aci_application_epg/resource-all-attributes.tf b/examples/resources/aci_application_epg/resource-all-attributes.tf index d160106e0..850d1100c 100644 --- a/examples/resources/aci_application_epg/resource-all-attributes.tf +++ b/examples/resources/aci_application_epg/resource-all-attributes.tf @@ -15,31 +15,25 @@ resource "aci_application_epg" "full_example_application_profile" { preferred_group_member = "exclude" priority = "level1" admin_state = "no" - epg_useg_block_statement = [ - { - annotation = "annotation_1" - description = "description_1" - match = "all" - name = "criterion" - name_alias = "name_alias_1" - owner_key = "owner_key_1" - owner_tag = "owner_tag_1" - precedence = "1" - scope = "scope-bd" - } - ] - relation_to_application_epg_monitoring_policy = [ - { - annotation = "annotation_1" - monitoring_policy_name = aci_monitoring_policy.example.name - } - ] - relation_to_bridge_domain = [ - { - annotation = "annotation_1" - bridge_domain_name = aci_bridge_domain.example.name - } - ] + epg_useg_block_statement = { + annotation = "annotation_1" + description = "description_1" + match = "all" + name = "criterion" + name_alias = "name_alias_1" + owner_key = "owner_key_1" + owner_tag = "owner_tag_1" + precedence = "1" + scope = "scope-bd" + } + relation_to_application_epg_monitoring_policy = { + annotation = "annotation_1" + monitoring_policy_name = aci_monitoring_policy.example.name + } + relation_to_bridge_domain = { + annotation = "annotation_1" + bridge_domain_name = aci_bridge_domain.example.name + } relation_to_consumed_contracts = [ { annotation = "annotation_1" @@ -54,12 +48,10 @@ resource "aci_application_epg" "full_example_application_profile" { imported_contract_name = aci_imported_contract.example.name } ] - relation_to_custom_qos_policy = [ - { - annotation = "annotation_1" - custom_qos_policy_name = aci_custom_qos_policy.example.name - } - ] + relation_to_custom_qos_policy = { + annotation = "annotation_1" + custom_qos_policy_name = aci_custom_qos_policy.example.name + } relation_to_domains = [ { annotation = "annotation_1" @@ -90,12 +82,10 @@ resource "aci_application_epg" "full_example_application_profile" { vnet_only = "no" } ] - relation_to_data_plane_policing_policy = [ - { - annotation = "annotation_1" - data_plane_policing_policy_name = aci_data_plane_policing_policy.example.name - } - ] + relation_to_data_plane_policing_policy = { + annotation = "annotation_1" + data_plane_policing_policy_name = aci_data_plane_policing_policy.example.name + } relation_to_fibre_channel_paths = [ { annotation = "annotation_1" @@ -152,12 +142,10 @@ resource "aci_application_epg" "full_example_application_profile" { target_dn = aci_application_epg.test_application_epg_0.id } ] - relation_to_trust_control_policy = [ - { - annotation = "annotation_1" - trust_control_policy_name = aci_trust_control_policy.example.name - } - ] + relation_to_trust_control_policy = { + annotation = "annotation_1" + trust_control_policy_name = aci_trust_control_policy.example.name + } annotations = [ { key = "key_0" diff --git a/examples/resources/aci_endpoint_security_group/resource-all-attributes.tf b/examples/resources/aci_endpoint_security_group/resource-all-attributes.tf index f0840105a..8166af5b7 100644 --- a/examples/resources/aci_endpoint_security_group/resource-all-attributes.tf +++ b/examples/resources/aci_endpoint_security_group/resource-all-attributes.tf @@ -38,12 +38,10 @@ resource "aci_endpoint_security_group" "full_example_application_profile" { contract_name = aci_contract.example.name } ] - relation_to_vrf = [ - { - annotation = "annotation_1" - vrf_name = aci_vrf.example.name - } - ] + relation_to_vrf = { + annotation = "annotation_1" + vrf_name = aci_vrf.example.name + } relation_to_contract_masters = [ { annotation = "annotation_1" diff --git a/examples/resources/aci_netflow_exporter_policy/resource-all-attributes.tf b/examples/resources/aci_netflow_exporter_policy/resource-all-attributes.tf index 8cf1e63a4..6bf1e2e71 100644 --- a/examples/resources/aci_netflow_exporter_policy/resource-all-attributes.tf +++ b/examples/resources/aci_netflow_exporter_policy/resource-all-attributes.tf @@ -13,18 +13,14 @@ resource "aci_netflow_exporter_policy" "full_example_tenant" { source_ip_type = "custom-src-ip" source_ip_address = "1.1.1.1/10" version = "v9" - relation_to_vrf = [ - { - annotation = "annotation_1" - target_dn = aci_vrf.example.id - } - ] - relation_to_epg = [ - { - annotation = "annotation_1" - target_dn = aci_application_epg.example.id - } - ] + relation_to_vrf = { + annotation = "annotation_1" + target_dn = aci_vrf.example.id + } + relation_to_epg = { + annotation = "annotation_1" + target_dn = aci_application_epg.example.id + } annotations = [ { key = "key_0" diff --git a/examples/resources/aci_netflow_monitor_policy/resource-all-attributes.tf b/examples/resources/aci_netflow_monitor_policy/resource-all-attributes.tf index 949cb6054..199f3e68f 100644 --- a/examples/resources/aci_netflow_monitor_policy/resource-all-attributes.tf +++ b/examples/resources/aci_netflow_monitor_policy/resource-all-attributes.tf @@ -13,12 +13,10 @@ resource "aci_netflow_monitor_policy" "full_example_tenant" { netflow_exporter_policy_name = aci_netflow_exporter_policy.example.name } ] - relation_to_netflow_record = [ - { - annotation = "annotation_1" - netflow_record_policy_name = aci_netflow_record_policy.example.name - } - ] + relation_to_netflow_record = { + annotation = "annotation_1" + netflow_record_policy_name = aci_netflow_record_policy.example.name + } annotations = [ { key = "key_0" diff --git a/examples/resources/aci_vrf_fallback_route_group/resource-all-attributes.tf b/examples/resources/aci_vrf_fallback_route_group/resource-all-attributes.tf index 0e84430a3..002eca544 100644 --- a/examples/resources/aci_vrf_fallback_route_group/resource-all-attributes.tf +++ b/examples/resources/aci_vrf_fallback_route_group/resource-all-attributes.tf @@ -14,15 +14,13 @@ resource "aci_vrf_fallback_route_group" "full_example_vrf" { fallback_member = "2.2.2.2" } ] - vrf_fallback_routes = [ - { - annotation = "annotation_1" - description = "description_1" - prefix_address = "2.2.2.2/24" - name = "name_1" - name_alias = "name_alias_1" - } - ] + vrf_fallback_route = { + annotation = "annotation_1" + description = "description_1" + prefix_address = "2.2.2.2/24" + name = "name_1" + name_alias = "name_alias_1" + } annotations = [ { key = "key_0" diff --git a/gen/definitions/classes.yaml b/gen/definitions/classes.yaml index 5d1ee8fa7..0733b2f12 100644 --- a/gen/definitions/classes.yaml +++ b/gen/definitions/classes.yaml @@ -334,6 +334,10 @@ netflowMonitorPol: - "Tenants -> Policies -> NetFlow -> NetFlow Monitors" - "Fabric -> Access Policies -> Policies -> Interface -> NetFlow -> NetFlow Monitors" sub_category: "Tenant Policies" + type_changes: + - attribute: "netflowRsMonitorToRecord" + old_type: "SetNestedAttribute" + version: 0 netflowRsMonitorToRecord: resource_name: "relation_to_netflow_record" @@ -537,6 +541,12 @@ vzBrCP: vzTaboo: resource_name: "taboo_contract" +fabricPathEp: + resource_name: "fabric_path_endpoint" + +infraAccBaseGrp: + resource_name: "access_interface_policy_group" + fvCrtrn: resource_name: "epg_useg_block_statement" ui_locations: diff --git a/gen/generator.go b/gen/generator.go index 388376a17..de07f8ed0 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -125,6 +125,9 @@ var templateFuncs = template.FuncMap{ "containsRequired": ContainsRequired, "hasPrefix": strings.HasPrefix, "hasCustomTypeDocs": HasCustomTypeDocs, + "containsSingleNestedChildren": ContainsSingleNestedChildren, + "isResourceClass": IsResourceClass, + "getOldType": GetOldType, } func ContainsRequired(properties map[string]Property) bool { @@ -136,6 +139,15 @@ func ContainsRequired(properties map[string]Property) bool { return false } +func ContainsSingleNestedChildren(children map[string]Model) bool { + for _, child := range children { + if len(child.IdentifiedBy) == 0 || child.MaxOneClassAllowed { + return true + } + } + return false +} + func Replace(oldValue, newValue, inputString string) string { return strings.Replace(inputString, oldValue, newValue, -1) } @@ -249,7 +261,11 @@ var resourceNames = map[string]string{} var targetRelationalPropertyClasses = map[string]string{} var alwaysIncludeChildren = []string{"tag:Annotation", "tag:Tag"} var excludeChildResourceNamesFromDocs = []string{"", "annotation", "tag"} -var classesWithoutResource = []string{} +var classesWithoutResource = []string{"fabricPathEp"} + +func IsResourceClass(className string) bool { + return !slices.Contains(classesWithoutResource, className) +} func GetResourceNameAsDescription(s string, definitions Definitions) string { resourceName := cases.Title(language.English).String(strings.ReplaceAll(s, "_", " ")) @@ -753,9 +769,9 @@ func main() { childMap := make(map[string]Model, 0) for childName, childModel := range model.Children { childModel.ChildResourceName = GetResourceName(childModel.PkgName, definitions) - if len(childModel.IdentifiedBy) > 0 && !(strings.HasPrefix(childModel.RnFormat, "rs") && childModel.MaxOneClassAllowed) { + childModel.ResourceNameDocReference = childModel.ChildResourceName + if len(childModel.IdentifiedBy) > 0 && !childModel.MaxOneClassAllowed { // TODO add logic to determine the naming for plural child resources - childModel.ResourceNameDocReference = childModel.ChildResourceName childModel.ResourceName = fmt.Sprintf("%ss", childModel.ChildResourceName) } else { childModel.ResourceName = childModel.ChildResourceName @@ -869,6 +885,7 @@ type Model struct { DnFormats []interface{} Properties map[string]Property NamedProperties map[string]Property + RequiredPropertiesNames []string LegacyAttributes map[string]LegacyAttribute LegacyBlocks []LegacyBlock LegacySchemaVersion int @@ -879,6 +896,8 @@ type Model struct { TestVars map[string]interface{} Definitions Definitions ResourceNameAsDescription string + TypeChanges map[int][]TypeChange + SchemaVersion int // Below booleans are used during template rendering to determine correct rendering the go code AllowDelete bool AllowChildDelete bool @@ -898,6 +917,11 @@ type Model struct { HasCustomTypeProperties bool } +type TypeChange struct { + OldType string + Attribute string +} + type TestDependency struct { ClassName string ParentDependency string @@ -1053,6 +1077,46 @@ func (m *Model) setClassModel(metaPath string, child bool, definitions Definitio m.SetLegacyAttributes(definitions) } + m.SetStateUpgradeTypeChanges(definitions) + +} + +func (m *Model) SetStateUpgradeTypeChanges(definitions Definitions) { + if v, ok := definitions.Classes[m.PkgName]; ok { + for key, value := range v.(map[interface{}]interface{}) { + if key.(string) == "type_changes" { + m.TypeChanges = make(map[int][]TypeChange) + for _, value := range value.([]interface{}) { + var version int + change := TypeChange{} + for key, value := range value.(map[interface{}]interface{}) { + if key.(string) == "old_type" { + change.OldType = value.(string) + } + if key.(string) == "attribute" { + change.Attribute = value.(string) + } + if key.(string) == "version" { + version = value.(int) + } + } + m.TypeChanges[version] = append(m.TypeChanges[version], change) + if version >= m.SchemaVersion { + m.SchemaVersion = version + 1 + } + } + } + } + } +} + +func GetOldType(attributeName string, typeChanges []TypeChange) string { + for _, change := range typeChanges { + if change.Attribute == attributeName { + return change.OldType + } + } + return "" } func (m *Model) SetClassLabel(classDetails interface{}, child bool) { @@ -1373,6 +1437,7 @@ func (m *Model) SetClassProperties(classDetails interface{}) { if requiredProperty(GetOverwriteAttributeName(m.PkgName, propertyName, m.Definitions), m.PkgName, m.Definitions) || property.IsNaming { property.IsRequired = true requiredCount += 1 + m.RequiredPropertiesNames = append(m.RequiredPropertiesNames, GetOverwriteAttributeName(m.PkgName, property.SnakeCaseName, m.Definitions)) } if !property.IsRequired { diff --git a/gen/templates/datasource.go.tmpl b/gen/templates/datasource.go.tmpl index 212b3b2de..e7c2ddb4e 100644 --- a/gen/templates/datasource.go.tmpl +++ b/gen/templates/datasource.go.tmpl @@ -111,6 +111,26 @@ func (d *{{.ResourceClassName}}DataSource) Schema(ctx context.Context, req datas },{{- end}} {{- end}} {{- range .Children}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + "{{overwriteProperty .PkgName .ResourceName $.Definitions}}": schema.SingleNestedAttribute{ + MarkdownDescription: `{{.Comment}}`, + Computed: true, + Attributes: map[string]schema.Attribute{ + {{- range .Properties}} + {{- if eq .ValueType "bitmask"}} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": schema.SetAttribute{ + Computed: true, + MarkdownDescription: `{{.Comment}}`, + ElementType: types.StringType, + },{{else}} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `{{.Comment}}`, + },{{- end}} + {{- end}} + }, + }, + {{- else}} "{{overwriteProperty .PkgName .ResourceName $.Definitions}}": schema.SetNestedAttribute{ MarkdownDescription: `{{.Comment}}`, Computed: true, @@ -134,6 +154,7 @@ func (d *{{.ResourceClassName}}DataSource) Schema(ctx context.Context, req datas }, }, }, + {{- end}} {{- end}} }, {{- if .LegacyAttributes}} diff --git a/gen/templates/datasource.md.tmpl b/gen/templates/datasource.md.tmpl index 0ed8777e5..592152454 100644 --- a/gen/templates/datasource.md.tmpl +++ b/gen/templates/datasource.md.tmpl @@ -100,10 +100,18 @@ Data source for ACI {{.ResourceNameAsDescription}} {{- end}} {{- end}} {{- else}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} +* `{{- overwriteProperty .PkgName .ResourceName $.Definitions}}` - (map) A map of {{getResourceNameAsDescription .ResourceName $.Definitions}} (ACI object {{getDevnetDocForClass .PkgName}}) pointing to {{range $index, $value := .RelationshipResourceNames}}{{- if or (eq $length 1) (eq $.PkgName (index $RelationshipClasses $index))}}{{getResourceNameAsDescription $value $.Definitions}} (ACI Object {{getDevnetDocForClass (index $RelationshipClasses $index)}}){{end}}{{end}}.{{if and (ne $.Versions .Versions) (ne .Versions "")}} This attribute is supported in ACI versions: {{ .Versions}}{{- end}} + {{- else}} * `{{- overwriteProperty .PkgName .ResourceName $.Definitions}}` - (list) A list of {{getResourceNameAsDescription .ResourceName $.Definitions}} (ACI object {{getDevnetDocForClass .PkgName}}) pointing to {{range $index, $value := .RelationshipResourceNames}}{{- if or (eq $length 1) (eq $.PkgName (index $RelationshipClasses $index))}}{{getResourceNameAsDescription $value $.Definitions}} (ACI Object {{getDevnetDocForClass (index $RelationshipClasses $index)}}){{end}}{{end}}.{{if and (ne $.Versions .Versions) (ne .Versions "")}} This attribute is supported in ACI versions: {{ .Versions}}{{- end}} {{- end}} + {{- end}} {{- else}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} +* `{{- overwriteProperty .PkgName .ResourceName $.Definitions}}` - (map) A map of {{getResourceNameAsDescription .ResourceName $.Definitions}} (ACI object {{getDevnetDocForClass .PkgName}}).{{if and (ne $.Versions .Versions) (ne .Versions "")}} This attribute is supported in ACI versions: {{ .Versions}}{{- end}} + {{- else}} * `{{- overwriteProperty .PkgName .ResourceName $.Definitions}}` - (list) A list of {{getResourceNameAsDescription .ResourceName $.Definitions}} (ACI object {{getDevnetDocForClass .PkgName}}).{{if and (ne $.Versions .Versions) (ne .Versions "")}} This attribute is supported in ACI versions: {{ .Versions}}{{- end}} + {{- end}} {{- end}} {{- range .Properties}} diff --git a/gen/templates/resource.go.tmpl b/gen/templates/resource.go.tmpl index 7f1551762..778444608 100644 --- a/gen/templates/resource.go.tmpl +++ b/gen/templates/resource.go.tmpl @@ -8,41 +8,26 @@ import ( "context" "fmt" "encoding/json" - {{- if .IdentifiedBy}} "reflect" - {{- end}} "strings" "github.com/ciscoecosystem/aci-go-client/v2/client" "github.com/ciscoecosystem/aci-go-client/v2/container" - {{- if .HasCustomTypeProperties}} - "github.com/CiscoDevNet/terraform-provider-aci/v2/internal/validators" - customTypes "github.com/CiscoDevNet/terraform-provider-aci/v2/internal/custom_types" - {{- end}} - {{- if or .HasBitmask .HasChildWithoutIdentifier}} + "github.com/CiscoDevNet/terraform-provider-aci/v2/internal/validators" + customTypes "github.com/CiscoDevNet/terraform-provider-aci/v2/internal/custom_types" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" - {{- end}} - {{- if .HasValidValues}} "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - {{- end}} - {{- if .LegacyAttributes}} "github.com/hashicorp/terraform-plugin-framework/attr" - {{- end}} "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" - {{- if or .HasAnnotation}} "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - {{- end}} "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - {{- if or .HasChild .HasBitmask}} "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" - {{- end}} "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - {{- if or .HasValidValues .HasChildWithoutIdentifier}} "github.com/hashicorp/terraform-plugin-framework/schema/validator" - {{- end}} "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-log/tflog" @@ -79,7 +64,11 @@ type {{.ResourceClassName}}ResourceModel struct { {{- end}} {{- end}} {{- range .Children}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + {{ .ResourceClassName }} types.Object `tfsdk:"{{overwriteProperty .PkgName .ResourceName $.Definitions}}"` + {{- else}} {{ .ResourceClassName }} types.Set `tfsdk:"{{overwriteProperty .PkgName .ResourceName $.Definitions}}"` + {{- end}} {{- end}} {{- if .LegacyAttributes}} {{- range .LegacyAttributes}}{{- if ne .ReplacedBy.AttributeName "" }} @@ -109,6 +98,17 @@ func getEmpty{{.ResourceClassName}}ResourceModel() *{{.ResourceClassName}}Resour {{- end}} {{- end}} {{- range .Children}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + {{ .ResourceClassName }}: types.ObjectNull(map[string]attr.Type{ + {{- range .Properties}} + {{- if eq .ValueType "bitmask"}} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": types.SetType(types.StringType), + {{- else}} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": types.StringType, + {{- end}} + {{- end}} + }), + {{- else}} {{ .ResourceClassName }}: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ {{- range .Properties}} @@ -120,6 +120,7 @@ func getEmpty{{.ResourceClassName}}ResourceModel() *{{.ResourceClassName}}Resour {{- end}} }, }), + {{- end}} {{- end}} {{- if .LegacyAttributes}} {{- range .LegacyAttributes}}{{- if ne .ReplacedBy.AttributeName "" }} @@ -167,6 +168,17 @@ func getEmpty{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel() {{.Res {{- end}} } } + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} +var {{.ResourceClassName}}{{$.ResourceClassName}}Type = map[string]attr.Type{ + {{- range .Properties}} + {{- if eq .ValueType "bitmask"}} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": types.SetType(types.StringType), + {{- else}} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": types.StringType, + {{- end}} + {{- end}} +} + {{- end}} {{- end}} {{if .IdentifiedBy}} @@ -312,109 +324,135 @@ func (r *{{.ResourceClassName}}Resource) UpgradeState(ctx context.Context) map[i {{ range .Children}} {{- if isLegacyChild .PkgName $.LegacyChildren}}{{ $PkgName := .PkgName}} - {{.ResourceClassName}}List := make([]{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel, 0) {{- $childClassType := getValueFromMap .PkgName $.MigrationClassTypes}} - {{- if eq $childClassType "string" }} - {{.ResourceClassName}} := {{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel{ - {{- range .Properties}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + {{- if eq $childClassType "string" }} + {{.ResourceClassName}}Object := {{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel{ + {{- range .Properties}} {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} {{- $AttributeName := getLegacyChildAttribute $PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} - {{- if eq $AttributeName ""}} + {{- if eq $AttributeName ""}} {{ .Name }}: basetypes.NewStringNull(), - {{- else}} - {{- if isNewNamedClassAttribute $Overwrite }} + {{- else}} + {{- if isNewNamedClassAttribute $Overwrite }} {{ .Name }}: basetypes.NewStringValue(GetMOName(priorStateData.{{ $AttributeName }}.ValueString())), + {{- else}} + {{ .Name }}: priorStateData.{{ $AttributeName }}, + {{- end}} + {{- end}} + {{- end}} + } + {{ .PkgName }}Object, _ := types.ObjectValueFrom(ctx, {{ .ResourceClassName }}{{$.ResourceClassName}}Type, {{.ResourceClassName}}Object) + upgradedStateData.{{ .ResourceClassName }} = {{ .PkgName }}Object + {{- end }} + {{- else}} + {{.ResourceClassName}}List := make([]{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel, 0) + {{- if eq $childClassType "string" }} + {{.ResourceClassName}} := {{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel{ + {{- range .Properties}} + {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} + {{- $AttributeName := getLegacyChildAttribute $PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} + {{- if eq $AttributeName ""}} + {{ .Name }}: basetypes.NewStringNull(), {{- else}} + {{- if isNewNamedClassAttribute $Overwrite }} + {{ .Name }}: basetypes.NewStringValue(GetMOName(priorStateData.{{ $AttributeName }}.ValueString())), + {{- else}} {{ .Name }}: priorStateData.{{ $AttributeName }}, + {{- end}} {{- end}} {{- end}} - {{- end}} } {{.ResourceClassName}}List = append({{.ResourceClassName}}List, {{.ResourceClassName}}) {{.ResourceClassName}}Type := types.ObjectType{ AttrTypes: map[string]attr.Type{ - {{- range .Properties}} + {{- range .Properties}} {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} "{{$Overwrite}}": basetypes.StringType{}, - {{- end}} + {{- end}} }, } {{.ResourceClassName}}Set, _ := types.SetValueFrom(ctx, {{.ResourceClassName}}Type, {{.ResourceClassName}}List) upgradedStateData.{{.ResourceClassName}} = {{.ResourceClassName}}Set - {{- else if eq $childClassType "block,set" }} + {{- else if eq $childClassType "block,set" }} var priorStateData{{.ResourceClassName}}List []{{ .ResourceClassName }}{{$.ResourceClassName}}ResourceModelV{{$.LegacySchemaVersion}} priorStateData.{{.ResourceClassName}}.ElementsAs(ctx, &priorStateData{{.ResourceClassName}}List, false) for _, priorStateData{{.ResourceClassName}} := range priorStateData{{.ResourceClassName}}List { {{ .ResourceClassName }} := {{ .ResourceClassName }}{{$.ResourceClassName}}ResourceModel { - {{- range .Properties}} + {{- range .Properties}} {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} {{- $AttributeName := getLegacyChildAttribute $PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} - {{- if eq $AttributeName ""}} + {{- if eq $AttributeName ""}} {{ .Name }}: basetypes.NewStringNull(), - {{- else if .HasCustomType}} + {{- else if .HasCustomType}} {{ .Name }}: customTypes.{{.ResourceClassName}}{{.Name}}StringValue{StringValue: priorStateData{{.ResourceClassName}}.{{ $AttributeName }}}, - {{- else}} + {{- else}} {{ .Name }}: priorStateData{{.ResourceClassName}}.{{ $AttributeName }}, + {{- end}} {{- end}} - {{- end}} } {{.ResourceClassName}}List = append({{.ResourceClassName}}List, {{ .ResourceClassName }}) } {{.ResourceClassName}}Type := types.ObjectType{ AttrTypes: map[string]attr.Type{ - {{- range .Properties}} + {{- range .Properties}} {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} "{{$Overwrite}}": basetypes.StringType{}, - {{- end}} + {{- end}} }, } {{.ResourceClassName}}Set, _ := types.SetValueFrom(ctx, {{.ResourceClassName}}Type, {{.ResourceClassName}}List) upgradedStateData.{{.ResourceClassName}} = {{.ResourceClassName}}Set - {{- else if eq $childClassType "set,string" }} + {{- else if eq $childClassType "set,string" }} var priorStateData{{.ResourceClassName}}List []string priorStateData.{{.ResourceClassName}}.ElementsAs(ctx, &priorStateData{{.ResourceClassName}}List, false) for _, priorStateData{{.ResourceClassName}} := range priorStateData{{.ResourceClassName}}List { {{ .ResourceClassName }} := {{ .ResourceClassName }}{{$.ResourceClassName}}ResourceModel { - {{- range .Properties}} - {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} - {{- $AttributeName := getLegacyChildAttribute $PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} - {{- if eq $AttributeName ""}} + {{- range .Properties}} + {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} + {{- $AttributeName := getLegacyChildAttribute $PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} + {{- if eq $AttributeName ""}} {{ .Name }}: basetypes.NewStringNull(), - {{- else}} - {{- if isNewNamedClassAttribute $Overwrite }} + {{- else}} + {{- if isNewNamedClassAttribute $Overwrite }} {{ .Name }}: basetypes.NewStringValue(GetMOName(priorStateData{{ .ResourceClassName }})), - {{- else if eq .Name "TDn"}} + {{- else if eq .Name "TDn"}} {{ .Name }}: basetypes.NewStringValue(priorStateData{{ .ResourceClassName }}), + {{- end}} + {{- end}} {{- end}} - {{- end}} - {{- end}} } {{.ResourceClassName}}List = append({{.ResourceClassName}}List, {{ .ResourceClassName }}) } {{.ResourceClassName}}Type := types.ObjectType{ AttrTypes: map[string]attr.Type{ - {{- range .Properties}} - {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} + {{- range .Properties}} + {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} "{{$Overwrite}}": basetypes.StringType{}, - {{- end}} + {{- end}} }, } {{.ResourceClassName}}Set, _ := types.SetValueFrom(ctx, {{.ResourceClassName}}Type, {{.ResourceClassName}}List) upgradedStateData.{{.ResourceClassName}} = {{.ResourceClassName}}Set + {{- end}} {{- end}} - {{ else}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + // {{ .ResourceClassName }} is a child of {{ $.ResourceClassName }}. + upgradedStateData.{{ .ResourceClassName }} = types.ObjectNull({{ .ResourceClassName }}{{$.ResourceClassName}}Type) + {{- else}} upgradedStateData.{{ .ResourceClassName }} = types.SetNull( types.ObjectType{ AttrTypes: map[string]attr.Type{ {{- range .Properties}} {{- if ne .ValueType "bitmask"}} - "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": basetypes.StringType{}, + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": basetypes.StringType{}, {{- end}} {{- end}} }, }, ) + {{- end}} {{ end}} {{- end}} @@ -537,10 +575,10 @@ func set{{ .ResourceClassName }}LegacyAttributes(ctx context.Context, diags *dia func (r *{{.ResourceClassName}}Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { if !req.Plan.Raw.IsNull() { - var planData, stateData{{ if .LegacyAttributes}}, configData{{end}} *{{.ResourceClassName}}ResourceModel + var planData, stateData{{ if or .LegacyAttributes (containsSingleNestedChildren .Children)}}, configData{{end}} *{{.ResourceClassName}}ResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &planData)...) resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) - {{ if .LegacyAttributes}}resp.Diagnostics.Append(req.Config.Get(ctx, &configData)...){{end}} + {{ if or .LegacyAttributes (containsSingleNestedChildren .Children)}}resp.Diagnostics.Append(req.Config.Get(ctx, &configData)...){{end}} if resp.Diagnostics.HasError() { return @@ -557,6 +595,19 @@ func (r *{{.ResourceClassName}}Resource) ModifyPlan(ctx context.Context, req res } } + {{- if containsSingleNestedChildren .Children}} + {{- range $.Children}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + if !configData.{{ .ResourceClassName }}.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.{{ .ResourceClassName }}.Attributes()) { + {{.ResourceClassName}}Object, _ := types.ObjectValueFrom(ctx, {{.ResourceClassName}}{{$.ResourceClassName}}Type, getEmpty{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel()) + planData.{{.ResourceClassName}} = {{.ResourceClassName}}Object + } + } + + {{- end}} + {{- end}} + {{- end}} {{ if .LegacyAttributes}}{{ $ResourceClassName := .ResourceClassName}} {{ range .LegacyAttributes}}{{$SetName := .Name}} {{- if and (ne .ReplacedBy.AttributeName "") (eq (getMigrationType .ValueType) "String") (isNewAttributeStringType .ReplacedBy.AttributeName) }} @@ -592,44 +643,83 @@ func (r *{{.ResourceClassName}}Resource) ModifyPlan(ctx context.Context, req res {{- end}} {{- else if and (ne .ReplacedBy.AttributeName "") (eq (getMigrationType .ValueType) "String") }} {{- range $.Children}} - {{- if isLegacyChild .PkgName $.LegacyChildren}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} {{- if eq $SetName (capitalize .PkgName)}} + if !configData.{{ $SetName }}.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.{{ $SetName }}.Attributes()) { + planData.{{ $SetName }} = configData.{{ $SetName }} + planData.Deprecated{{ $SetName }} = basetypes.NewStringNull() + } else { + var attributeValues {{$SetName}}{{$.ResourceClassName}}ResourceModel + configData.{{ $SetName }}.As(ctx, &attributeValues, basetypes.ObjectAsOptions{}) + {{- range .Properties}} + {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} + {{- $AttributeName := getLegacyChildAttribute .PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} + {{- if ne $AttributeName ""}} + if GetMOName(stateData.Deprecated{{$SetName}}.ValueString()) == attributeValues.{{ .Name }}.ValueString() && !attributeValues.{{ .Name }}.IsNull() { + planData.Deprecated{{$SetName}} = stateData.Deprecated{{$SetName}} + } + } + {{- end}} + {{- end}} + } else if !configData.Deprecated{{ $SetName }}.IsNull() { + {{$SetName}} := {{$SetName}}{{$.ResourceClassName}}ResourceModel{ + {{- range .Properties}} + {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} + {{- $AttributeName := getLegacyChildAttribute .PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} + {{- if eq .Name "Annotation"}} + {{ .Name }}: planData.Annotation, + {{- else if eq $AttributeName ""}} + {{ .Name }}: basetypes.NewStringNull(), + {{- else}} + {{ .Name }}: {{- if isNewNamedClassAttribute $Overwrite }}basetypes.NewStringValue(GetMOName(configData.Deprecated{{ $AttributeName }}.ValueString())){{- else }}configData.Deprecated{{ .Name }}{{- end }}, + {{- end}} + {{- end}} + } + {{$SetName}}Object, _ := types.ObjectValueFrom(ctx, {{$SetName}}{{$.ResourceClassName}}Type, {{$SetName}}) + planData.{{$SetName}} = {{$SetName}}Object + } else if stateData != nil { // used to replace use state for unknown + planData.Deprecated{{$SetName}} = stateData.Deprecated{{$SetName}} + } {{- end}} + {{- else}} + {{- if isLegacyChild .PkgName $.LegacyChildren}} + {{- if eq $SetName (capitalize .PkgName)}} if !configData.{{ $SetName }}.IsNull() && stateData != nil { var attributeValues []{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel configData.{{.ResourceClassName}}.ElementsAs(ctx, &attributeValues, false) for _, attributeValue := range attributeValues { - {{- range .Properties}} - {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} - {{- $AttributeName := getLegacyChildAttribute .PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} - {{- if ne $AttributeName ""}} + {{- range .Properties}} + {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} + {{- $AttributeName := getLegacyChildAttribute .PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} + {{- if ne $AttributeName ""}} if GetMOName(stateData.Deprecated{{.ResourceClassName}}.ValueString()) == attributeValue.{{ .Name }}.ValueString() { planData.Deprecated{{.ResourceClassName}} = stateData.Deprecated{{.ResourceClassName}} } - {{- end}} - {{- end}} + {{- end}} + {{- end}} } } else if !configData.Deprecated{{ $SetName }}.IsNull() { {{.ResourceClassName}}List := make([]{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel, 0) {{.ResourceClassName}} := {{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel{ - {{- range .Properties}} - {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} - {{- $AttributeName := getLegacyChildAttribute .PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} - {{- if eq .Name "Annotation"}} + {{- range .Properties}} + {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} + {{- $AttributeName := getLegacyChildAttribute .PkgName $Overwrite . $.LegacyAttributes $.LegacyBlocks}} + {{- if eq .Name "Annotation"}} {{ .Name }}: planData.Annotation, - {{- else if eq $AttributeName ""}} + {{- else if eq $AttributeName ""}} {{ .Name }}: basetypes.NewStringNull(), - {{- else}} + {{- else}} {{ .Name }}: {{- if isNewNamedClassAttribute $Overwrite }}basetypes.NewStringValue(GetMOName(configData.Deprecated{{ $AttributeName }}.ValueString())){{- else }}configData.Deprecated{{ .Name }}{{- end }}, - {{- end}} - {{- end}} + {{- end}} + {{- end}} } {{.ResourceClassName}}List = append({{.ResourceClassName}}List, {{.ResourceClassName}}) {{.ResourceClassName}}Type := types.ObjectType{ AttrTypes: map[string]attr.Type{ - {{- range .Properties}} + {{- range .Properties}} {{- $Overwrite := overwriteProperty .PkgName .SnakeCaseName $.Definitions}} "{{$Overwrite}}": basetypes.StringType{}, - {{- end}} + {{- end}} }, } {{.ResourceClassName}}Set, _ := types.SetValueFrom(ctx, {{.ResourceClassName}}Type, {{.ResourceClassName}}List) @@ -637,6 +727,7 @@ func (r *{{.ResourceClassName}}Resource) ModifyPlan(ctx context.Context, req res } else if stateData != nil { // used to replace use state for unknown planData.Deprecated{{.ResourceClassName}} = stateData.Deprecated{{.ResourceClassName}} } + {{- end }} {{- end }} {{- end }} {{- end }} @@ -644,7 +735,6 @@ func (r *{{.ResourceClassName}}Resource) ModifyPlan(ctx context.Context, req res {{- range $.Children}} {{- if isLegacyChild .PkgName $.LegacyChildren}} {{- if eq $SetName (capitalize .PkgName)}} - // HasNamedProperties {{.HasNamedProperties}} if !configData.{{ $SetName }}.IsNull() && stateData != nil { var attributeValues []{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel var newAttributeValues, stateAttributeValues []string @@ -844,6 +934,169 @@ func (r *{{.ResourceClassName}}Resource) ModifyPlan(ctx context.Context, req res resp.Diagnostics.Append(resp.Plan.Set(ctx, &planData)...) } } +{{- if .TypeChanges}} + {{- range $version, $value := .TypeChanges}} +type {{$.ResourceClassName}}ResourceModelV{{$version}} struct { + Id types.String `tfsdk:"id"` + {{- if $.HasParent}} + ParentDn types.String `tfsdk:"parent_dn"` + {{- end}} + {{- range $.Properties}}{{$oldType := getOldType .PropertyName $value}} + {{- if ne $oldType ""}} + // {{.PropertyName}} + {{- else if eq .ValueType "bitmask"}} + {{ .Name }} types.Set `tfsdk:"{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}"` + {{- else if eq .Name "Id"}} + {{.ResourceClassName}}{{ .Name }} types.String `tfsdk:"{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}"` + {{- else}} + {{ .Name }} types.String `tfsdk:"{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}"` + {{- end}} + {{- end}} + {{- range $.Children}}{{$oldType := getOldType .PkgName $value}} + {{- if ne $oldType ""}} + {{- if eq $oldType "SetNestedAttribute"}} + {{ .ResourceClassName }} types.Set `tfsdk:"{{- .ResourceName}}"` + {{- end}} + {{- else if or (not .IdentifiedBy) .MaxOneClassAllowed}} + {{ .ResourceClassName }} types.Object `tfsdk:"{{- .ResourceName}}"` + {{- else}} + {{ .ResourceClassName }} types.Set `tfsdk:"{{- .ResourceName}}"` + {{- end}} + {{- end}} +} + {{- end}} + +func (r *{{.ResourceClassName}}Resource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { + return map[int64]resource.StateUpgrader{ + {{- range $version, $value := .TypeChanges}} + {{$version}}: { + PriorSchema: &schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + {{- if $.HasParent}} + "parent_dn": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + {{- end}} + {{- range $.Properties}}{{$oldType := getOldType .PropertyName $value}} + {{- if ne $oldType ""}} + // {{.PropertyName}} // Add logic when needed, current no type change on properties + {{- else }} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": schema.StringAttribute{ + Required: {{- if or .IsNaming .IsRequired}}true{{else}}false{{end}}, + Optional: {{- if not .ReadOnly}}true{{else}}false{{end}}, + Computed: true, + }, + {{- end}} + {{- end}} + {{- range $.Children}}{{$oldType := getOldType .PkgName $value}} + {{- if ne $oldType ""}} + {{- if eq $oldType "SetNestedAttribute"}} + "{{- .ResourceName}}": schema.SetNestedAttribute{ + Required: false, + Optional: true, + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + {{- range .Properties}}{{$oldType := getOldType .PropertyName $value}} + {{- if ne $oldType ""}} + // {{.PropertyName}} // Add logic when needed, current no type change on properties + {{- else }} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": schema.StringAttribute{ + Required: {{- if or .IsNaming .IsRequired}}true{{else}}false{{end}}, + Optional: {{- if or .IsNaming .IsRequired}}false{{else}}true{{end}}, + Computed: {{- if or .IsNaming .IsRequired}}false{{else}}true{{end}}, + }, + {{- end}} + {{- end}} + }, + }, + }, + {{- end}} + {{- else if or (not .IdentifiedBy) .MaxOneClassAllowed}} + // "{{- .ResourceName}}" map + {{- else}} + "{{- .ResourceName}}": schema.SetNestedAttribute{ + Required: false, + Optional: true, + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + {{- range .Properties}}{{$oldType := getOldType .PropertyName $value}} + {{- if ne $oldType ""}} + // {{.PropertyName}} // Add logic when needed, current no type change on properties + {{- else }} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": schema.StringAttribute{ + Required: {{- if or .IsNaming .IsRequired}}true{{else}}false{{end}}, + Optional: {{- if or .IsNaming .IsRequired}}false{{else}}true{{end}}, + Computed: {{- if or .IsNaming .IsRequired}}false{{else}}true{{end}}, + }, + {{- end}} + {{- end}} + }, + }, + }, + {{- end}} + {{- end}} + }, + }, + StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + var priorStateData {{$.ResourceClassName}}ResourceModelV{{$version}} + + resp.Diagnostics.Append(req.State.Get(ctx, &priorStateData)...) + + if resp.Diagnostics.HasError() { + return + } + + upgradedStateData := {{$.ResourceClassName}}ResourceModel{ + Id: priorStateData.Id, + {{- if $.HasParent}} + ParentDn: priorStateData.ParentDn, + {{- end}} + {{- range $.Properties}}{{$oldType := getOldType .PropertyName $value}} + {{- if ne $oldType ""}} + // {{.PropertyName}} // Add logic when needed, current no type change on properties + {{- else }} + {{ .Name }}: priorStateData.{{ .Name }}, + {{- end}} + {{- end}} + {{- range $.Children}}{{$oldType := getOldType .PkgName $value}} + {{- if ne $oldType ""}} + {{.ResourceClassName}}: types.ObjectNull({{.ResourceClassName}}{{$.ResourceClassName}}Type), + {{- else }} + {{ .ResourceClassName }}: priorStateData.{{ .ResourceClassName }}, + {{- end}} + {{- end}} + } + + {{- range $.Children}}{{$oldType := getOldType .PkgName $value}} + {{- if ne $oldType ""}} + + var {{.ResourceClassName}}{{$.ResourceClassName}}List []{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel + priorStateData.{{.ResourceClassName}}.ElementsAs(ctx, &{{.ResourceClassName}}{{$.ResourceClassName}}List, false) + + if len({{.ResourceClassName}}{{$.ResourceClassName}}List) > 0 { + {{.ResourceClassName}}Object, _ := types.ObjectValueFrom(ctx, {{.ResourceClassName}}{{$.ResourceClassName}}Type, {{.ResourceClassName}}{{$.ResourceClassName}}List[0]) + upgradedStateData.{{.ResourceClassName}} = {{.ResourceClassName}}Object + } + {{- end}} + {{- end}} + + resp.Diagnostics.Append(resp.State.Set(ctx, upgradedStateData)...) + }, + }, + {{- 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}}") @@ -861,6 +1114,9 @@ func (r *{{.ResourceClassName}}Resource) Schema(ctx context.Context, req resourc {{- else if .LegacySchemaVersion}} Version: {{.LegacySchemaVersion}}, {{- end}} + {{- if .TypeChanges}} + Version: {{.SchemaVersion}}, + {{- end}} Attributes: map[string]schema.Attribute{ {{- if .LegacyAttributes}} @@ -1024,6 +1280,69 @@ func (r *{{.ResourceClassName}}Resource) Schema(ctx context.Context, req resourc {{- end}} {{- end}} {{- range .Children}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + "{{overwriteProperty .PkgName .ResourceName $.Definitions}}": schema.SingleNestedAttribute{ + MarkdownDescription: `{{.Comment}}`, + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + {{- if .RequiredPropertiesNames }} + Validators: []validator.Object{ + MakeSingleNestedAttributeRequiredAttributesNotProvidedValidator("{{.ResourceName}}", []string{"{{listToString .RequiredPropertiesNames}}"}), + }, + {{- end }} + Attributes: map[string]schema.Attribute{ + {{- range .Properties}} + {{- if eq .ValueType "bitmask"}} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": schema.SetAttribute{ + MarkdownDescription: `{{.Comment}}`, + {{- if not .ReadOnly }} + Optional: true, + {{- end }} + Computed: true, + {{- if not .ReadOnly }} + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, + {{- end }} + {{- if .ValidValues}} + Validators: []validator.Set{ + setvalidator.SizeAtMost({{ len .ValidValues }}), + setvalidator.ValueStringsAre( + stringvalidator.OneOf({{- validatorString .ValidValues}}), + ), + }, + {{- end}} + ElementType: types.StringType, + }, + {{- else}} + "{{overwriteProperty .PkgName .SnakeCaseName $.Definitions}}": schema.StringAttribute{ + Optional: true, + Computed: true, + {{- if not .ReadOnly }} + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + {{- end}} + {{- if or .ValidValues $.HasReadOnlyProperties}} + Validators: []validator.String{ + {{- if .ValidValues}} + stringvalidator.OneOf({{- validatorString .ValidValues}}), + {{- end}} + {{- if or .IsNaming .IsRequired}} + MakeStringRequired(), + {{- end}} + }, + {{- end}} + MarkdownDescription: `{{.Comment}}`, + }, + {{- end}} + {{- end}} + }, + }, + {{- else }} "{{overwriteProperty .PkgName .ResourceName $.Definitions}}": schema.SetNestedAttribute{ MarkdownDescription: `{{.Comment}}`, Optional: true, @@ -1031,11 +1350,6 @@ func (r *{{.ResourceClassName}}Resource) Schema(ctx context.Context, req resourc PlanModifiers: []planmodifier.Set{ setplanmodifier.UseStateForUnknown(), }, - {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), - }, - {{- end}} NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ {{- range .Properties}} @@ -1102,6 +1416,7 @@ func (r *{{.ResourceClassName}}Resource) Schema(ctx context.Context, req resourc }, }, {{- end}} + {{- end}} }, {{- if .LegacyAttributes}} Blocks: map[string]schema.Block{ @@ -1216,9 +1531,15 @@ func (r *{{.ResourceClassName}}Resource) Create(ctx context.Context, req resourc {{ if .HasChild}} {{- range .Children}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + var {{.PkgName}}Plan, {{.PkgName}}State {{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel + data.{{ .ResourceClassName }}.As(ctx, &{{.PkgName}}Plan, basetypes.ObjectAsOptions{}) + stateData.{{ .ResourceClassName }}.As(ctx, &{{.PkgName}}State, basetypes.ObjectAsOptions{}) + {{- else}} var {{.PkgName}}Plan, {{.PkgName}}State []{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel data.{{ .ResourceClassName }}.ElementsAs(ctx, &{{.PkgName}}Plan, false) stateData.{{ .ResourceClassName }}.ElementsAs(ctx, &{{.PkgName}}State, false) + {{- end}} {{- end}} jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, true, data{{- range .Children}}, {{.PkgName}}Plan, {{.PkgName}}State{{- end}}) {{- else}} @@ -1278,6 +1599,17 @@ func (r *{{.ResourceClassName}}Resource) Update(ctx context.Context, req resourc resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) {{- if .HasChild}} resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + {{- range .Children}} + {{- if and (or (not .IdentifiedBy) .MaxOneClassAllowed) (not .AllowDelete)}} + // Error out when child object {{.PkgName}} is being deleted + if IsEmptySingleNestedAttribute(data.{{ .ResourceClassName }}.Attributes()) && !IsEmptySingleNestedAttribute(stateData.{{ .ResourceClassName }}.Attributes()) { + resp.Diagnostics.AddError( + "{{ .ResourceClassName }} object cannot be deleted", + "deletion of child is only possible upon deletion of the parent", + ) + } + {{- end}} + {{- end}} {{- end}} if resp.Diagnostics.HasError() { @@ -1288,9 +1620,15 @@ func (r *{{.ResourceClassName}}Resource) Update(ctx context.Context, req resourc {{ if .HasChild}} {{- range .Children}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + var {{.PkgName}}Plan, {{.PkgName}}State {{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel + data.{{ .ResourceClassName }}.As(ctx, &{{.PkgName}}Plan, basetypes.ObjectAsOptions{}) + stateData.{{ .ResourceClassName }}.As(ctx, &{{.PkgName}}State, basetypes.ObjectAsOptions{}) + {{- else}} var {{.PkgName}}Plan, {{.PkgName}}State []{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel data.{{ .ResourceClassName }}.ElementsAs(ctx, &{{.PkgName}}Plan, false) stateData.{{ .ResourceClassName }}.ElementsAs(ctx, &{{.PkgName}}State, false) + {{- end}} {{- end}} jsonPayload := get{{.ResourceClassName}}CreateJsonPayload(ctx, &resp.Diagnostics, false, data{{- range .Children}}, {{.PkgName}}Plan, {{.PkgName}}State{{- end}}) {{- else}} @@ -1473,8 +1811,18 @@ func getAndSet{{.ResourceClassName}}Attributes(ctx context.Context, diags *diag. } } {{- range .Children}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + if len({{.ResourceClassName}}{{$.ResourceClassName}}List) == 1 { + {{ .PkgName }}Object, _ := types.ObjectValueFrom(ctx, {{ .ResourceClassName }}{{$.ResourceClassName}}Type, {{ .ResourceClassName }}{{$.ResourceClassName}}List[0]) + data.{{ .ResourceClassName }} = {{ .PkgName }}Object + } else { + {{ .PkgName }}Object, _ := types.ObjectValueFrom(ctx, {{ .ResourceClassName }}{{$.ResourceClassName}}Type, getEmpty{{ .ResourceClassName }}{{$.ResourceClassName}}ResourceModel()) + data.{{ .ResourceClassName }} = {{ .PkgName }}Object + } + {{- else}} {{ .PkgName }}Set, _ := types.SetValueFrom(ctx, data.{{ .ResourceClassName }}.ElementType(ctx), {{ .ResourceClassName }}{{$.ResourceClassName}}List) data.{{ .ResourceClassName }} = {{ .PkgName }}Set + {{- end}} {{- end}} {{- end}} {{- if .LegacyAttributes}} @@ -1539,51 +1887,79 @@ func set{{.ResourceClassName}}Id(ctx context.Context, data *{{.ResourceClassName } {{ range .Children}} -func get{{$.ResourceClassName}}{{ .ResourceClassName }}ChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *{{$.ResourceClassName}}ResourceModel, {{.PkgName}}Plan, {{.PkgName}}State []{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel) []map[string]interface{} { +func get{{$.ResourceClassName}}{{ .ResourceClassName }}ChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *{{$.ResourceClassName}}ResourceModel, {{.PkgName}}Plan, {{.PkgName}}State {{if not ( or (not .IdentifiedBy) .MaxOneClassAllowed)}}[]{{end}}{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.{{ .ResourceClassName }}.IsUnknown() { - {{- if and .IdentifiedBy (not (and .MaxOneClassAllowed (hasPrefix .RnFormat "rs")))}} + {{- if and (and .IdentifiedBy (not (and .MaxOneClassAllowed (hasPrefix .RnFormat "rs")))) (not (or (not .IdentifiedBy) .MaxOneClassAllowed))}} {{ .PkgName }}Identifiers := []{{ .ResourceClassName }}Identifier{} {{- end}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.{{ .ResourceClassName }}.Attributes()) { + {{- range .Properties}} + if !{{ .PkgName }}Plan.{{- if eq .Name "Id"}}{{.ResourceClassName}}{{ .Name }}{{- else}}{{.Name}}{{- end}}.IsUnknown() && !{{ .PkgName }}Plan.{{- if eq .Name "Id"}}{{.ResourceClassName}}{{ .Name }}{{- else}}{{.Name}}{{- end}}.IsNull(){ + {{- if eq .ValueType "bitmask"}} + var tmp{{ .Name }} []string + {{ .PkgName }}.{{ .Name }}.ElementsAs(ctx, &tmp{{ .Name }}, false) + childMap["attributes"]["{{ .PropertyName }}"] = strings.Join(tmp{{ .Name }}, ",") + {{- else if eq .Name "Id"}} + childMap["attributes"]["{{ .PropertyName }}"] = {{ .PkgName }}Plan.{{.ResourceClassName}}{{ .Name }}.ValueString() + {{- else}} + childMap["attributes"]["{{ .PropertyName }}"] = {{ .PkgName }}Plan.{{ .Name }}.ValueString() + {{- end}} + } {{- if eq .Name "Annotation" }} else { + childMap["attributes"]["annotation"] = globalAnnotation + } {{- end}} + {{- end}} + }{{- if .AllowDelete}} else { + {{- range .Properties}} + {{- if .IsNaming }} + childMap["attributes"]["{{.PropertyName}}"] = {{.PkgName}}State.{{- if eq .Name "Id"}}{{.ResourceClassName}}{{ .Name }}{{- else}}{{.Name}}{{- end}}.ValueString() + {{- end}} + {{- end}} + childMap["attributes"]["status"] = "deleted" + }{{- end}} + childPayloads = append(childPayloads, map[string]interface{}{"{{ .PkgName }}": childMap}) + {{- else}} for _, {{ .PkgName }} := range {{ .PkgName }}Plan { childMap := map[string]map[string]interface{}{"attributes": {}} - {{- range .Properties}} + {{- range .Properties}} if !{{ .PkgName }}.{{- if eq .Name "Id"}}{{.ResourceClassName}}{{ .Name }}{{- else}}{{.Name}}{{- end}}.IsUnknown() && !{{ .PkgName }}.{{- if eq .Name "Id"}}{{.ResourceClassName}}{{ .Name }}{{- else}}{{.Name}}{{- end}}.IsNull(){ - {{- if eq .ValueType "bitmask"}} + {{- if eq .ValueType "bitmask"}} var tmp{{ .Name }} []string {{ .PkgName }}.{{ .Name }}.ElementsAs(ctx, &tmp{{ .Name }}, false) childMap["attributes"]["{{ .PropertyName }}"] = strings.Join(tmp{{ .Name }}, ",") - {{- else if eq .Name "Id"}} + {{- else if eq .Name "Id"}} childMap["attributes"]["{{ .PropertyName }}"] = {{ .PkgName }}.{{.ResourceClassName}}{{ .Name }}.ValueString() - {{- else}} + {{- else}} childMap["attributes"]["{{ .PropertyName }}"] = {{ .PkgName }}.{{ .Name }}.ValueString() - {{- end}} + {{- end}} } {{- if eq .Name "Annotation" }} else { childMap["attributes"]["annotation"] = globalAnnotation } {{- end}} - {{- end}} + {{- end}} childPayloads = append(childPayloads, map[string]interface{}{"{{ .PkgName }}": childMap}) - {{- if and .IdentifiedBy (not (and .MaxOneClassAllowed (hasPrefix .RnFormat "rs")))}} + {{- if and .IdentifiedBy (not (and .MaxOneClassAllowed (hasPrefix .RnFormat "rs")))}} {{ .PkgName }}Identifier := {{ .ResourceClassName }}Identifier{} - {{- range .Properties}} - {{- if .IsNaming}} + {{- range .Properties}} + {{- if .IsNaming}} {{ .PkgName }}Identifier.{{.Name}} = {{ .PkgName }}.{{- if eq .Name "Id"}}{{.ResourceClassName}}{{ .Name }}{{- else}}{{.Name}}{{- end}} + {{- end}} {{- end}} - {{- end}} {{ .PkgName }}Identifiers = append({{ .PkgName }}Identifiers, {{ .PkgName }}Identifier) - {{- end}} + {{- end}} } - {{- if and .IdentifiedBy (not (and .MaxOneClassAllowed (hasPrefix .RnFormat "rs")))}} + {{- if and .IdentifiedBy (not (and .MaxOneClassAllowed (hasPrefix .RnFormat "rs")))}} for _, {{ .PkgName }} := range {{ .PkgName }}State { delete := true for _, {{ .PkgName }}Identifier := range {{ .PkgName }}Identifiers { {{$i := 1}}{{$length := len .IdentifiedBy}} if - {{- range .Properties}} - {{- if .IsNaming }} + {{- range .Properties}} + {{- if .IsNaming }} {{ .PkgName }}Identifier.{{.Name}} == {{ .PkgName }}.{{- if eq .Name "Id"}}{{.ResourceClassName}}{{ .Name }}{{- else}}{{.Name}}{{- end}}{{- if ne $length $i}} {{$i = add $i 1}} && {{- end}} - {{- end}} - {{- end}} { + {{- end}} + {{- end}} { delete = false break } @@ -1591,45 +1967,53 @@ func get{{$.ResourceClassName}}{{ .ResourceClassName }}ChildPayloads(ctx context if delete { childMap := map[string]map[string]interface{}{"attributes": {}} childMap["attributes"]["status"] = "deleted" - {{- range .Properties}} - {{- if .IsNaming }} + {{- range .Properties}} + {{- if .IsNaming }} childMap["attributes"]["{{.PropertyName}}"] = {{ .PkgName }}.{{- if eq .Name "Id"}}{{.ResourceClassName}}{{ .Name }}{{- else}}{{.Name}}{{- end}}.ValueString() + {{- end}} {{- end}} - {{- end}} childPayloads = append(childPayloads, map[string]interface{}{"{{ .PkgName }}": childMap}) } } - {{- else}} + {{- else}} + {{- if not (or (not .IdentifiedBy) .MaxOneClassAllowed)}} if len({{ .PkgName }}Plan) == 0 && len({{ .PkgName }}State) == 1 { - {{- if .AllowDelete}} + {{- if .AllowDelete}} childMap := map[string]map[string]interface{}{"attributes": {}} childMap["attributes"]["status"] = "deleted" - {{- if .IdentifiedBy}} - {{- range .Properties}} - {{- if .IsNaming }} + {{- if .IdentifiedBy}} + {{- range .Properties}} + {{- if .IsNaming }} childMap["attributes"]["{{.PropertyName}}"] = {{ .PkgName }}State[0].{{- if eq .Name "Id"}}{{.ResourceClassName}}{{ .Name }}{{- else}}{{.Name}}{{- end}}.ValueString() - {{- end}} - {{- end}} - {{- end}} + {{- end}} + {{- end}} + {{- end}} childPayloads = append(childPayloads, map[string]interface{}{"{{ .PkgName }}": childMap}) - {{- else}} + {{- else}} diags.AddError( "{{ .ResourceClassName }} object cannot be deleted", "deletion of child is only possible upon deletion of the parent", ) return nil - {{- end }} + {{- end }} } + {{- end}} + {{- end}} {{- end}} } else { + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + {{ .ResourceClassName }}Object, _ := types.ObjectValueFrom(ctx, {{.ResourceClassName}}{{$.ResourceClassName}}Type, getEmpty{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel()) + data.{{ .ResourceClassName }} = {{ .ResourceClassName }}Object + {{- else}} data.{{ .ResourceClassName }} = types.SetNull(data.{{ .ResourceClassName }}.ElementType(ctx)) + {{- end}} } return childPayloads } {{- end}} -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 { +func get{{.ResourceClassName}}CreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *{{.ResourceClassName}}ResourceModel{{- range .Children}}, {{.PkgName}}Plan, {{.PkgName}}State {{if not ( or (not .IdentifiedBy) .MaxOneClassAllowed)}}[]{{end}}{{.ResourceClassName}}{{$.ResourceClassName}}ResourceModel{{- end}}) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} diff --git a/gen/templates/resource.md.tmpl b/gen/templates/resource.md.tmpl index b5c9f61b0..26ae95c1f 100644 --- a/gen/templates/resource.md.tmpl +++ b/gen/templates/resource.md.tmpl @@ -137,16 +137,18 @@ All examples for the {{.ResourceNameAsDescription}} resource can be found in the {{- end}} {{- end}} {{- else}} -* `{{- overwriteProperty .PkgName .ResourceName $.Definitions}}` - (list) A list of {{getResourceNameAsDescription .ResourceName $.Definitions}} (ACI object {{getDevnetDocForClass .PkgName}}) pointing to {{range $index, $value := .RelationshipResourceNames}}{{- if or (eq $length 1) (eq $.PkgName (index $RelationshipClasses $index))}}{{getResourceNameAsDescription $value $.Definitions}} (ACI Object {{getDevnetDocForClass (index $RelationshipClasses $index)}}){{- if ne $value ""}} which can be configured using the [aci_{{($value)}}](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/{{($value)}}) resource{{end}}{{end}}{{end}}.{{if and (ne $.Versions .Versions) (ne .Versions "")}} This attribute is supported in ACI versions: {{ .Versions}}{{- end}} - {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} - - Max Items: 1 - {{- end}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} +* `{{- overwriteProperty .PkgName .ResourceName $.Definitions}}` - (map) A map of {{getResourceNameAsDescription .ResourceName $.Definitions}} (ACI object {{getDevnetDocForClass .PkgName}}) pointing to {{range $index, $value := .RelationshipResourceNames}}{{- if or (eq $length 1) (eq $.PkgName (index $RelationshipClasses $index))}}{{getResourceNameAsDescription $value $.Definitions}} (ACI Object {{getDevnetDocForClass (index $RelationshipClasses $index)}}){{- if and (ne $value "") (isResourceClass (index $RelationshipClasses $index)) }} which can be configured using the [aci_{{($value)}}](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/{{($value)}}) resource{{end}}{{end}}{{end}}.{{if and (ne $.Versions .Versions) (ne .Versions "")}} This attribute is supported in ACI versions: {{ .Versions}}{{- end}} + {{- else}} +* `{{- overwriteProperty .PkgName .ResourceName $.Definitions}}` - (list) A list of {{getResourceNameAsDescription .ResourceName $.Definitions}} (ACI object {{getDevnetDocForClass .PkgName}}) pointing to {{range $index, $value := .RelationshipResourceNames}}{{- if or (eq $length 1) (eq $.PkgName (index $RelationshipClasses $index))}}{{getResourceNameAsDescription $value $.Definitions}} (ACI Object {{getDevnetDocForClass (index $RelationshipClasses $index)}}){{- if and (ne $value "") (isResourceClass (index $RelationshipClasses $index)) }} which can be configured using the [aci_{{($value)}}](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/{{($value)}}) resource{{end}}{{end}}{{end}}.{{if and (ne $.Versions .Versions) (ne .Versions "")}} This attribute is supported in ACI versions: {{ .Versions}}{{- end}} + {{- end}} {{- end}} {{- else}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} +* `{{- overwriteProperty .PkgName .ResourceName $.Definitions}}` - (map) A map of {{getResourceNameAsDescription .ResourceName $.Definitions}} (ACI object {{getDevnetDocForClass .PkgName}}). {{getResourceNameAsDescription .ResourceName $.Definitions}} can also be configured using a separate [aci_{{.ResourceNameDocReference}}](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/{{.ResourceNameDocReference}}) resource.{{if and (ne $.Versions .Versions) (ne .Versions "")}} This attribute is supported in ACI versions: {{ .Versions}}{{- end}} + {{- else}} * `{{- overwriteProperty .PkgName .ResourceName $.Definitions}}` - (list) A list of {{getResourceNameAsDescription .ResourceName $.Definitions}} (ACI object {{getDevnetDocForClass .PkgName}}). {{getResourceNameAsDescription .ResourceName $.Definitions}} can also be configured using a separate [aci_{{.ResourceNameDocReference}}](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/{{.ResourceNameDocReference}}) resource.{{if and (ne $.Versions .Versions) (ne .Versions "")}} This attribute is supported in ACI versions: {{ .Versions}}{{- end}} - {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} - - Max Items: 1 - {{- end}} + {{- end}} {{- end}} {{ if containsRequired .Properties}} #### Required #### diff --git a/gen/templates/resource_example_all_attributes.tf.tmpl b/gen/templates/resource_example_all_attributes.tf.tmpl index ca3b52ac5..f7b497740 100644 --- a/gen/templates/resource_example_all_attributes.tf.tmpl +++ b/gen/templates/resource_example_all_attributes.tf.tmpl @@ -20,6 +20,21 @@ resource "aci_{{$.ResourceName}}" "full_example_{{getResourceName $key $.Definit {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = "{{lookupTestValue .PkgName .SnakeCaseName $.TestVars $.Definitions}}" {{- end}}{{- end}}{{- end}} {{- range $key, $value := $.Children}}{{$ChildResourceName := overwriteProperty .PkgName .ResourceName $.Definitions}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + {{$ChildResourceName}} = { + {{- range .Properties}}{{- if not .ReadOnly }}{{- if ne .NamedPropertyClass ""}} + {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = aci_{{getResourceName .NamedPropertyClass $.Definitions}}.example.name + {{- else if eq .SnakeCaseName "t_dn" }} + {{- range $index, $testParent := $.TestVars.parents }}{{$attributeVale := getTestTargetDn $.TestVars.child_targets $ChildResourceName "target_dn_0" true nil 0 true | replace "test_0" "example_2" }} + target_dn = {{if containsString $attributeVale "."}}{{$attributeVale}}{{else}}"{{$attributeVale}}"{{end}} + {{- break}} + {{- end}} + {{- else if .IgnoreInTest}} + {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = "{{.IgnoreInTestExampleValue}}" + {{- else}} + {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = "{{lookupChildTestValue .PkgName $ChildResourceName .SnakeCaseName $.TestVars 0 $.Definitions}}"{{- end}}{{ end}}{{- end}} + } + {{- else}} {{$ChildResourceName}} = [ { {{- range .Properties}}{{- if not .ReadOnly }}{{- if ne .NamedPropertyClass ""}} {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = aci_{{getResourceName .NamedPropertyClass $.Definitions}}.example.name @@ -34,6 +49,7 @@ resource "aci_{{$.ResourceName}}" "full_example_{{getResourceName $key $.Definit {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = "{{lookupChildTestValue .PkgName $ChildResourceName .SnakeCaseName $.TestVars 0 $.Definitions}}"{{- end}}{{ end}}{{- end}} } ] + {{- end}} {{- end}} } {{ end}} @@ -49,6 +65,18 @@ resource "aci_{{$.ResourceName}}" "full_example" { {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = "{{lookupTestValue .PkgName .SnakeCaseName $.TestVars $.Definitions}}" {{- end}}{{- end}}{{- end}} {{- range $key, $value := $.Children}}{{$ChildResourceName := overwriteProperty .PkgName .ResourceName $.Definitions}} + {{- if or (not .IdentifiedBy) .MaxOneClassAllowed}} + {{$ChildResourceName}} = { + {{- range .Properties}}{{- if not .ReadOnly }}{{- if ne .NamedPropertyClass ""}} + {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = aci_{{getResourceName .NamedPropertyClass $.Definitions}}.example.name + {{- else if eq .SnakeCaseName "t_dn" }}{{$attributeVale := getTestTargetDn $.TestVars.child_targets $ChildResourceName "target_dn_0" true nil 0 true | replace "test_0" "example_2" }} + target_dn = {{if containsString $attributeVale "."}}{{$attributeVale}}{{else}}"{{$attributeVale}}"{{end}} + {{- else if .IgnoreInTest}} + {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = "{{.IgnoreInTestExampleValue}}" + {{- else}} + {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = "{{lookupChildTestValue .PkgName $ChildResourceName .SnakeCaseName $.TestVars 0 $.Definitions}}"{{- end}}{{ end}}{{- end}} + } + {{- else}} {{$ChildResourceName}} = [ { {{- range .Properties}}{{- if not .ReadOnly }}{{- if ne .NamedPropertyClass ""}} {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = aci_{{getResourceName .NamedPropertyClass $.Definitions}}.example.name @@ -60,6 +88,7 @@ resource "aci_{{$.ResourceName}}" "full_example" { {{overwriteProperty .PkgName .SnakeCaseName $.Definitions}} = "{{lookupChildTestValue .PkgName $ChildResourceName .SnakeCaseName $.TestVars 0 $.Definitions}}"{{- end}}{{ end}}{{- end}} } ] + {{- end}} {{- end}} } {{ end}} \ No newline at end of file diff --git a/gen/templates/resource_test.go.tmpl b/gen/templates/resource_test.go.tmpl index 8f78718ee..9ecdb17ea 100644 --- a/gen/templates/resource_test.go.tmpl +++ b/gen/templates/resource_test.go.tmpl @@ -11,6 +11,14 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) +{{$single_nested_children := false}} +{{- range $key, $value := $.children}} + {{- if eq (len $value) 1}}{{- $child := index $value 0 }} + {{- if $child.deletable_child}} + {{- $single_nested_children = true}} + {{- end}} + {{- end}} +{{- end}} {{- if .parents}} {{- range .parents}}{{$target_classes := .target_classes}} {{- $parentClassName := capitalize .class_name }} @@ -269,12 +277,24 @@ func TestAccResource{{$.resourceClassName}}With{{capitalize .class_name}}(t *tes resource.TestCheckResourceAttrSet("aci_{{$.resourceName}}.test", "{{$key}}"), {{- end}} {{- range $key, $value := $.children}} - {{- range $child_index, $child := $value}} - {{- range $child_key, $child_value := $child}} - {{- if eq $child_key "target_dn"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), - {{- else if ne $child_key "deletable_child"}} + {{- if eq (len $value) 1}} + {{- range $child_index, $child := $value}} + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false }}"), + {{- else if ne $child_key "deletable_child"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_key}}", "{{$child_value}}"), + {{- end}} + {{- end}} + {{- end}} + {{- else}} + {{- range $child_index, $child := $value}} + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), + {{- else if ne $child_key "deletable_child"}} resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{$child_value}}"), + {{- end}} {{- end}} {{- end}} {{- end}} @@ -316,18 +336,26 @@ func TestAccResource{{$.resourceClassName}}With{{capitalize .class_name}}(t *tes resource.TestCheckResourceAttrSet("aci_{{$.resourceName}}.test", "{{$key}}"), {{- end}} {{- range $key, $value := $.children}} - {{- range $child_index, $child := $value}} - {{- range $child_key, $child_value := $child}} - {{- if eq $child_key "target_dn"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), - {{- else if ne $child_key "deletable_child"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{$child_value}}"), + {{- if eq (len $value) 1}} + {{- range $child_index, $child := $value}} + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), + {{- else if ne $child_key "deletable_child"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_key}}", "{{$child_value}}"), + {{- end}} {{- end}} {{- end}} - {{- end}} - {{- if eq (len $value) 1}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.#", "1"), {{- else}} + {{- range $child_index, $child := $value}} + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), + {{- else if ne $child_key "deletable_child"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{$child_value}}"), + {{- end}} + {{- end}} + {{- end}} resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.#", "2"), {{- end}} {{- end}} @@ -351,17 +379,14 @@ func TestAccResource{{$.resourceClassName}}With{{capitalize .class_name}}(t *tes {{- end}} {{- range $key, $value := $.children}} {{- if eq (len $value) 1}}{{- $child := index $value 0 }} - {{- if $child.deletable_child}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.#", "0"), - {{- else}} + {{- if not $child.deletable_child}} {{- range $child_key, $child_value := $child}} {{- if eq $child_key "target_dn"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.0.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil 0 false}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil 0 false}}"), {{- else if ne $child_key "deletable_child"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.0.{{$child_key}}", "{{$child_value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_key}}", "{{$child_value}}"), {{- end}} {{- end}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.#", "1"), {{- end}} {{- else}} {{- $child := index $value 1 }} @@ -376,6 +401,29 @@ func TestAccResource{{$.resourceClassName}}With{{capitalize .class_name}}(t *tes {{- end}} {{- end}} ), + {{- if $single_nested_children}} + ConfigStateChecks: []statecheck.StateCheck{ + {{- range $key, $value := $.children}} + {{- if eq (len $value) 1}}{{- $child := index $value 0 }} + {{- if $child.deletable_child}} + statecheck.ExpectKnownValue("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", + tfjsonpath.New("{{$key}}"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + {{- range $child_key, $child_value := $child}} + {{- if ne $child_key "deletable_child"}} + "{{$child_key}}": knownvalue.Null(), + {{- end}} + {{- end}} + }, + ), + ), + {{- end}} + {{- end}} + {{- end}} + }, + + {{- end}} }, // Update with all children removed { @@ -395,21 +443,41 @@ func TestAccResource{{$.resourceClassName}}With{{capitalize .class_name}}(t *tes {{- end}} {{- range $key, $value := $.children}} {{- if eq (len $value) 1}}{{- $child := index $value 0 }} - {{- if $child.deletable_child}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.#", "0"), - {{- else}} + {{- if not $child.deletable_child}} {{- range $child_key, $child_value := $child}} {{- if ne $child_key "deletable_child"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.0.{{$child_key}}", "{{$child_value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.{{$child_key}}", "{{$child_value}}"), {{- end}} {{- end}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.#", "1"), {{- end}} {{- else}} resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", "{{$key}}.#", "0"), {{- end}} {{- end}} ), + {{- if $single_nested_children}} + ConfigStateChecks: []statecheck.StateCheck{ + {{- range $key, $value := $.children}} + {{- if eq (len $value) 1}}{{- $child := index $value 0 }} + {{- if $child.deletable_child}} + statecheck.ExpectKnownValue("aci_{{$.resourceName}}.test{{- if eq $.resourceClassName $parentClassName }}_1{{- end }}", + tfjsonpath.New("{{$key}}"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + {{- range $child_key, $child_value := $child}} + {{- if ne $child_key "deletable_child"}} + "{{$child_key}}": knownvalue.Null(), + {{- end}} + {{- end}} + }, + ), + ), + {{- end}} + {{- end}} + {{- end}} + }, + + {{- end}} }, {{- end}} {{- if $.custom_type}} @@ -669,16 +737,28 @@ func TestAccResource{{.resourceClassName}}(t *testing.T) { resource.TestCheckResourceAttrSet("aci_{{$.resourceName}}.test", "{{$key}}"), {{- end}} {{- range $key, $value := $.children}} - {{- range $child_index, $child := $value}} - {{- range $child_key, $child_value := $child}} - {{- if eq $child_key "target_dn"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), - {{- else if ne $child_key "deletable_child"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{$child_value}}"), + {{- if eq (len $value) 1}} + {{- range $child_index, $child := $value}} + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), + {{- else if ne $child_key "deletable_child"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_key}}", "{{$child_value}}"), + {{- end}} + {{- end}} + {{- end}} + {{- else}} + {{- range $child_index, $child := $value}} + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), + {{- else if ne $child_key "deletable_child"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{$child_value}}"), + {{- end}} {{- end}} {{- end}} - {{- end}} resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "{{len $value}}"), + {{- end}} {{- end}} ), }, @@ -709,16 +789,28 @@ func TestAccResource{{.resourceClassName}}(t *testing.T) { resource.TestCheckResourceAttrSet("aci_{{$.resourceName}}.test", "{{$key}}"), {{- end}} {{- range $key, $value := $.children}} - {{- range $child_index, $child := $value}} - {{- range $child_key, $child_value := $child}} - {{- if eq $child_key "target_dn"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), - {{- else if ne $child_key "deletable_child"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{$child_value}}"), + {{- if eq (len $value) 1}} + {{- range $child_index, $child := $value}} + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), + {{- else if ne $child_key "deletable_child"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_key}}", "{{$child_value}}"), + {{- end}} {{- end}} {{- end}} + {{- else}} + {{- range $child_index, $child := $value}} + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil $child_index false}}"), + {{- else if ne $child_key "deletable_child"}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_index}}.{{$child_key}}", "{{$child_value}}"), + {{- end}} + {{- end}} + {{- end}} + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "{{len $value}}"), {{- end}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "{{len $value}}"), {{- end}} ), }, @@ -750,17 +842,14 @@ func TestAccResource{{.resourceClassName}}(t *testing.T) { {{- end}} {{- range $key, $value := $.children}} {{- if eq (len $value) 1}}{{- $child := index $value 0 }} - {{- if $child.deletable_child}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "0"), - {{- else}} + {{- if not $child.deletable_child}} {{- range $child_key, $child_value := $child}} {{- if eq $child_key "target_dn"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.0.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil 1 false}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_key}}", "{{getTestTargetDn $.child_targets $key $child_value false nil 1 false}}"), {{- else if ne $child_key "deletable_child"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.0.{{$child_key}}", "{{$child_value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_key}}", "{{$child_value}}"), {{- end}} {{- end}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "1"), {{- end}} {{- else}} {{- $child := index $value 1 }} @@ -775,6 +864,29 @@ func TestAccResource{{.resourceClassName}}(t *testing.T) { {{- end}} {{- end}} ), + {{- if $single_nested_children}} + ConfigStateChecks: []statecheck.StateCheck{ + {{- range $key, $value := $.children}} + {{- if eq (len $value) 1}}{{- $child := index $value 0 }} + {{- if $child.deletable_child}} + statecheck.ExpectKnownValue("aci_{{$.resourceName}}.test", + tfjsonpath.New("{{$key}}"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + {{- range $child_key, $child_value := $child}} + {{- if ne $child_key "deletable_child"}} + "{{$child_key}}": knownvalue.Null(), + {{- end}} + {{- end}} + }, + ), + ), + {{- end}} + {{- end}} + {{- end}} + }, + + {{- end}} }, // Update with all children removed { @@ -804,21 +916,41 @@ func TestAccResource{{.resourceClassName}}(t *testing.T) { {{- end}} {{- range $key, $value := $.children}} {{- if eq (len $value) 1}}{{- $child := index $value 0 }} - {{- if $child.deletable_child}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "0"), - {{- else}} + {{- if not $child.deletable_child}} {{- range $child_key, $child_value := $child}} {{- if ne $child_key "deletable_child"}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.0.{{$child_key}}", "{{$child_value}}"), + resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.{{$child_key}}", "{{$child_value}}"), {{- end}} {{- end}} - resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "1"), {{- end}} {{- else}} resource.TestCheckResourceAttr("aci_{{$.resourceName}}.test", "{{$key}}.#", "0"), {{- end}} {{- end}} ), + {{- if $single_nested_children}} + ConfigStateChecks: []statecheck.StateCheck{ + {{- range $key, $value := $.children}} + {{- if eq (len $value) 1}}{{- $child := index $value 0 }} + {{- if $child.deletable_child}} + statecheck.ExpectKnownValue("aci_{{$.resourceName}}.test", + tfjsonpath.New("{{$key}}"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + {{- range $child_key, $child_value := $child}} + {{- if ne $child_key "deletable_child"}} + "{{$child_key}}": knownvalue.Null(), + {{- end}} + {{- end}} + }, + ), + ), + {{- end}} + {{- end}} + {{- end}} + }, + + {{- end}} }, {{- end}} {{- if $.custom_type}} @@ -983,20 +1115,33 @@ resource "aci_{{$.resourceName}}" "test{{- if eq $.resourceClassName $parentClas {{- end }} {{- end}} {{- range $key, $value := $.children}}{{$index := 0}} - {{$key}} = [ - {{- range $child := $value}} - { + {{- if eq (len $value) 1}} + {{- $child := index $value 0}} + {{$key}} = { {{- range $child_key, $child_value := $child}} {{- if eq $child_key "target_dn"}}{{$attributeValue := getTestTargetDn $.child_targets $key $child_value true nil $index false}} - {{$child_key}} = {{if containsString $attributeValue "."}}{{$attributeValue}}{{else}}"{{$attributeValue}}"{{end}} + {{$child_key}} = {{if containsString $attributeValue "."}}{{$attributeValue}}{{else}}"{{$attributeValue}}"{{end}} {{- else if ne $child_key "deletable_child"}} - {{$child_key}} = "{{$child_value}}" + {{$child_key}} = "{{$child_value}}" {{- end}} {{- end}}{{ $index = add $index 1 }} + } + {{- else}} + {{$key}} = [ + {{- range $child := $value}} + { + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}}{{$attributeValue := getTestTargetDn $.child_targets $key $child_value true nil $index false}} + {{$child_key}} = {{if containsString $attributeValue "."}}{{$attributeValue}}{{else}}"{{$attributeValue}}"{{end}} + {{- else if ne $child_key "deletable_child"}} + {{$child_key}} = "{{$child_value}}" + {{- end}} + {{- end}}{{ $index = add $index 1 }} }, - {{- end}} + {{- end}} ] - {{- end}} + {{- end}} + {{- end}} } ` @@ -1027,7 +1172,7 @@ resource "aci_{{$.resourceName}}" "test{{- if eq $.resourceClassName $parentClas {{- if eq (len $value) 1}} {{- $child := index $value 0}} {{- if $child.deletable_child}} - {{$key}} = [] + {{$key}} = {} {{- end}} {{- else}} {{$key}} = [ {{$child := index $value 1 }} @@ -1060,7 +1205,7 @@ resource "aci_{{$.resourceName}}" "test{{- if eq $.resourceClassName $parentClas {{- if eq (len $value) 1}} {{- $child := index $value 0}} {{- if $child.deletable_child}} - {{$key}} = [] + {{$key}} = {} {{- end}} {{- else}} {{$key}} = [] @@ -1162,19 +1307,32 @@ resource "aci_{{$.resourceName}}" "test" { {{- end }} {{- end}} {{- range $key, $value := $.children}}{{$index := 0}} - {{$key}} = [ - {{- range $child := $value}} - { + {{- if eq (len $value) 1}} + {{- $child := index $value 0}} + {{$key}} = { {{- range $child_key, $child_value := $child}} {{- if eq $child_key "target_dn"}}{{$attributeValue := getTestTargetDn $.child_targets $key $child_value true nil $index false}} - {{$child_key}} = {{if containsString $attributeValue "."}}{{$attributeValue}}{{else}}"{{$attributeValue}}"{{end}} + {{$child_key}} = {{if containsString $attributeValue "."}}{{$attributeValue}}{{else}}"{{$attributeValue}}"{{end}} {{- else if ne $child_key "deletable_child"}} - {{$child_key}} = "{{$child_value}}" + {{$child_key}} = "{{$child_value}}" {{- end}} {{- end}}{{ $index = add $index 1 }} + } + {{- else}} + {{$key}} = [ + {{- range $child := $value}} + { + {{- range $child_key, $child_value := $child}} + {{- if eq $child_key "target_dn"}}{{$attributeValue := getTestTargetDn $.child_targets $key $child_value true nil $index false}} + {{$child_key}} = {{if containsString $attributeValue "."}}{{$attributeValue}}{{else}}"{{$attributeValue}}"{{end}} + {{- else if ne $child_key "deletable_child"}} + {{$child_key}} = "{{$child_value}}" + {{- end}} + {{- end}}{{ $index = add $index 1 }} }, - {{- end}} + {{- end}} ] + {{- end}} {{- end}} } ` @@ -1204,7 +1362,7 @@ resource "aci_{{$.resourceName}}" "test" { {{- if eq (len $value) 1}} {{- $child := index $value 0}} {{- if $child.deletable_child}} - {{$key}} = [] + {{$key}} = {} {{- end}} {{- else}} {{$key}} = [ {{$child := index $value 1 }} @@ -1233,10 +1391,10 @@ resource "aci_{{$.resourceName}}" "test" { {{- end }} {{- end}} {{- range $key, $value := $.children}} - {{- if eq (len $value) 1}} + {{- if eq (len $value) 1}} {{- $child := index $value 0}} {{- if $child.deletable_child}} - {{$key}} = [] + {{$key}} = {} {{- end}} {{- else}} {{$key}} = [] diff --git a/gen/testvars/fvFBRGroup.yaml b/gen/testvars/fvFBRGroup.yaml index cb5f4fec6..d7a4f0795 100644 --- a/gen/testvars/fvFBRGroup.yaml +++ b/gen/testvars/fvFBRGroup.yaml @@ -35,7 +35,7 @@ children: name_alias: "name_alias_2" fallback_member: "2.2.2.3" - vrf_fallback_routes: + vrf_fallback_route: - annotation: "annotation_1" description: "description_1" prefix_address: "2.2.2.2/24" diff --git a/gen/testvars/fvRsFcPathAtt.yaml b/gen/testvars/fvRsFcPathAtt.yaml index 9a414bb3b..14b75b753 100644 --- a/gen/testvars/fvRsFcPathAtt.yaml +++ b/gen/testvars/fvRsFcPathAtt.yaml @@ -48,20 +48,20 @@ parents: targets: - class_name: "fabricPathEp" target_dn: "topology/pod-1/paths-101/pathep-[eth1/1]" - target_dn_ref: "aci_.test__0.id" + target_dn_ref: "aci_fabric_path_endpoint.test_fabric_path_endpoint_0.id" parent_dependency: "" parent_dependency_dn_ref: "" - target_resource_name: "" + target_resource_name: "fabric_path_endpoint" relation_resource_name: "fibre_channel_path" parent_dn_key: "parent_dn" static: true properties: - class_name: "fabricPathEp" target_dn: "topology/pod-1/paths-101/pathep-[eth1/2]" - target_dn_ref: "aci_.test__1.id" + target_dn_ref: "aci_fabric_path_endpoint.test_fabric_path_endpoint_1.id" parent_dependency: "" parent_dependency_dn_ref: "" - target_resource_name: "" + target_resource_name: "fabric_path_endpoint" relation_resource_name: "fibre_channel_path" parent_dn_key: "parent_dn" static: true diff --git a/gen/testvars/fvRsPathAtt.yaml b/gen/testvars/fvRsPathAtt.yaml index b6f771a74..3325e154c 100644 --- a/gen/testvars/fvRsPathAtt.yaml +++ b/gen/testvars/fvRsPathAtt.yaml @@ -53,20 +53,20 @@ parents: targets: - class_name: "fabricPathEp" target_dn: "topology/pod-1/paths-101/pathep-[eth1/1]" - target_dn_ref: "aci_.test__0.id" + target_dn_ref: "aci_fabric_path_endpoint.test_fabric_path_endpoint_0.id" parent_dependency: "" parent_dependency_dn_ref: "" - target_resource_name: "" + target_resource_name: "fabric_path_endpoint" relation_resource_name: "static_path" parent_dn_key: "parent_dn" static: true properties: - class_name: "fabricPathEp" target_dn: "topology/pod-1/paths-101/pathep-[eth1/2]" - target_dn_ref: "aci_.test__1.id" + target_dn_ref: "aci_fabric_path_endpoint.test_fabric_path_endpoint_1.id" parent_dependency: "" parent_dependency_dn_ref: "" - target_resource_name: "" + target_resource_name: "fabric_path_endpoint" relation_resource_name: "static_path" parent_dn_key: "parent_dn" static: true diff --git a/internal/provider/data_source_aci_access_interface_override.go b/internal/provider/data_source_aci_access_interface_override.go index 7169293f2..474f3cfca 100644 --- a/internal/provider/data_source_aci_access_interface_override.go +++ b/internal/provider/data_source_aci_access_interface_override.go @@ -73,35 +73,31 @@ func (d *InfraHPathSDataSource) Schema(ctx context.Context, req datasource.Schem Computed: true, MarkdownDescription: `A tag for enabling clients to add their own data. For example, to indicate who created this object.`, }, - "relation_to_host_path": schema.SetNestedAttribute{ + "relation_to_host_path": schema.SingleNestedAttribute{ MarkdownDescription: ``, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation To Host Path object.`, - }, - "target_dn": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The distinguished name of the target.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation To Host Path object.`, + }, + "target_dn": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The distinguished name of the target.`, }, }, }, - "relation_to_access_interface_policy_group": schema.SetNestedAttribute{ + "relation_to_access_interface_policy_group": schema.SingleNestedAttribute{ MarkdownDescription: ``, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation To Access Interface Policy Group object.`, - }, - "target_dn": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The distinguished name of the target.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation To Access Interface Policy Group object.`, + }, + "target_dn": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The distinguished name of the target.`, }, }, }, diff --git a/internal/provider/data_source_aci_application_epg.go b/internal/provider/data_source_aci_application_epg.go index 13b8b277c..85c55a8c0 100644 --- a/internal/provider/data_source_aci_application_epg.go +++ b/internal/provider/data_source_aci_application_epg.go @@ -217,79 +217,73 @@ func (d *FvAEPgDataSource) Schema(ctx context.Context, req datasource.SchemaRequ Computed: true, MarkdownDescription: `Withdraw AEPg Configuration from all Nodes in the Fabric.`, }, - "epg_useg_block_statement": schema.SetNestedAttribute{ + "epg_useg_block_statement": schema.SingleNestedAttribute{ MarkdownDescription: `A criterion.`, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the EPG uSeg Block Statement object.`, - }, - "description": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The description of the EPG uSeg Block Statement object.`, - }, - "match": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The Matching Rule Type of the EPG uSeg Block Statement object.`, - }, - "name": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The name of the EPG uSeg Block Statement object.`, - }, - "name_alias": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The name alias of the EPG uSeg Block Statement object.`, - }, - "owner_key": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The key for enabling clients to own their data for entity correlation.`, - }, - "owner_tag": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `A tag for enabling clients to add their own data. For example, to indicate who created this object.`, - }, - "precedence": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The precedence of the EPG uSeg Block Statement object.`, - }, - "scope": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The scope of the EPG uSeg Block Statement object.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the EPG uSeg Block Statement object.`, + }, + "description": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The description of the EPG uSeg Block Statement object.`, + }, + "match": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The Matching Rule Type of the EPG uSeg Block Statement object.`, + }, + "name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The name of the EPG uSeg Block Statement object.`, + }, + "name_alias": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The name alias of the EPG uSeg Block Statement object.`, + }, + "owner_key": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The key for enabling clients to own their data for entity correlation.`, + }, + "owner_tag": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `A tag for enabling clients to add their own data. For example, to indicate who created this object.`, + }, + "precedence": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The precedence of the EPG uSeg Block Statement object.`, + }, + "scope": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The scope of the EPG uSeg Block Statement object.`, }, }, }, - "relation_to_application_epg_monitoring_policy": schema.SetNestedAttribute{ + "relation_to_application_epg_monitoring_policy": schema.SingleNestedAttribute{ MarkdownDescription: `A source relation to the monitoring policy model for the endpoint group semantic scope. This is an internal object.`, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation To Application EPG Monitoring Policy object.`, - }, - "monitoring_policy_name": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The name of the monitoring policy.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation To Application EPG Monitoring Policy object.`, + }, + "monitoring_policy_name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The name of the monitoring policy.`, }, }, }, - "relation_to_bridge_domain": schema.SetNestedAttribute{ + "relation_to_bridge_domain": schema.SingleNestedAttribute{ MarkdownDescription: `A source relation to the bridge domain associated to this endpoint group. This is an internal object.`, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation To Bridge Domain object.`, - }, - "bridge_domain_name": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The name of the bridge domain associated with this EPG.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation To Bridge Domain object.`, + }, + "bridge_domain_name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The name of the bridge domain associated with this EPG.`, }, }, }, @@ -335,19 +329,17 @@ func (d *FvAEPgDataSource) Schema(ctx context.Context, req datasource.SchemaRequ }, }, }, - "relation_to_custom_qos_policy": schema.SetNestedAttribute{ + "relation_to_custom_qos_policy": schema.SingleNestedAttribute{ MarkdownDescription: `A source relation to a custom QoS policy that enables different levels of service to be assigned to network traffic, including specifications for the Differentiated Services Code Point (DSCP) value(s) and the 802.1p Dot1p priority. This is an internal object.`, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation To Custom Qos Policy object.`, - }, - "custom_qos_policy_name": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The Custom QoS traffic policy name.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation To Custom Qos Policy object.`, + }, + "custom_qos_policy_name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The Custom QoS traffic policy name.`, }, }, }, @@ -463,19 +455,17 @@ func (d *FvAEPgDataSource) Schema(ctx context.Context, req datasource.SchemaRequ }, }, }, - "relation_to_data_plane_policing_policy": schema.SetNestedAttribute{ + "relation_to_data_plane_policing_policy": schema.SingleNestedAttribute{ MarkdownDescription: `Relationship for Dpp QOS policy`, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation To Data Plane Policing Policy object.`, - }, - "data_plane_policing_policy_name": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `Name.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation To Data Plane Policing Policy object.`, + }, + "data_plane_policing_policy_name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `Name.`, }, }, }, @@ -655,19 +645,17 @@ func (d *FvAEPgDataSource) Schema(ctx context.Context, req datasource.SchemaRequ }, }, }, - "relation_to_trust_control_policy": schema.SetNestedAttribute{ + "relation_to_trust_control_policy": schema.SingleNestedAttribute{ MarkdownDescription: `Relationship for FHS trust control`, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation To Trust Control Policy object.`, - }, - "trust_control_policy_name": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `Name.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation To Trust Control Policy object.`, + }, + "trust_control_policy_name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `Name.`, }, }, }, diff --git a/internal/provider/data_source_aci_endpoint_security_group.go b/internal/provider/data_source_aci_endpoint_security_group.go index b42d79254..ce8a57fa1 100644 --- a/internal/provider/data_source_aci_endpoint_security_group.go +++ b/internal/provider/data_source_aci_endpoint_security_group.go @@ -209,19 +209,17 @@ func (d *FvESgDataSource) Schema(ctx context.Context, req datasource.SchemaReque }, }, }, - "relation_to_vrf": schema.SetNestedAttribute{ + "relation_to_vrf": schema.SingleNestedAttribute{ MarkdownDescription: ``, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation To VRF object.`, - }, - "vrf_name": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The name of the VRF object.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation To VRF object.`, + }, + "vrf_name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The name of the VRF object.`, }, }, }, diff --git a/internal/provider/data_source_aci_netflow_exporter_policy.go b/internal/provider/data_source_aci_netflow_exporter_policy.go index 45a338ca0..13064088e 100644 --- a/internal/provider/data_source_aci_netflow_exporter_policy.go +++ b/internal/provider/data_source_aci_netflow_exporter_policy.go @@ -100,35 +100,31 @@ func (d *NetflowExporterPolDataSource) Schema(ctx context.Context, req datasourc Computed: true, MarkdownDescription: `The NetFlow Exporter Version of the NetFlow Exporter Policy object.`, }, - "relation_to_vrf": schema.SetNestedAttribute{ + "relation_to_vrf": schema.SingleNestedAttribute{ MarkdownDescription: `Points to the Ctx behind which the Netflow Exporter Resides`, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation From NetFlow Exporter To VRF object.`, - }, - "target_dn": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The distinguished name of the target.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation From NetFlow Exporter To VRF object.`, + }, + "target_dn": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The distinguished name of the target.`, }, }, }, - "relation_to_epg": schema.SetNestedAttribute{ + "relation_to_epg": schema.SingleNestedAttribute{ MarkdownDescription: `Points to the EPg behind which the Netflow Exporter Resides`, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation From NetFlow Exporter To EPG object.`, - }, - "target_dn": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The distinguished name of the target.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation From NetFlow Exporter To EPG object.`, + }, + "target_dn": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The distinguished name of the target.`, }, }, }, diff --git a/internal/provider/data_source_aci_netflow_monitor_policy.go b/internal/provider/data_source_aci_netflow_monitor_policy.go index ae17fc8ea..f12063190 100644 --- a/internal/provider/data_source_aci_netflow_monitor_policy.go +++ b/internal/provider/data_source_aci_netflow_monitor_policy.go @@ -89,19 +89,17 @@ func (d *NetflowMonitorPolDataSource) Schema(ctx context.Context, req datasource }, }, }, - "relation_to_netflow_record": schema.SetNestedAttribute{ + "relation_to_netflow_record": schema.SingleNestedAttribute{ MarkdownDescription: ``, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the Relation To NetFlow Record object.`, - }, - "netflow_record_policy_name": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `Name.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the Relation To NetFlow Record object.`, + }, + "netflow_record_policy_name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `Name.`, }, }, }, diff --git a/internal/provider/data_source_aci_vrf_fallback_route_group.go b/internal/provider/data_source_aci_vrf_fallback_route_group.go index d5270596a..0086672bf 100644 --- a/internal/provider/data_source_aci_vrf_fallback_route_group.go +++ b/internal/provider/data_source_aci_vrf_fallback_route_group.go @@ -92,31 +92,29 @@ func (d *FvFBRGroupDataSource) Schema(ctx context.Context, req datasource.Schema }, }, }, - "vrf_fallback_routes": schema.SetNestedAttribute{ + "vrf_fallback_route": schema.SingleNestedAttribute{ MarkdownDescription: ``, Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The annotation of the VRF Fallback Route object.`, - }, - "description": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The description of the VRF Fallback Route object.`, - }, - "prefix_address": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The prefix address of the VRF Fallback Route object.`, - }, - "name": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The name of the VRF Fallback Route object.`, - }, - "name_alias": schema.StringAttribute{ - Computed: true, - MarkdownDescription: `The name alias of the VRF Fallback Route object.`, - }, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The annotation of the VRF Fallback Route object.`, + }, + "description": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The description of the VRF Fallback Route object.`, + }, + "prefix_address": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The prefix address of the VRF Fallback Route object.`, + }, + "name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The name of the VRF Fallback Route object.`, + }, + "name_alias": schema.StringAttribute{ + Computed: true, + MarkdownDescription: `The name alias of the VRF Fallback Route object.`, }, }, }, diff --git a/internal/provider/resource_aci_access_interface_override.go b/internal/provider/resource_aci_access_interface_override.go index e0034b32d..11f1fcee7 100644 --- a/internal/provider/resource_aci_access_interface_override.go +++ b/internal/provider/resource_aci_access_interface_override.go @@ -13,12 +13,12 @@ import ( "github.com/ciscoecosystem/aci-go-client/v2/client" "github.com/ciscoecosystem/aci-go-client/v2/container" - "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" @@ -52,8 +52,8 @@ type InfraHPathSResourceModel struct { NameAlias types.String `tfsdk:"name_alias"` OwnerKey types.String `tfsdk:"owner_key"` OwnerTag types.String `tfsdk:"owner_tag"` - InfraRsHPathAtt types.Set `tfsdk:"relation_to_host_path"` - InfraRsPathToAccBaseGrp types.Set `tfsdk:"relation_to_access_interface_policy_group"` + InfraRsHPathAtt types.Object `tfsdk:"relation_to_host_path"` + InfraRsPathToAccBaseGrp types.Object `tfsdk:"relation_to_access_interface_policy_group"` TagAnnotation types.Set `tfsdk:"annotations"` TagTag types.Set `tfsdk:"tags"` } @@ -68,17 +68,13 @@ func getEmptyInfraHPathSResourceModel() *InfraHPathSResourceModel { NameAlias: basetypes.NewStringNull(), OwnerKey: basetypes.NewStringNull(), OwnerTag: basetypes.NewStringNull(), - InfraRsHPathAtt: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "target_dn": types.StringType, - }, + InfraRsHPathAtt: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "target_dn": types.StringType, }), - InfraRsPathToAccBaseGrp: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "target_dn": types.StringType, - }, + InfraRsPathToAccBaseGrp: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "target_dn": types.StringType, }), TagAnnotation: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -108,6 +104,11 @@ func getEmptyInfraRsHPathAttInfraHPathSResourceModel() InfraRsHPathAttInfraHPath } } +var InfraRsHPathAttInfraHPathSType = map[string]attr.Type{ + "annotation": types.StringType, + "target_dn": types.StringType, +} + // InfraRsPathToAccBaseGrpInfraHPathSResourceModel describes the resource data model for the children without relation ships. type InfraRsPathToAccBaseGrpInfraHPathSResourceModel struct { Annotation types.String `tfsdk:"annotation"` @@ -121,6 +122,11 @@ func getEmptyInfraRsPathToAccBaseGrpInfraHPathSResourceModel() InfraRsPathToAccB } } +var InfraRsPathToAccBaseGrpInfraHPathSType = map[string]attr.Type{ + "annotation": types.StringType, + "target_dn": types.StringType, +} + // TagAnnotationInfraHPathSResourceModel describes the resource data model for the children without relation ships. type TagAnnotationInfraHPathSResourceModel struct { Key types.String `tfsdk:"key"` @@ -153,9 +159,10 @@ type InfraHPathSIdentifier struct { func (r *InfraHPathSResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { if !req.Plan.Raw.IsNull() { - var planData, stateData *InfraHPathSResourceModel + var planData, stateData, configData *InfraHPathSResourceModel 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 resp.Diagnostics.HasError() { return @@ -171,6 +178,18 @@ func (r *InfraHPathSResource) ModifyPlan(ctx context.Context, req resource.Modif return } } + if !configData.InfraRsHPathAtt.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.InfraRsHPathAtt.Attributes()) { + InfraRsHPathAttObject, _ := types.ObjectValueFrom(ctx, InfraRsHPathAttInfraHPathSType, getEmptyInfraRsHPathAttInfraHPathSResourceModel()) + planData.InfraRsHPathAtt = InfraRsHPathAttObject + } + } + if !configData.InfraRsPathToAccBaseGrp.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.InfraRsPathToAccBaseGrp.Attributes()) { + InfraRsPathToAccBaseGrpObject, _ := types.ObjectValueFrom(ctx, InfraRsPathToAccBaseGrpInfraHPathSType, getEmptyInfraRsPathToAccBaseGrpInfraHPathSResourceModel()) + planData.InfraRsPathToAccBaseGrp = InfraRsPathToAccBaseGrpObject + } + } resp.Diagnostics.Append(resp.Plan.Set(ctx, &planData)...) } @@ -261,64 +280,58 @@ func (r *InfraHPathSResource) Schema(ctx context.Context, req resource.SchemaReq }, MarkdownDescription: `A tag for enabling clients to add their own data. For example, to indicate who created this object.`, }, - "relation_to_host_path": schema.SetNestedAttribute{ + "relation_to_host_path": schema.SingleNestedAttribute{ MarkdownDescription: ``, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + Validators: []validator.Object{ + MakeSingleNestedAttributeRequiredAttributesNotProvidedValidator("relation_to_host_path", []string{"target_dn"}), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The annotation of the Relation To Host Path object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "target_dn": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The distinguished name of the target.`, + MarkdownDescription: `The annotation of the Relation To Host Path object.`, + }, + "target_dn": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + MarkdownDescription: `The distinguished name of the target.`, }, }, }, - "relation_to_access_interface_policy_group": schema.SetNestedAttribute{ + "relation_to_access_interface_policy_group": schema.SingleNestedAttribute{ MarkdownDescription: ``, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The annotation of the Relation To Access Interface Policy Group object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "target_dn": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The distinguished name of the target.`, + MarkdownDescription: `The annotation of the Relation To Access Interface Policy Group object.`, + }, + "target_dn": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + MarkdownDescription: `The distinguished name of the target.`, }, }, }, @@ -433,12 +446,12 @@ func (r *InfraHPathSResource) Create(ctx context.Context, req resource.CreateReq tflog.Debug(ctx, fmt.Sprintf("Create of resource aci_access_interface_override with id '%s'", data.Id.ValueString())) - var infraRsHPathAttPlan, infraRsHPathAttState []InfraRsHPathAttInfraHPathSResourceModel - data.InfraRsHPathAtt.ElementsAs(ctx, &infraRsHPathAttPlan, false) - stateData.InfraRsHPathAtt.ElementsAs(ctx, &infraRsHPathAttState, false) - var infraRsPathToAccBaseGrpPlan, infraRsPathToAccBaseGrpState []InfraRsPathToAccBaseGrpInfraHPathSResourceModel - data.InfraRsPathToAccBaseGrp.ElementsAs(ctx, &infraRsPathToAccBaseGrpPlan, false) - stateData.InfraRsPathToAccBaseGrp.ElementsAs(ctx, &infraRsPathToAccBaseGrpState, false) + var infraRsHPathAttPlan, infraRsHPathAttState InfraRsHPathAttInfraHPathSResourceModel + data.InfraRsHPathAtt.As(ctx, &infraRsHPathAttPlan, basetypes.ObjectAsOptions{}) + stateData.InfraRsHPathAtt.As(ctx, &infraRsHPathAttState, basetypes.ObjectAsOptions{}) + var infraRsPathToAccBaseGrpPlan, infraRsPathToAccBaseGrpState InfraRsPathToAccBaseGrpInfraHPathSResourceModel + data.InfraRsPathToAccBaseGrp.As(ctx, &infraRsPathToAccBaseGrpPlan, basetypes.ObjectAsOptions{}) + stateData.InfraRsPathToAccBaseGrp.As(ctx, &infraRsPathToAccBaseGrpState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationInfraHPathSResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -504,12 +517,12 @@ func (r *InfraHPathSResource) Update(ctx context.Context, req resource.UpdateReq tflog.Debug(ctx, fmt.Sprintf("Update of resource aci_access_interface_override with id '%s'", data.Id.ValueString())) - var infraRsHPathAttPlan, infraRsHPathAttState []InfraRsHPathAttInfraHPathSResourceModel - data.InfraRsHPathAtt.ElementsAs(ctx, &infraRsHPathAttPlan, false) - stateData.InfraRsHPathAtt.ElementsAs(ctx, &infraRsHPathAttState, false) - var infraRsPathToAccBaseGrpPlan, infraRsPathToAccBaseGrpState []InfraRsPathToAccBaseGrpInfraHPathSResourceModel - data.InfraRsPathToAccBaseGrp.ElementsAs(ctx, &infraRsPathToAccBaseGrpPlan, false) - stateData.InfraRsPathToAccBaseGrp.ElementsAs(ctx, &infraRsPathToAccBaseGrpState, false) + var infraRsHPathAttPlan, infraRsHPathAttState InfraRsHPathAttInfraHPathSResourceModel + data.InfraRsHPathAtt.As(ctx, &infraRsHPathAttPlan, basetypes.ObjectAsOptions{}) + stateData.InfraRsHPathAtt.As(ctx, &infraRsHPathAttState, basetypes.ObjectAsOptions{}) + var infraRsPathToAccBaseGrpPlan, infraRsPathToAccBaseGrpState InfraRsPathToAccBaseGrpInfraHPathSResourceModel + data.InfraRsPathToAccBaseGrp.As(ctx, &infraRsPathToAccBaseGrpPlan, basetypes.ObjectAsOptions{}) + stateData.InfraRsPathToAccBaseGrp.As(ctx, &infraRsPathToAccBaseGrpState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationInfraHPathSResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -666,10 +679,20 @@ func getAndSetInfraHPathSAttributes(ctx context.Context, diags *diag.Diagnostics } } } - infraRsHPathAttSet, _ := types.SetValueFrom(ctx, data.InfraRsHPathAtt.ElementType(ctx), InfraRsHPathAttInfraHPathSList) - data.InfraRsHPathAtt = infraRsHPathAttSet - infraRsPathToAccBaseGrpSet, _ := types.SetValueFrom(ctx, data.InfraRsPathToAccBaseGrp.ElementType(ctx), InfraRsPathToAccBaseGrpInfraHPathSList) - data.InfraRsPathToAccBaseGrp = infraRsPathToAccBaseGrpSet + if len(InfraRsHPathAttInfraHPathSList) == 1 { + infraRsHPathAttObject, _ := types.ObjectValueFrom(ctx, InfraRsHPathAttInfraHPathSType, InfraRsHPathAttInfraHPathSList[0]) + data.InfraRsHPathAtt = infraRsHPathAttObject + } else { + infraRsHPathAttObject, _ := types.ObjectValueFrom(ctx, InfraRsHPathAttInfraHPathSType, getEmptyInfraRsHPathAttInfraHPathSResourceModel()) + data.InfraRsHPathAtt = infraRsHPathAttObject + } + if len(InfraRsPathToAccBaseGrpInfraHPathSList) == 1 { + infraRsPathToAccBaseGrpObject, _ := types.ObjectValueFrom(ctx, InfraRsPathToAccBaseGrpInfraHPathSType, InfraRsPathToAccBaseGrpInfraHPathSList[0]) + data.InfraRsPathToAccBaseGrp = infraRsPathToAccBaseGrpObject + } else { + infraRsPathToAccBaseGrpObject, _ := types.ObjectValueFrom(ctx, InfraRsPathToAccBaseGrpInfraHPathSType, getEmptyInfraRsPathToAccBaseGrpInfraHPathSResourceModel()) + data.InfraRsPathToAccBaseGrp = infraRsPathToAccBaseGrpObject + } tagAnnotationSet, _ := types.SetValueFrom(ctx, data.TagAnnotation.ElementType(ctx), TagAnnotationInfraHPathSList) data.TagAnnotation = tagAnnotationSet tagTagSet, _ := types.SetValueFrom(ctx, data.TagTag.ElementType(ctx), TagTagInfraHPathSList) @@ -716,57 +739,53 @@ func setInfraHPathSId(ctx context.Context, data *InfraHPathSResourceModel) { data.Id = types.StringValue(fmt.Sprintf("%s/%s", data.ParentDn.ValueString(), rn)) } -func getInfraHPathSInfraRsHPathAttChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *InfraHPathSResourceModel, infraRsHPathAttPlan, infraRsHPathAttState []InfraRsHPathAttInfraHPathSResourceModel) []map[string]interface{} { +func getInfraHPathSInfraRsHPathAttChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *InfraHPathSResourceModel, infraRsHPathAttPlan, infraRsHPathAttState InfraRsHPathAttInfraHPathSResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.InfraRsHPathAtt.IsUnknown() { - for _, infraRsHPathAtt := range infraRsHPathAttPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !infraRsHPathAtt.Annotation.IsUnknown() && !infraRsHPathAtt.Annotation.IsNull() { - childMap["attributes"]["annotation"] = infraRsHPathAtt.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.InfraRsHPathAtt.Attributes()) { + if !infraRsHPathAttPlan.Annotation.IsUnknown() && !infraRsHPathAttPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = infraRsHPathAttPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !infraRsHPathAtt.TDn.IsUnknown() && !infraRsHPathAtt.TDn.IsNull() { - childMap["attributes"]["tDn"] = infraRsHPathAtt.TDn.ValueString() + if !infraRsHPathAttPlan.TDn.IsUnknown() && !infraRsHPathAttPlan.TDn.IsNull() { + childMap["attributes"]["tDn"] = infraRsHPathAttPlan.TDn.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"infraRsHPathAtt": childMap}) - } - if len(infraRsHPathAttPlan) == 0 && len(infraRsHPathAttState) == 1 { - childMap := map[string]map[string]interface{}{"attributes": {}} + } else { + childMap["attributes"]["tDn"] = infraRsHPathAttState.TDn.ValueString() childMap["attributes"]["status"] = "deleted" - childMap["attributes"]["tDn"] = infraRsHPathAttState[0].TDn.ValueString() - childPayloads = append(childPayloads, map[string]interface{}{"infraRsHPathAtt": childMap}) } + childPayloads = append(childPayloads, map[string]interface{}{"infraRsHPathAtt": childMap}) } else { - data.InfraRsHPathAtt = types.SetNull(data.InfraRsHPathAtt.ElementType(ctx)) + InfraRsHPathAttObject, _ := types.ObjectValueFrom(ctx, InfraRsHPathAttInfraHPathSType, getEmptyInfraRsHPathAttInfraHPathSResourceModel()) + data.InfraRsHPathAtt = InfraRsHPathAttObject } return childPayloads } -func getInfraHPathSInfraRsPathToAccBaseGrpChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *InfraHPathSResourceModel, infraRsPathToAccBaseGrpPlan, infraRsPathToAccBaseGrpState []InfraRsPathToAccBaseGrpInfraHPathSResourceModel) []map[string]interface{} { +func getInfraHPathSInfraRsPathToAccBaseGrpChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *InfraHPathSResourceModel, infraRsPathToAccBaseGrpPlan, infraRsPathToAccBaseGrpState InfraRsPathToAccBaseGrpInfraHPathSResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.InfraRsPathToAccBaseGrp.IsUnknown() { - for _, infraRsPathToAccBaseGrp := range infraRsPathToAccBaseGrpPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !infraRsPathToAccBaseGrp.Annotation.IsUnknown() && !infraRsPathToAccBaseGrp.Annotation.IsNull() { - childMap["attributes"]["annotation"] = infraRsPathToAccBaseGrp.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.InfraRsPathToAccBaseGrp.Attributes()) { + if !infraRsPathToAccBaseGrpPlan.Annotation.IsUnknown() && !infraRsPathToAccBaseGrpPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = infraRsPathToAccBaseGrpPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !infraRsPathToAccBaseGrp.TDn.IsUnknown() && !infraRsPathToAccBaseGrp.TDn.IsNull() { - childMap["attributes"]["tDn"] = infraRsPathToAccBaseGrp.TDn.ValueString() + if !infraRsPathToAccBaseGrpPlan.TDn.IsUnknown() && !infraRsPathToAccBaseGrpPlan.TDn.IsNull() { + childMap["attributes"]["tDn"] = infraRsPathToAccBaseGrpPlan.TDn.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"infraRsPathToAccBaseGrp": childMap}) - } - if len(infraRsPathToAccBaseGrpPlan) == 0 && len(infraRsPathToAccBaseGrpState) == 1 { - childMap := map[string]map[string]interface{}{"attributes": {}} + } else { childMap["attributes"]["status"] = "deleted" - childPayloads = append(childPayloads, map[string]interface{}{"infraRsPathToAccBaseGrp": childMap}) } + childPayloads = append(childPayloads, map[string]interface{}{"infraRsPathToAccBaseGrp": childMap}) } else { - data.InfraRsPathToAccBaseGrp = types.SetNull(data.InfraRsPathToAccBaseGrp.ElementType(ctx)) + InfraRsPathToAccBaseGrpObject, _ := types.ObjectValueFrom(ctx, InfraRsPathToAccBaseGrpInfraHPathSType, getEmptyInfraRsPathToAccBaseGrpInfraHPathSResourceModel()) + data.InfraRsPathToAccBaseGrp = InfraRsPathToAccBaseGrpObject } return childPayloads @@ -850,7 +869,7 @@ func getInfraHPathSTagTagChildPayloads(ctx context.Context, diags *diag.Diagnost return childPayloads } -func getInfraHPathSCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *InfraHPathSResourceModel, infraRsHPathAttPlan, infraRsHPathAttState []InfraRsHPathAttInfraHPathSResourceModel, infraRsPathToAccBaseGrpPlan, infraRsPathToAccBaseGrpState []InfraRsPathToAccBaseGrpInfraHPathSResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationInfraHPathSResourceModel, tagTagPlan, tagTagState []TagTagInfraHPathSResourceModel) *container.Container { +func getInfraHPathSCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *InfraHPathSResourceModel, infraRsHPathAttPlan, infraRsHPathAttState InfraRsHPathAttInfraHPathSResourceModel, infraRsPathToAccBaseGrpPlan, infraRsPathToAccBaseGrpState InfraRsPathToAccBaseGrpInfraHPathSResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationInfraHPathSResourceModel, tagTagPlan, tagTagState []TagTagInfraHPathSResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} diff --git a/internal/provider/resource_aci_access_interface_override_test.go b/internal/provider/resource_aci_access_interface_override_test.go index 013c13939..d7e4e94a1 100644 --- a/internal/provider/resource_aci_access_interface_override_test.go +++ b/internal/provider/resource_aci_access_interface_override_test.go @@ -9,6 +9,9 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" ) func TestAccResourceInfraHPathS(t *testing.T) { @@ -149,12 +152,10 @@ func TestAccResourceInfraHPathS(t *testing.T) { resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.1.key", "key_1"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.1.value", "test_value"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.#", "2"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.0.target_dn", "uni/infra/funcprof/accportgrp-access_interface_policy_group"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.#", "1"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.0.target_dn", "topology/pod-1/paths-101/pathep-[eth1/1]"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.#", "1"), + resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.target_dn", "uni/infra/funcprof/accportgrp-access_interface_policy_group"), + resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.target_dn", "topology/pod-1/paths-101/pathep-[eth1/1]"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.1.key", "key_1"), @@ -176,12 +177,10 @@ func TestAccResourceInfraHPathS(t *testing.T) { resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.1.key", "key_1"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.1.value", "test_value"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.#", "2"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.0.target_dn", "uni/infra/funcprof/accportgrp-access_interface_policy_group"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.#", "1"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.0.target_dn", "topology/pod-1/paths-101/pathep-[eth1/1]"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.#", "1"), + resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.target_dn", "uni/infra/funcprof/accportgrp-access_interface_policy_group"), + resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.target_dn", "topology/pod-1/paths-101/pathep-[eth1/1]"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.1.key", "key_1"), @@ -201,12 +200,30 @@ func TestAccResourceInfraHPathS(t *testing.T) { resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.0.key", "key_1"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.0.value", "test_value"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.#", "1"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.#", "0"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.#", "0"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.0.key", "key_1"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.0.value", "test_value"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.#", "1"), ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("aci_access_interface_override.test", + tfjsonpath.New("relation_to_access_interface_policy_group"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "target_dn": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_access_interface_override.test", + tfjsonpath.New("relation_to_host_path"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "target_dn": knownvalue.Null(), + }, + ), + ), + }, }, // Update with all children removed { @@ -218,10 +235,28 @@ func TestAccResourceInfraHPathS(t *testing.T) { resource.TestCheckResourceAttr("aci_access_interface_override.test", "owner_key", ""), resource.TestCheckResourceAttr("aci_access_interface_override.test", "owner_tag", ""), resource.TestCheckResourceAttr("aci_access_interface_override.test", "annotations.#", "0"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_access_interface_policy_group.#", "0"), - resource.TestCheckResourceAttr("aci_access_interface_override.test", "relation_to_host_path.#", "0"), resource.TestCheckResourceAttr("aci_access_interface_override.test", "tags.#", "0"), ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("aci_access_interface_override.test", + tfjsonpath.New("relation_to_access_interface_policy_group"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "target_dn": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_access_interface_override.test", + tfjsonpath.New("relation_to_host_path"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "target_dn": knownvalue.Null(), + }, + ), + ), + }, }, }, }) @@ -280,18 +315,14 @@ resource "aci_access_interface_override" "test" { value = "test_value" }, ] - relation_to_access_interface_policy_group = [ - { - annotation = "annotation_1" - target_dn = "uni/infra/funcprof/accportgrp-access_interface_policy_group" - }, - ] - relation_to_host_path = [ - { - annotation = "annotation_1" - target_dn = "topology/pod-1/paths-101/pathep-[eth1/1]" - }, - ] + relation_to_access_interface_policy_group = { + annotation = "annotation_1" + target_dn = "uni/infra/funcprof/accportgrp-access_interface_policy_group" + } + relation_to_host_path = { + annotation = "annotation_1" + target_dn = "topology/pod-1/paths-101/pathep-[eth1/1]" + } tags = [ { key = "key_0" @@ -320,8 +351,8 @@ resource "aci_access_interface_override" "test" { value = "test_value" }, ] - relation_to_access_interface_policy_group = [] - relation_to_host_path = [] + relation_to_access_interface_policy_group = {} + relation_to_host_path = {} tags = [ { key = "key_1" @@ -335,8 +366,8 @@ const testConfigInfraHPathSChildrenRemoveAll = testChildDependencyConfigInfraHPa resource "aci_access_interface_override" "test" { name = "host_path_selector" annotations = [] - relation_to_access_interface_policy_group = [] - relation_to_host_path = [] + relation_to_access_interface_policy_group = {} + relation_to_host_path = {} tags = [] } ` diff --git a/internal/provider/resource_aci_application_epg.go b/internal/provider/resource_aci_application_epg.go index 8d5b37496..40a416254 100644 --- a/internal/provider/resource_aci_application_epg.go +++ b/internal/provider/resource_aci_application_epg.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" @@ -64,14 +65,14 @@ type FvAEPgResourceModel struct { PrefGrMemb types.String `tfsdk:"preferred_group_member"` Prio customTypes.FvAEPgPrioStringValue `tfsdk:"priority"` Shutdown types.String `tfsdk:"admin_state"` - FvCrtrn types.Set `tfsdk:"epg_useg_block_statement"` - FvRsAEPgMonPol types.Set `tfsdk:"relation_to_application_epg_monitoring_policy"` - FvRsBd types.Set `tfsdk:"relation_to_bridge_domain"` + FvCrtrn types.Object `tfsdk:"epg_useg_block_statement"` + FvRsAEPgMonPol types.Object `tfsdk:"relation_to_application_epg_monitoring_policy"` + FvRsBd types.Object `tfsdk:"relation_to_bridge_domain"` FvRsCons types.Set `tfsdk:"relation_to_consumed_contracts"` FvRsConsIf types.Set `tfsdk:"relation_to_imported_contracts"` - FvRsCustQosPol types.Set `tfsdk:"relation_to_custom_qos_policy"` + FvRsCustQosPol types.Object `tfsdk:"relation_to_custom_qos_policy"` FvRsDomAtt types.Set `tfsdk:"relation_to_domains"` - FvRsDppPol types.Set `tfsdk:"relation_to_data_plane_policing_policy"` + FvRsDppPol types.Object `tfsdk:"relation_to_data_plane_policing_policy"` FvRsFcPathAtt types.Set `tfsdk:"relation_to_fibre_channel_paths"` FvRsIntraEpg types.Set `tfsdk:"relation_to_intra_epg_contracts"` FvRsNodeAtt types.Set `tfsdk:"relation_to_static_leafs"` @@ -79,7 +80,7 @@ type FvAEPgResourceModel struct { FvRsProtBy types.Set `tfsdk:"relation_to_taboo_contracts"` FvRsProv types.Set `tfsdk:"relation_to_provided_contracts"` FvRsSecInherited types.Set `tfsdk:"relation_to_contract_masters"` - FvRsTrustCtrl types.Set `tfsdk:"relation_to_trust_control_policy"` + FvRsTrustCtrl types.Object `tfsdk:"relation_to_trust_control_policy"` TagAnnotation types.Set `tfsdk:"annotations"` TagTag types.Set `tfsdk:"tags"` DeprecatedExceptionTag types.String `tfsdk:"exception_tag"` @@ -128,30 +129,24 @@ func getEmptyFvAEPgResourceModel() *FvAEPgResourceModel { PrefGrMemb: basetypes.NewStringNull(), Prio: customTypes.NewFvAEPgPrioStringNull(), Shutdown: basetypes.NewStringNull(), - FvCrtrn: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "description": types.StringType, - "match": types.StringType, - "name": types.StringType, - "name_alias": types.StringType, - "owner_key": types.StringType, - "owner_tag": types.StringType, - "precedence": types.StringType, - "scope": types.StringType, - }, + FvCrtrn: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "description": types.StringType, + "match": types.StringType, + "name": types.StringType, + "name_alias": types.StringType, + "owner_key": types.StringType, + "owner_tag": types.StringType, + "precedence": types.StringType, + "scope": types.StringType, }), - FvRsAEPgMonPol: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "monitoring_policy_name": types.StringType, - }, + FvRsAEPgMonPol: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "monitoring_policy_name": types.StringType, }), - FvRsBd: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "bridge_domain_name": types.StringType, - }, + FvRsBd: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "bridge_domain_name": types.StringType, }), FvRsCons: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -167,11 +162,9 @@ func getEmptyFvAEPgResourceModel() *FvAEPgResourceModel { "imported_contract_name": types.StringType, }, }), - FvRsCustQosPol: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "custom_qos_policy_name": types.StringType, - }, + FvRsCustQosPol: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "custom_qos_policy_name": types.StringType, }), FvRsDomAtt: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -203,11 +196,9 @@ func getEmptyFvAEPgResourceModel() *FvAEPgResourceModel { "vnet_only": types.StringType, }, }), - FvRsDppPol: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "data_plane_policing_policy_name": types.StringType, - }, + FvRsDppPol: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "data_plane_policing_policy_name": types.StringType, }), FvRsFcPathAtt: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -265,11 +256,9 @@ func getEmptyFvAEPgResourceModel() *FvAEPgResourceModel { "target_dn": types.StringType, }, }), - FvRsTrustCtrl: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "trust_control_policy_name": types.StringType, - }, + FvRsTrustCtrl: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "trust_control_policy_name": types.StringType, }), TagAnnotation: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -338,6 +327,18 @@ func getEmptyFvCrtrnFvAEPgResourceModel() FvCrtrnFvAEPgResourceModel { } } +var FvCrtrnFvAEPgType = map[string]attr.Type{ + "annotation": types.StringType, + "description": types.StringType, + "match": types.StringType, + "name": types.StringType, + "name_alias": types.StringType, + "owner_key": types.StringType, + "owner_tag": types.StringType, + "precedence": types.StringType, + "scope": types.StringType, +} + // FvRsAEPgMonPolFvAEPgResourceModel describes the resource data model for the children without relation ships. type FvRsAEPgMonPolFvAEPgResourceModel struct { Annotation types.String `tfsdk:"annotation"` @@ -351,6 +352,11 @@ func getEmptyFvRsAEPgMonPolFvAEPgResourceModel() FvRsAEPgMonPolFvAEPgResourceMod } } +var FvRsAEPgMonPolFvAEPgType = map[string]attr.Type{ + "annotation": types.StringType, + "monitoring_policy_name": types.StringType, +} + // FvRsBdFvAEPgResourceModel describes the resource data model for the children without relation ships. type FvRsBdFvAEPgResourceModel struct { Annotation types.String `tfsdk:"annotation"` @@ -364,6 +370,11 @@ func getEmptyFvRsBdFvAEPgResourceModel() FvRsBdFvAEPgResourceModel { } } +var FvRsBdFvAEPgType = map[string]attr.Type{ + "annotation": types.StringType, + "bridge_domain_name": types.StringType, +} + // FvRsConsFvAEPgResourceModel describes the resource data model for the children without relation ships. type FvRsConsFvAEPgResourceModel struct { Annotation types.String `tfsdk:"annotation"` @@ -407,6 +418,11 @@ func getEmptyFvRsCustQosPolFvAEPgResourceModel() FvRsCustQosPolFvAEPgResourceMod } } +var FvRsCustQosPolFvAEPgType = map[string]attr.Type{ + "annotation": types.StringType, + "custom_qos_policy_name": types.StringType, +} + // FvRsDomAttFvAEPgResourceModel describes the resource data model for the children without relation ships. type FvRsDomAttFvAEPgResourceModel struct { Annotation types.String `tfsdk:"annotation"` @@ -481,6 +497,11 @@ func getEmptyFvRsDppPolFvAEPgResourceModel() FvRsDppPolFvAEPgResourceModel { } } +var FvRsDppPolFvAEPgType = map[string]attr.Type{ + "annotation": types.StringType, + "data_plane_policing_policy_name": types.StringType, +} + // FvRsFcPathAttFvAEPgResourceModel describes the resource data model for the children without relation ships. type FvRsFcPathAttFvAEPgResourceModel struct { Annotation types.String `tfsdk:"annotation"` @@ -613,6 +634,11 @@ func getEmptyFvRsTrustCtrlFvAEPgResourceModel() FvRsTrustCtrlFvAEPgResourceModel } } +var FvRsTrustCtrlFvAEPgType = map[string]attr.Type{ + "annotation": types.StringType, + "trust_control_policy_name": types.StringType, +} + // TagAnnotationFvAEPgResourceModel describes the resource data model for the children without relation ships. type TagAnnotationFvAEPgResourceModel struct { Key types.String `tfsdk:"key"` @@ -939,51 +965,22 @@ func (r *FvAEPgResource) UpgradeState(ctx context.Context) map[int64]resource.St DeprecatedFvRsTrustCtrl: priorStateData.FvRsTrustCtrl, } - upgradedStateData.FvCrtrn = types.SetNull( - types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "description": basetypes.StringType{}, - "match": basetypes.StringType{}, - "name": basetypes.StringType{}, - "name_alias": basetypes.StringType{}, - "owner_key": basetypes.StringType{}, - "owner_tag": basetypes.StringType{}, - "precedence": basetypes.StringType{}, - "scope": basetypes.StringType{}, - }, - }, - ) + // FvCrtrn is a child of FvAEPg. + upgradedStateData.FvCrtrn = types.ObjectNull(FvCrtrnFvAEPgType) - FvRsAEPgMonPolList := make([]FvRsAEPgMonPolFvAEPgResourceModel, 0) - FvRsAEPgMonPol := FvRsAEPgMonPolFvAEPgResourceModel{ + FvRsAEPgMonPolObject := FvRsAEPgMonPolFvAEPgResourceModel{ Annotation: basetypes.NewStringNull(), TnMonEPGPolName: basetypes.NewStringValue(GetMOName(priorStateData.FvRsAEPgMonPol.ValueString())), } - FvRsAEPgMonPolList = append(FvRsAEPgMonPolList, FvRsAEPgMonPol) - FvRsAEPgMonPolType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "monitoring_policy_name": basetypes.StringType{}, - }, - } - FvRsAEPgMonPolSet, _ := types.SetValueFrom(ctx, FvRsAEPgMonPolType, FvRsAEPgMonPolList) - upgradedStateData.FvRsAEPgMonPol = FvRsAEPgMonPolSet + fvRsAEPgMonPolObject, _ := types.ObjectValueFrom(ctx, FvRsAEPgMonPolFvAEPgType, FvRsAEPgMonPolObject) + upgradedStateData.FvRsAEPgMonPol = fvRsAEPgMonPolObject - FvRsBdList := make([]FvRsBdFvAEPgResourceModel, 0) - FvRsBd := FvRsBdFvAEPgResourceModel{ + FvRsBdObject := FvRsBdFvAEPgResourceModel{ Annotation: basetypes.NewStringNull(), TnFvBDName: basetypes.NewStringValue(GetMOName(priorStateData.FvRsBd.ValueString())), } - FvRsBdList = append(FvRsBdList, FvRsBd) - FvRsBdType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "bridge_domain_name": basetypes.StringType{}, - }, - } - FvRsBdSet, _ := types.SetValueFrom(ctx, FvRsBdType, FvRsBdList) - upgradedStateData.FvRsBd = FvRsBdSet + fvRsBdObject, _ := types.ObjectValueFrom(ctx, FvRsBdFvAEPgType, FvRsBdObject) + upgradedStateData.FvRsBd = fvRsBdObject FvRsConsList := make([]FvRsConsFvAEPgResourceModel, 0) var priorStateDataFvRsConsList []string @@ -1025,20 +1022,12 @@ func (r *FvAEPgResource) UpgradeState(ctx context.Context) map[int64]resource.St FvRsConsIfSet, _ := types.SetValueFrom(ctx, FvRsConsIfType, FvRsConsIfList) upgradedStateData.FvRsConsIf = FvRsConsIfSet - FvRsCustQosPolList := make([]FvRsCustQosPolFvAEPgResourceModel, 0) - FvRsCustQosPol := FvRsCustQosPolFvAEPgResourceModel{ + FvRsCustQosPolObject := FvRsCustQosPolFvAEPgResourceModel{ Annotation: basetypes.NewStringNull(), TnQosCustomPolName: basetypes.NewStringValue(GetMOName(priorStateData.FvRsCustQosPol.ValueString())), } - FvRsCustQosPolList = append(FvRsCustQosPolList, FvRsCustQosPol) - FvRsCustQosPolType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "custom_qos_policy_name": basetypes.StringType{}, - }, - } - FvRsCustQosPolSet, _ := types.SetValueFrom(ctx, FvRsCustQosPolType, FvRsCustQosPolList) - upgradedStateData.FvRsCustQosPol = FvRsCustQosPolSet + fvRsCustQosPolObject, _ := types.ObjectValueFrom(ctx, FvRsCustQosPolFvAEPgType, FvRsCustQosPolObject) + upgradedStateData.FvRsCustQosPol = fvRsCustQosPolObject upgradedStateData.FvRsDomAtt = types.SetNull( types.ObjectType{ @@ -1073,20 +1062,12 @@ func (r *FvAEPgResource) UpgradeState(ctx context.Context) map[int64]resource.St }, ) - FvRsDppPolList := make([]FvRsDppPolFvAEPgResourceModel, 0) - FvRsDppPol := FvRsDppPolFvAEPgResourceModel{ + FvRsDppPolObject := FvRsDppPolFvAEPgResourceModel{ Annotation: basetypes.NewStringNull(), TnQosDppPolName: basetypes.NewStringValue(GetMOName(priorStateData.FvRsDppPol.ValueString())), } - FvRsDppPolList = append(FvRsDppPolList, FvRsDppPol) - FvRsDppPolType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "data_plane_policing_policy_name": basetypes.StringType{}, - }, - } - FvRsDppPolSet, _ := types.SetValueFrom(ctx, FvRsDppPolType, FvRsDppPolList) - upgradedStateData.FvRsDppPol = FvRsDppPolSet + fvRsDppPolObject, _ := types.ObjectValueFrom(ctx, FvRsDppPolFvAEPgType, FvRsDppPolObject) + upgradedStateData.FvRsDppPol = fvRsDppPolObject FvRsFcPathAttList := make([]FvRsFcPathAttFvAEPgResourceModel, 0) var priorStateDataFvRsFcPathAttList []string @@ -1246,20 +1227,12 @@ func (r *FvAEPgResource) UpgradeState(ctx context.Context) map[int64]resource.St FvRsSecInheritedSet, _ := types.SetValueFrom(ctx, FvRsSecInheritedType, FvRsSecInheritedList) upgradedStateData.FvRsSecInherited = FvRsSecInheritedSet - FvRsTrustCtrlList := make([]FvRsTrustCtrlFvAEPgResourceModel, 0) - FvRsTrustCtrl := FvRsTrustCtrlFvAEPgResourceModel{ + FvRsTrustCtrlObject := FvRsTrustCtrlFvAEPgResourceModel{ Annotation: basetypes.NewStringNull(), TnFhsTrustCtrlPolName: basetypes.NewStringValue(GetMOName(priorStateData.FvRsTrustCtrl.ValueString())), } - FvRsTrustCtrlList = append(FvRsTrustCtrlList, FvRsTrustCtrl) - FvRsTrustCtrlType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "trust_control_policy_name": basetypes.StringType{}, - }, - } - FvRsTrustCtrlSet, _ := types.SetValueFrom(ctx, FvRsTrustCtrlType, FvRsTrustCtrlList) - upgradedStateData.FvRsTrustCtrl = FvRsTrustCtrlSet + fvRsTrustCtrlObject, _ := types.ObjectValueFrom(ctx, FvRsTrustCtrlFvAEPgType, FvRsTrustCtrlObject) + upgradedStateData.FvRsTrustCtrl = fvRsTrustCtrlObject upgradedStateData.TagAnnotation = types.SetNull( types.ObjectType{ @@ -1521,6 +1494,42 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan return } } + if !configData.FvCrtrn.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.FvCrtrn.Attributes()) { + FvCrtrnObject, _ := types.ObjectValueFrom(ctx, FvCrtrnFvAEPgType, getEmptyFvCrtrnFvAEPgResourceModel()) + planData.FvCrtrn = FvCrtrnObject + } + } + if !configData.FvRsAEPgMonPol.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.FvRsAEPgMonPol.Attributes()) { + FvRsAEPgMonPolObject, _ := types.ObjectValueFrom(ctx, FvRsAEPgMonPolFvAEPgType, getEmptyFvRsAEPgMonPolFvAEPgResourceModel()) + planData.FvRsAEPgMonPol = FvRsAEPgMonPolObject + } + } + if !configData.FvRsBd.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.FvRsBd.Attributes()) { + FvRsBdObject, _ := types.ObjectValueFrom(ctx, FvRsBdFvAEPgType, getEmptyFvRsBdFvAEPgResourceModel()) + planData.FvRsBd = FvRsBdObject + } + } + if !configData.FvRsCustQosPol.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.FvRsCustQosPol.Attributes()) { + FvRsCustQosPolObject, _ := types.ObjectValueFrom(ctx, FvRsCustQosPolFvAEPgType, getEmptyFvRsCustQosPolFvAEPgResourceModel()) + planData.FvRsCustQosPol = FvRsCustQosPolObject + } + } + if !configData.FvRsDppPol.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.FvRsDppPol.Attributes()) { + FvRsDppPolObject, _ := types.ObjectValueFrom(ctx, FvRsDppPolFvAEPgType, getEmptyFvRsDppPolFvAEPgResourceModel()) + planData.FvRsDppPol = FvRsDppPolObject + } + } + if !configData.FvRsTrustCtrl.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.FvRsTrustCtrl.Attributes()) { + FvRsTrustCtrlObject, _ := types.ObjectValueFrom(ctx, FvRsTrustCtrlFvAEPgType, getEmptyFvRsTrustCtrlFvAEPgResourceModel()) + planData.FvRsTrustCtrl = FvRsTrustCtrlObject + } + } if !configData.ExceptionTag.IsNull() { planData.DeprecatedExceptionTag = configData.ExceptionTag @@ -1624,60 +1633,49 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan } if !configData.FvRsAEPgMonPol.IsNull() && stateData != nil { - var attributeValues []FvRsAEPgMonPolFvAEPgResourceModel - configData.FvRsAEPgMonPol.ElementsAs(ctx, &attributeValues, false) - for _, attributeValue := range attributeValues { - if GetMOName(stateData.DeprecatedFvRsAEPgMonPol.ValueString()) == attributeValue.TnMonEPGPolName.ValueString() { + if IsEmptySingleNestedAttribute(configData.FvRsAEPgMonPol.Attributes()) { + planData.FvRsAEPgMonPol = configData.FvRsAEPgMonPol + planData.DeprecatedFvRsAEPgMonPol = basetypes.NewStringNull() + } else { + var attributeValues FvRsAEPgMonPolFvAEPgResourceModel + configData.FvRsAEPgMonPol.As(ctx, &attributeValues, basetypes.ObjectAsOptions{}) + if GetMOName(stateData.DeprecatedFvRsAEPgMonPol.ValueString()) == attributeValues.TnMonEPGPolName.ValueString() && !attributeValues.TnMonEPGPolName.IsNull() { planData.DeprecatedFvRsAEPgMonPol = stateData.DeprecatedFvRsAEPgMonPol } } } else if !configData.DeprecatedFvRsAEPgMonPol.IsNull() { - FvRsAEPgMonPolList := make([]FvRsAEPgMonPolFvAEPgResourceModel, 0) FvRsAEPgMonPol := FvRsAEPgMonPolFvAEPgResourceModel{ Annotation: planData.Annotation, TnMonEPGPolName: basetypes.NewStringValue(GetMOName(configData.DeprecatedFvRsAEPgMonPol.ValueString())), } - FvRsAEPgMonPolList = append(FvRsAEPgMonPolList, FvRsAEPgMonPol) - FvRsAEPgMonPolType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "monitoring_policy_name": basetypes.StringType{}, - }, - } - FvRsAEPgMonPolSet, _ := types.SetValueFrom(ctx, FvRsAEPgMonPolType, FvRsAEPgMonPolList) - planData.FvRsAEPgMonPol = FvRsAEPgMonPolSet + FvRsAEPgMonPolObject, _ := types.ObjectValueFrom(ctx, FvRsAEPgMonPolFvAEPgType, FvRsAEPgMonPol) + planData.FvRsAEPgMonPol = FvRsAEPgMonPolObject } else if stateData != nil { // used to replace use state for unknown planData.DeprecatedFvRsAEPgMonPol = stateData.DeprecatedFvRsAEPgMonPol } if !configData.FvRsBd.IsNull() && stateData != nil { - var attributeValues []FvRsBdFvAEPgResourceModel - configData.FvRsBd.ElementsAs(ctx, &attributeValues, false) - for _, attributeValue := range attributeValues { - if GetMOName(stateData.DeprecatedFvRsBd.ValueString()) == attributeValue.TnFvBDName.ValueString() { + if IsEmptySingleNestedAttribute(configData.FvRsBd.Attributes()) { + planData.FvRsBd = configData.FvRsBd + planData.DeprecatedFvRsBd = basetypes.NewStringNull() + } else { + var attributeValues FvRsBdFvAEPgResourceModel + configData.FvRsBd.As(ctx, &attributeValues, basetypes.ObjectAsOptions{}) + if GetMOName(stateData.DeprecatedFvRsBd.ValueString()) == attributeValues.TnFvBDName.ValueString() && !attributeValues.TnFvBDName.IsNull() { planData.DeprecatedFvRsBd = stateData.DeprecatedFvRsBd } } } else if !configData.DeprecatedFvRsBd.IsNull() { - FvRsBdList := make([]FvRsBdFvAEPgResourceModel, 0) FvRsBd := FvRsBdFvAEPgResourceModel{ Annotation: planData.Annotation, TnFvBDName: basetypes.NewStringValue(GetMOName(configData.DeprecatedFvRsBd.ValueString())), } - FvRsBdList = append(FvRsBdList, FvRsBd) - FvRsBdType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "bridge_domain_name": basetypes.StringType{}, - }, - } - FvRsBdSet, _ := types.SetValueFrom(ctx, FvRsBdType, FvRsBdList) - planData.FvRsBd = FvRsBdSet + FvRsBdObject, _ := types.ObjectValueFrom(ctx, FvRsBdFvAEPgType, FvRsBd) + planData.FvRsBd = FvRsBdObject } else if stateData != nil { // used to replace use state for unknown planData.DeprecatedFvRsBd = stateData.DeprecatedFvRsBd } - // HasNamedProperties true if !configData.FvRsCons.IsNull() && stateData != nil { var attributeValues []FvRsConsFvAEPgResourceModel var newAttributeValues, stateAttributeValues []string @@ -1744,7 +1742,6 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan planData.DeprecatedFvRsCons = stateData.DeprecatedFvRsCons } - // HasNamedProperties false if !configData.FvRsSecInherited.IsNull() && stateData != nil { var attributeValues []FvRsSecInheritedFvAEPgResourceModel var newAttributeValues, stateAttributeValues []string @@ -1809,60 +1806,49 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan } if !configData.FvRsCustQosPol.IsNull() && stateData != nil { - var attributeValues []FvRsCustQosPolFvAEPgResourceModel - configData.FvRsCustQosPol.ElementsAs(ctx, &attributeValues, false) - for _, attributeValue := range attributeValues { - if GetMOName(stateData.DeprecatedFvRsCustQosPol.ValueString()) == attributeValue.TnQosCustomPolName.ValueString() { + if IsEmptySingleNestedAttribute(configData.FvRsCustQosPol.Attributes()) { + planData.FvRsCustQosPol = configData.FvRsCustQosPol + planData.DeprecatedFvRsCustQosPol = basetypes.NewStringNull() + } else { + var attributeValues FvRsCustQosPolFvAEPgResourceModel + configData.FvRsCustQosPol.As(ctx, &attributeValues, basetypes.ObjectAsOptions{}) + if GetMOName(stateData.DeprecatedFvRsCustQosPol.ValueString()) == attributeValues.TnQosCustomPolName.ValueString() && !attributeValues.TnQosCustomPolName.IsNull() { planData.DeprecatedFvRsCustQosPol = stateData.DeprecatedFvRsCustQosPol } } } else if !configData.DeprecatedFvRsCustQosPol.IsNull() { - FvRsCustQosPolList := make([]FvRsCustQosPolFvAEPgResourceModel, 0) FvRsCustQosPol := FvRsCustQosPolFvAEPgResourceModel{ Annotation: planData.Annotation, TnQosCustomPolName: basetypes.NewStringValue(GetMOName(configData.DeprecatedFvRsCustQosPol.ValueString())), } - FvRsCustQosPolList = append(FvRsCustQosPolList, FvRsCustQosPol) - FvRsCustQosPolType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "custom_qos_policy_name": basetypes.StringType{}, - }, - } - FvRsCustQosPolSet, _ := types.SetValueFrom(ctx, FvRsCustQosPolType, FvRsCustQosPolList) - planData.FvRsCustQosPol = FvRsCustQosPolSet + FvRsCustQosPolObject, _ := types.ObjectValueFrom(ctx, FvRsCustQosPolFvAEPgType, FvRsCustQosPol) + planData.FvRsCustQosPol = FvRsCustQosPolObject } else if stateData != nil { // used to replace use state for unknown planData.DeprecatedFvRsCustQosPol = stateData.DeprecatedFvRsCustQosPol } if !configData.FvRsDppPol.IsNull() && stateData != nil { - var attributeValues []FvRsDppPolFvAEPgResourceModel - configData.FvRsDppPol.ElementsAs(ctx, &attributeValues, false) - for _, attributeValue := range attributeValues { - if GetMOName(stateData.DeprecatedFvRsDppPol.ValueString()) == attributeValue.TnQosDppPolName.ValueString() { + if IsEmptySingleNestedAttribute(configData.FvRsDppPol.Attributes()) { + planData.FvRsDppPol = configData.FvRsDppPol + planData.DeprecatedFvRsDppPol = basetypes.NewStringNull() + } else { + var attributeValues FvRsDppPolFvAEPgResourceModel + configData.FvRsDppPol.As(ctx, &attributeValues, basetypes.ObjectAsOptions{}) + if GetMOName(stateData.DeprecatedFvRsDppPol.ValueString()) == attributeValues.TnQosDppPolName.ValueString() && !attributeValues.TnQosDppPolName.IsNull() { planData.DeprecatedFvRsDppPol = stateData.DeprecatedFvRsDppPol } } } else if !configData.DeprecatedFvRsDppPol.IsNull() { - FvRsDppPolList := make([]FvRsDppPolFvAEPgResourceModel, 0) FvRsDppPol := FvRsDppPolFvAEPgResourceModel{ Annotation: planData.Annotation, TnQosDppPolName: basetypes.NewStringValue(GetMOName(configData.DeprecatedFvRsDppPol.ValueString())), } - FvRsDppPolList = append(FvRsDppPolList, FvRsDppPol) - FvRsDppPolType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "data_plane_policing_policy_name": basetypes.StringType{}, - }, - } - FvRsDppPolSet, _ := types.SetValueFrom(ctx, FvRsDppPolType, FvRsDppPolList) - planData.FvRsDppPol = FvRsDppPolSet + FvRsDppPolObject, _ := types.ObjectValueFrom(ctx, FvRsDppPolFvAEPgType, FvRsDppPol) + planData.FvRsDppPol = FvRsDppPolObject } else if stateData != nil { // used to replace use state for unknown planData.DeprecatedFvRsDppPol = stateData.DeprecatedFvRsDppPol } - // HasNamedProperties false if !configData.FvRsFcPathAtt.IsNull() && stateData != nil { var attributeValues []FvRsFcPathAttFvAEPgResourceModel var newAttributeValues, stateAttributeValues []string @@ -1934,7 +1920,6 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan planData.DeprecatedFvRsFcPathAtt = stateData.DeprecatedFvRsFcPathAtt } - // HasNamedProperties true if !configData.FvRsConsIf.IsNull() && stateData != nil { var attributeValues []FvRsConsIfFvAEPgResourceModel var newAttributeValues, stateAttributeValues []string @@ -2001,7 +1986,6 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan planData.DeprecatedFvRsConsIf = stateData.DeprecatedFvRsConsIf } - // HasNamedProperties true if !configData.FvRsIntraEpg.IsNull() && stateData != nil { var attributeValues []FvRsIntraEpgFvAEPgResourceModel var newAttributeValues, stateAttributeValues []string @@ -2065,7 +2049,6 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan planData.DeprecatedFvRsIntraEpg = stateData.DeprecatedFvRsIntraEpg } - // HasNamedProperties true if !configData.FvRsProv.IsNull() && stateData != nil { var attributeValues []FvRsProvFvAEPgResourceModel var newAttributeValues, stateAttributeValues []string @@ -2135,7 +2118,6 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan planData.DeprecatedFvRsProv = stateData.DeprecatedFvRsProv } - // HasNamedProperties false if !configData.FvRsPathAtt.IsNull() && stateData != nil { var attributeValues []FvRsPathAttFvAEPgResourceModel var newAttributeValues, stateAttributeValues []string @@ -2211,7 +2193,6 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan planData.DeprecatedFvRsPathAtt = stateData.DeprecatedFvRsPathAtt } - // HasNamedProperties true if !configData.FvRsProtBy.IsNull() && stateData != nil { var attributeValues []FvRsProtByFvAEPgResourceModel var newAttributeValues, stateAttributeValues []string @@ -2276,28 +2257,23 @@ func (r *FvAEPgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlan } if !configData.FvRsTrustCtrl.IsNull() && stateData != nil { - var attributeValues []FvRsTrustCtrlFvAEPgResourceModel - configData.FvRsTrustCtrl.ElementsAs(ctx, &attributeValues, false) - for _, attributeValue := range attributeValues { - if GetMOName(stateData.DeprecatedFvRsTrustCtrl.ValueString()) == attributeValue.TnFhsTrustCtrlPolName.ValueString() { + if IsEmptySingleNestedAttribute(configData.FvRsTrustCtrl.Attributes()) { + planData.FvRsTrustCtrl = configData.FvRsTrustCtrl + planData.DeprecatedFvRsTrustCtrl = basetypes.NewStringNull() + } else { + var attributeValues FvRsTrustCtrlFvAEPgResourceModel + configData.FvRsTrustCtrl.As(ctx, &attributeValues, basetypes.ObjectAsOptions{}) + if GetMOName(stateData.DeprecatedFvRsTrustCtrl.ValueString()) == attributeValues.TnFhsTrustCtrlPolName.ValueString() && !attributeValues.TnFhsTrustCtrlPolName.IsNull() { planData.DeprecatedFvRsTrustCtrl = stateData.DeprecatedFvRsTrustCtrl } } } else if !configData.DeprecatedFvRsTrustCtrl.IsNull() { - FvRsTrustCtrlList := make([]FvRsTrustCtrlFvAEPgResourceModel, 0) FvRsTrustCtrl := FvRsTrustCtrlFvAEPgResourceModel{ Annotation: planData.Annotation, TnFhsTrustCtrlPolName: basetypes.NewStringValue(GetMOName(configData.DeprecatedFvRsTrustCtrl.ValueString())), } - FvRsTrustCtrlList = append(FvRsTrustCtrlList, FvRsTrustCtrl) - FvRsTrustCtrlType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "trust_control_policy_name": basetypes.StringType{}, - }, - } - FvRsTrustCtrlSet, _ := types.SetValueFrom(ctx, FvRsTrustCtrlType, FvRsTrustCtrlList) - planData.FvRsTrustCtrl = FvRsTrustCtrlSet + FvRsTrustCtrlObject, _ := types.ObjectValueFrom(ctx, FvRsTrustCtrlFvAEPgType, FvRsTrustCtrl) + planData.FvRsTrustCtrl = FvRsTrustCtrlObject } else if stateData != nil { // used to replace use state for unknown planData.DeprecatedFvRsTrustCtrl = stateData.DeprecatedFvRsTrustCtrl } @@ -2789,169 +2765,154 @@ func (r *FvAEPgResource) Schema(ctx context.Context, req resource.SchemaRequest, }, MarkdownDescription: `Withdraw AEPg Configuration from all Nodes in the Fabric.`, }, - "epg_useg_block_statement": schema.SetNestedAttribute{ + "epg_useg_block_statement": schema.SingleNestedAttribute{ MarkdownDescription: `A criterion.`, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The annotation of the EPG uSeg Block Statement object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "description": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The description of the EPG uSeg Block Statement object.`, + Validators: []validator.String{}, + MarkdownDescription: `The annotation of the EPG uSeg Block Statement object.`, + }, + "description": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "match": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{ - stringvalidator.OneOf("all", "any"), - }, - MarkdownDescription: `The Matching Rule Type of the EPG uSeg Block Statement object.`, + Validators: []validator.String{}, + MarkdownDescription: `The description of the EPG uSeg Block Statement object.`, + }, + "match": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "name": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The name of the EPG uSeg Block Statement object.`, + Validators: []validator.String{ + stringvalidator.OneOf("all", "any"), }, - "name_alias": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The name alias of the EPG uSeg Block Statement object.`, + MarkdownDescription: `The Matching Rule Type of the EPG uSeg Block Statement object.`, + }, + "name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "owner_key": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The key for enabling clients to own their data for entity correlation.`, + Validators: []validator.String{}, + MarkdownDescription: `The name of the EPG uSeg Block Statement object.`, + }, + "name_alias": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "owner_tag": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `A tag for enabling clients to add their own data. For example, to indicate who created this object.`, + Validators: []validator.String{}, + MarkdownDescription: `The name alias of the EPG uSeg Block Statement object.`, + }, + "owner_key": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "precedence": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The precedence of the EPG uSeg Block Statement object.`, + Validators: []validator.String{}, + MarkdownDescription: `The key for enabling clients to own their data for entity correlation.`, + }, + "owner_tag": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "scope": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{ - stringvalidator.OneOf("scope-bd", "scope-vrf"), - }, - MarkdownDescription: `The scope of the EPG uSeg Block Statement object.`, + Validators: []validator.String{}, + MarkdownDescription: `A tag for enabling clients to add their own data. For example, to indicate who created this object.`, + }, + "precedence": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Validators: []validator.String{}, + MarkdownDescription: `The precedence of the EPG uSeg Block Statement object.`, + }, + "scope": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Validators: []validator.String{ + stringvalidator.OneOf("scope-bd", "scope-vrf"), }, + MarkdownDescription: `The scope of the EPG uSeg Block Statement object.`, }, }, }, - "relation_to_application_epg_monitoring_policy": schema.SetNestedAttribute{ + "relation_to_application_epg_monitoring_policy": schema.SingleNestedAttribute{ MarkdownDescription: `A source relation to the monitoring policy model for the endpoint group semantic scope. This is an internal object.`, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The annotation of the Relation To Application EPG Monitoring Policy object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "monitoring_policy_name": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The name of the monitoring policy.`, + Validators: []validator.String{}, + MarkdownDescription: `The annotation of the Relation To Application EPG Monitoring Policy object.`, + }, + "monitoring_policy_name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + Validators: []validator.String{}, + MarkdownDescription: `The name of the monitoring policy.`, }, }, }, - "relation_to_bridge_domain": schema.SetNestedAttribute{ + "relation_to_bridge_domain": schema.SingleNestedAttribute{ MarkdownDescription: `A source relation to the bridge domain associated to this endpoint group. This is an internal object.`, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The annotation of the Relation To Bridge Domain object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "bridge_domain_name": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The name of the bridge domain associated with this EPG.`, + Validators: []validator.String{}, + MarkdownDescription: `The annotation of the Relation To Bridge Domain object.`, + }, + "bridge_domain_name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + Validators: []validator.String{}, + MarkdownDescription: `The name of the bridge domain associated with this EPG.`, }, }, }, @@ -3049,36 +3010,31 @@ func (r *FvAEPgResource) Schema(ctx context.Context, req resource.SchemaRequest, }, }, }, - "relation_to_custom_qos_policy": schema.SetNestedAttribute{ + "relation_to_custom_qos_policy": schema.SingleNestedAttribute{ MarkdownDescription: `A source relation to a custom QoS policy that enables different levels of service to be assigned to network traffic, including specifications for the Differentiated Services Code Point (DSCP) value(s) and the 802.1p Dot1p priority. This is an internal object.`, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The annotation of the Relation To Custom Qos Policy object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "custom_qos_policy_name": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The Custom QoS traffic policy name.`, + Validators: []validator.String{}, + MarkdownDescription: `The annotation of the Relation To Custom Qos Policy object.`, + }, + "custom_qos_policy_name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + Validators: []validator.String{}, + MarkdownDescription: `The Custom QoS traffic policy name.`, }, }, }, @@ -3358,36 +3314,31 @@ func (r *FvAEPgResource) Schema(ctx context.Context, req resource.SchemaRequest, }, }, }, - "relation_to_data_plane_policing_policy": schema.SetNestedAttribute{ + "relation_to_data_plane_policing_policy": schema.SingleNestedAttribute{ MarkdownDescription: `Relationship for Dpp QOS policy`, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The annotation of the Relation To Data Plane Policing Policy object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "data_plane_policing_policy_name": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `Name.`, + Validators: []validator.String{}, + MarkdownDescription: `The annotation of the Relation To Data Plane Policing Policy object.`, + }, + "data_plane_policing_policy_name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + Validators: []validator.String{}, + MarkdownDescription: `Name.`, }, }, }, @@ -3770,36 +3721,31 @@ func (r *FvAEPgResource) Schema(ctx context.Context, req resource.SchemaRequest, }, }, }, - "relation_to_trust_control_policy": schema.SetNestedAttribute{ + "relation_to_trust_control_policy": schema.SingleNestedAttribute{ MarkdownDescription: `Relationship for FHS trust control`, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The annotation of the Relation To Trust Control Policy object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "trust_control_policy_name": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `Name.`, + Validators: []validator.String{}, + MarkdownDescription: `The annotation of the Relation To Trust Control Policy object.`, + }, + "trust_control_policy_name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + Validators: []validator.String{}, + MarkdownDescription: `Name.`, }, }, }, @@ -3988,30 +3934,30 @@ func (r *FvAEPgResource) Create(ctx context.Context, req resource.CreateRequest, tflog.Debug(ctx, fmt.Sprintf("Create of resource aci_application_epg with id '%s'", data.Id.ValueString())) - var fvCrtrnPlan, fvCrtrnState []FvCrtrnFvAEPgResourceModel - data.FvCrtrn.ElementsAs(ctx, &fvCrtrnPlan, false) - stateData.FvCrtrn.ElementsAs(ctx, &fvCrtrnState, false) - var fvRsAEPgMonPolPlan, fvRsAEPgMonPolState []FvRsAEPgMonPolFvAEPgResourceModel - data.FvRsAEPgMonPol.ElementsAs(ctx, &fvRsAEPgMonPolPlan, false) - stateData.FvRsAEPgMonPol.ElementsAs(ctx, &fvRsAEPgMonPolState, false) - var fvRsBdPlan, fvRsBdState []FvRsBdFvAEPgResourceModel - data.FvRsBd.ElementsAs(ctx, &fvRsBdPlan, false) - stateData.FvRsBd.ElementsAs(ctx, &fvRsBdState, false) + var fvCrtrnPlan, fvCrtrnState FvCrtrnFvAEPgResourceModel + data.FvCrtrn.As(ctx, &fvCrtrnPlan, basetypes.ObjectAsOptions{}) + stateData.FvCrtrn.As(ctx, &fvCrtrnState, basetypes.ObjectAsOptions{}) + var fvRsAEPgMonPolPlan, fvRsAEPgMonPolState FvRsAEPgMonPolFvAEPgResourceModel + data.FvRsAEPgMonPol.As(ctx, &fvRsAEPgMonPolPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsAEPgMonPol.As(ctx, &fvRsAEPgMonPolState, basetypes.ObjectAsOptions{}) + var fvRsBdPlan, fvRsBdState FvRsBdFvAEPgResourceModel + data.FvRsBd.As(ctx, &fvRsBdPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsBd.As(ctx, &fvRsBdState, basetypes.ObjectAsOptions{}) var fvRsConsPlan, fvRsConsState []FvRsConsFvAEPgResourceModel data.FvRsCons.ElementsAs(ctx, &fvRsConsPlan, false) stateData.FvRsCons.ElementsAs(ctx, &fvRsConsState, false) var fvRsConsIfPlan, fvRsConsIfState []FvRsConsIfFvAEPgResourceModel data.FvRsConsIf.ElementsAs(ctx, &fvRsConsIfPlan, false) stateData.FvRsConsIf.ElementsAs(ctx, &fvRsConsIfState, false) - var fvRsCustQosPolPlan, fvRsCustQosPolState []FvRsCustQosPolFvAEPgResourceModel - data.FvRsCustQosPol.ElementsAs(ctx, &fvRsCustQosPolPlan, false) - stateData.FvRsCustQosPol.ElementsAs(ctx, &fvRsCustQosPolState, false) + var fvRsCustQosPolPlan, fvRsCustQosPolState FvRsCustQosPolFvAEPgResourceModel + data.FvRsCustQosPol.As(ctx, &fvRsCustQosPolPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsCustQosPol.As(ctx, &fvRsCustQosPolState, basetypes.ObjectAsOptions{}) var fvRsDomAttPlan, fvRsDomAttState []FvRsDomAttFvAEPgResourceModel data.FvRsDomAtt.ElementsAs(ctx, &fvRsDomAttPlan, false) stateData.FvRsDomAtt.ElementsAs(ctx, &fvRsDomAttState, false) - var fvRsDppPolPlan, fvRsDppPolState []FvRsDppPolFvAEPgResourceModel - data.FvRsDppPol.ElementsAs(ctx, &fvRsDppPolPlan, false) - stateData.FvRsDppPol.ElementsAs(ctx, &fvRsDppPolState, false) + var fvRsDppPolPlan, fvRsDppPolState FvRsDppPolFvAEPgResourceModel + data.FvRsDppPol.As(ctx, &fvRsDppPolPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsDppPol.As(ctx, &fvRsDppPolState, basetypes.ObjectAsOptions{}) var fvRsFcPathAttPlan, fvRsFcPathAttState []FvRsFcPathAttFvAEPgResourceModel data.FvRsFcPathAtt.ElementsAs(ctx, &fvRsFcPathAttPlan, false) stateData.FvRsFcPathAtt.ElementsAs(ctx, &fvRsFcPathAttState, false) @@ -4033,9 +3979,9 @@ func (r *FvAEPgResource) Create(ctx context.Context, req resource.CreateRequest, var fvRsSecInheritedPlan, fvRsSecInheritedState []FvRsSecInheritedFvAEPgResourceModel data.FvRsSecInherited.ElementsAs(ctx, &fvRsSecInheritedPlan, false) stateData.FvRsSecInherited.ElementsAs(ctx, &fvRsSecInheritedState, false) - var fvRsTrustCtrlPlan, fvRsTrustCtrlState []FvRsTrustCtrlFvAEPgResourceModel - data.FvRsTrustCtrl.ElementsAs(ctx, &fvRsTrustCtrlPlan, false) - stateData.FvRsTrustCtrl.ElementsAs(ctx, &fvRsTrustCtrlState, false) + var fvRsTrustCtrlPlan, fvRsTrustCtrlState FvRsTrustCtrlFvAEPgResourceModel + data.FvRsTrustCtrl.As(ctx, &fvRsTrustCtrlPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsTrustCtrl.As(ctx, &fvRsTrustCtrlState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationFvAEPgResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -4094,6 +4040,20 @@ func (r *FvAEPgResource) Update(ctx context.Context, req resource.UpdateRequest, // Read Terraform plan data into the model resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + // Error out when child object fvRsBd is being deleted + if IsEmptySingleNestedAttribute(data.FvRsBd.Attributes()) && !IsEmptySingleNestedAttribute(stateData.FvRsBd.Attributes()) { + resp.Diagnostics.AddError( + "FvRsBd object cannot be deleted", + "deletion of child is only possible upon deletion of the parent", + ) + } + // Error out when child object fvRsCustQosPol is being deleted + if IsEmptySingleNestedAttribute(data.FvRsCustQosPol.Attributes()) && !IsEmptySingleNestedAttribute(stateData.FvRsCustQosPol.Attributes()) { + resp.Diagnostics.AddError( + "FvRsCustQosPol object cannot be deleted", + "deletion of child is only possible upon deletion of the parent", + ) + } if resp.Diagnostics.HasError() { return @@ -4101,30 +4061,30 @@ func (r *FvAEPgResource) Update(ctx context.Context, req resource.UpdateRequest, tflog.Debug(ctx, fmt.Sprintf("Update of resource aci_application_epg with id '%s'", data.Id.ValueString())) - var fvCrtrnPlan, fvCrtrnState []FvCrtrnFvAEPgResourceModel - data.FvCrtrn.ElementsAs(ctx, &fvCrtrnPlan, false) - stateData.FvCrtrn.ElementsAs(ctx, &fvCrtrnState, false) - var fvRsAEPgMonPolPlan, fvRsAEPgMonPolState []FvRsAEPgMonPolFvAEPgResourceModel - data.FvRsAEPgMonPol.ElementsAs(ctx, &fvRsAEPgMonPolPlan, false) - stateData.FvRsAEPgMonPol.ElementsAs(ctx, &fvRsAEPgMonPolState, false) - var fvRsBdPlan, fvRsBdState []FvRsBdFvAEPgResourceModel - data.FvRsBd.ElementsAs(ctx, &fvRsBdPlan, false) - stateData.FvRsBd.ElementsAs(ctx, &fvRsBdState, false) + var fvCrtrnPlan, fvCrtrnState FvCrtrnFvAEPgResourceModel + data.FvCrtrn.As(ctx, &fvCrtrnPlan, basetypes.ObjectAsOptions{}) + stateData.FvCrtrn.As(ctx, &fvCrtrnState, basetypes.ObjectAsOptions{}) + var fvRsAEPgMonPolPlan, fvRsAEPgMonPolState FvRsAEPgMonPolFvAEPgResourceModel + data.FvRsAEPgMonPol.As(ctx, &fvRsAEPgMonPolPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsAEPgMonPol.As(ctx, &fvRsAEPgMonPolState, basetypes.ObjectAsOptions{}) + var fvRsBdPlan, fvRsBdState FvRsBdFvAEPgResourceModel + data.FvRsBd.As(ctx, &fvRsBdPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsBd.As(ctx, &fvRsBdState, basetypes.ObjectAsOptions{}) var fvRsConsPlan, fvRsConsState []FvRsConsFvAEPgResourceModel data.FvRsCons.ElementsAs(ctx, &fvRsConsPlan, false) stateData.FvRsCons.ElementsAs(ctx, &fvRsConsState, false) var fvRsConsIfPlan, fvRsConsIfState []FvRsConsIfFvAEPgResourceModel data.FvRsConsIf.ElementsAs(ctx, &fvRsConsIfPlan, false) stateData.FvRsConsIf.ElementsAs(ctx, &fvRsConsIfState, false) - var fvRsCustQosPolPlan, fvRsCustQosPolState []FvRsCustQosPolFvAEPgResourceModel - data.FvRsCustQosPol.ElementsAs(ctx, &fvRsCustQosPolPlan, false) - stateData.FvRsCustQosPol.ElementsAs(ctx, &fvRsCustQosPolState, false) + var fvRsCustQosPolPlan, fvRsCustQosPolState FvRsCustQosPolFvAEPgResourceModel + data.FvRsCustQosPol.As(ctx, &fvRsCustQosPolPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsCustQosPol.As(ctx, &fvRsCustQosPolState, basetypes.ObjectAsOptions{}) var fvRsDomAttPlan, fvRsDomAttState []FvRsDomAttFvAEPgResourceModel data.FvRsDomAtt.ElementsAs(ctx, &fvRsDomAttPlan, false) stateData.FvRsDomAtt.ElementsAs(ctx, &fvRsDomAttState, false) - var fvRsDppPolPlan, fvRsDppPolState []FvRsDppPolFvAEPgResourceModel - data.FvRsDppPol.ElementsAs(ctx, &fvRsDppPolPlan, false) - stateData.FvRsDppPol.ElementsAs(ctx, &fvRsDppPolState, false) + var fvRsDppPolPlan, fvRsDppPolState FvRsDppPolFvAEPgResourceModel + data.FvRsDppPol.As(ctx, &fvRsDppPolPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsDppPol.As(ctx, &fvRsDppPolState, basetypes.ObjectAsOptions{}) var fvRsFcPathAttPlan, fvRsFcPathAttState []FvRsFcPathAttFvAEPgResourceModel data.FvRsFcPathAtt.ElementsAs(ctx, &fvRsFcPathAttPlan, false) stateData.FvRsFcPathAtt.ElementsAs(ctx, &fvRsFcPathAttState, false) @@ -4146,9 +4106,9 @@ func (r *FvAEPgResource) Update(ctx context.Context, req resource.UpdateRequest, var fvRsSecInheritedPlan, fvRsSecInheritedState []FvRsSecInheritedFvAEPgResourceModel data.FvRsSecInherited.ElementsAs(ctx, &fvRsSecInheritedPlan, false) stateData.FvRsSecInherited.ElementsAs(ctx, &fvRsSecInheritedState, false) - var fvRsTrustCtrlPlan, fvRsTrustCtrlState []FvRsTrustCtrlFvAEPgResourceModel - data.FvRsTrustCtrl.ElementsAs(ctx, &fvRsTrustCtrlPlan, false) - stateData.FvRsTrustCtrl.ElementsAs(ctx, &fvRsTrustCtrlState, false) + var fvRsTrustCtrlPlan, fvRsTrustCtrlState FvRsTrustCtrlFvAEPgResourceModel + data.FvRsTrustCtrl.As(ctx, &fvRsTrustCtrlPlan, basetypes.ObjectAsOptions{}) + stateData.FvRsTrustCtrl.As(ctx, &fvRsTrustCtrlState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationFvAEPgResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -4661,22 +4621,47 @@ func getAndSetFvAEPgAttributes(ctx context.Context, diags *diag.Diagnostics, cli } } } - fvCrtrnSet, _ := types.SetValueFrom(ctx, data.FvCrtrn.ElementType(ctx), FvCrtrnFvAEPgList) - data.FvCrtrn = fvCrtrnSet - fvRsAEPgMonPolSet, _ := types.SetValueFrom(ctx, data.FvRsAEPgMonPol.ElementType(ctx), FvRsAEPgMonPolFvAEPgList) - data.FvRsAEPgMonPol = fvRsAEPgMonPolSet - fvRsBdSet, _ := types.SetValueFrom(ctx, data.FvRsBd.ElementType(ctx), FvRsBdFvAEPgList) - data.FvRsBd = fvRsBdSet + if len(FvCrtrnFvAEPgList) == 1 { + fvCrtrnObject, _ := types.ObjectValueFrom(ctx, FvCrtrnFvAEPgType, FvCrtrnFvAEPgList[0]) + data.FvCrtrn = fvCrtrnObject + } else { + fvCrtrnObject, _ := types.ObjectValueFrom(ctx, FvCrtrnFvAEPgType, getEmptyFvCrtrnFvAEPgResourceModel()) + data.FvCrtrn = fvCrtrnObject + } + if len(FvRsAEPgMonPolFvAEPgList) == 1 { + fvRsAEPgMonPolObject, _ := types.ObjectValueFrom(ctx, FvRsAEPgMonPolFvAEPgType, FvRsAEPgMonPolFvAEPgList[0]) + data.FvRsAEPgMonPol = fvRsAEPgMonPolObject + } else { + fvRsAEPgMonPolObject, _ := types.ObjectValueFrom(ctx, FvRsAEPgMonPolFvAEPgType, getEmptyFvRsAEPgMonPolFvAEPgResourceModel()) + data.FvRsAEPgMonPol = fvRsAEPgMonPolObject + } + if len(FvRsBdFvAEPgList) == 1 { + fvRsBdObject, _ := types.ObjectValueFrom(ctx, FvRsBdFvAEPgType, FvRsBdFvAEPgList[0]) + data.FvRsBd = fvRsBdObject + } else { + fvRsBdObject, _ := types.ObjectValueFrom(ctx, FvRsBdFvAEPgType, getEmptyFvRsBdFvAEPgResourceModel()) + data.FvRsBd = fvRsBdObject + } fvRsConsSet, _ := types.SetValueFrom(ctx, data.FvRsCons.ElementType(ctx), FvRsConsFvAEPgList) data.FvRsCons = fvRsConsSet fvRsConsIfSet, _ := types.SetValueFrom(ctx, data.FvRsConsIf.ElementType(ctx), FvRsConsIfFvAEPgList) data.FvRsConsIf = fvRsConsIfSet - fvRsCustQosPolSet, _ := types.SetValueFrom(ctx, data.FvRsCustQosPol.ElementType(ctx), FvRsCustQosPolFvAEPgList) - data.FvRsCustQosPol = fvRsCustQosPolSet + if len(FvRsCustQosPolFvAEPgList) == 1 { + fvRsCustQosPolObject, _ := types.ObjectValueFrom(ctx, FvRsCustQosPolFvAEPgType, FvRsCustQosPolFvAEPgList[0]) + data.FvRsCustQosPol = fvRsCustQosPolObject + } else { + fvRsCustQosPolObject, _ := types.ObjectValueFrom(ctx, FvRsCustQosPolFvAEPgType, getEmptyFvRsCustQosPolFvAEPgResourceModel()) + data.FvRsCustQosPol = fvRsCustQosPolObject + } fvRsDomAttSet, _ := types.SetValueFrom(ctx, data.FvRsDomAtt.ElementType(ctx), FvRsDomAttFvAEPgList) data.FvRsDomAtt = fvRsDomAttSet - fvRsDppPolSet, _ := types.SetValueFrom(ctx, data.FvRsDppPol.ElementType(ctx), FvRsDppPolFvAEPgList) - data.FvRsDppPol = fvRsDppPolSet + if len(FvRsDppPolFvAEPgList) == 1 { + fvRsDppPolObject, _ := types.ObjectValueFrom(ctx, FvRsDppPolFvAEPgType, FvRsDppPolFvAEPgList[0]) + data.FvRsDppPol = fvRsDppPolObject + } else { + fvRsDppPolObject, _ := types.ObjectValueFrom(ctx, FvRsDppPolFvAEPgType, getEmptyFvRsDppPolFvAEPgResourceModel()) + data.FvRsDppPol = fvRsDppPolObject + } fvRsFcPathAttSet, _ := types.SetValueFrom(ctx, data.FvRsFcPathAtt.ElementType(ctx), FvRsFcPathAttFvAEPgList) data.FvRsFcPathAtt = fvRsFcPathAttSet fvRsIntraEpgSet, _ := types.SetValueFrom(ctx, data.FvRsIntraEpg.ElementType(ctx), FvRsIntraEpgFvAEPgList) @@ -4691,8 +4676,13 @@ func getAndSetFvAEPgAttributes(ctx context.Context, diags *diag.Diagnostics, cli data.FvRsProv = fvRsProvSet fvRsSecInheritedSet, _ := types.SetValueFrom(ctx, data.FvRsSecInherited.ElementType(ctx), FvRsSecInheritedFvAEPgList) data.FvRsSecInherited = fvRsSecInheritedSet - fvRsTrustCtrlSet, _ := types.SetValueFrom(ctx, data.FvRsTrustCtrl.ElementType(ctx), FvRsTrustCtrlFvAEPgList) - data.FvRsTrustCtrl = fvRsTrustCtrlSet + if len(FvRsTrustCtrlFvAEPgList) == 1 { + fvRsTrustCtrlObject, _ := types.ObjectValueFrom(ctx, FvRsTrustCtrlFvAEPgType, FvRsTrustCtrlFvAEPgList[0]) + data.FvRsTrustCtrl = fvRsTrustCtrlObject + } else { + fvRsTrustCtrlObject, _ := types.ObjectValueFrom(ctx, FvRsTrustCtrlFvAEPgType, getEmptyFvRsTrustCtrlFvAEPgResourceModel()) + data.FvRsTrustCtrl = fvRsTrustCtrlObject + } tagAnnotationSet, _ := types.SetValueFrom(ctx, data.TagAnnotation.ElementType(ctx), TagAnnotationFvAEPgList) data.TagAnnotation = tagAnnotationSet tagTagSet, _ := types.SetValueFrom(ctx, data.TagTag.ElementType(ctx), TagTagFvAEPgList) @@ -4740,106 +4730,96 @@ func setFvAEPgId(ctx context.Context, data *FvAEPgResourceModel) { data.Id = types.StringValue(fmt.Sprintf("%s/%s", data.ParentDn.ValueString(), rn)) } -func getFvAEPgFvCrtrnChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvCrtrnPlan, fvCrtrnState []FvCrtrnFvAEPgResourceModel) []map[string]interface{} { +func getFvAEPgFvCrtrnChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvCrtrnPlan, fvCrtrnState FvCrtrnFvAEPgResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.FvCrtrn.IsUnknown() { - for _, fvCrtrn := range fvCrtrnPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !fvCrtrn.Annotation.IsUnknown() && !fvCrtrn.Annotation.IsNull() { - childMap["attributes"]["annotation"] = fvCrtrn.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.FvCrtrn.Attributes()) { + if !fvCrtrnPlan.Annotation.IsUnknown() && !fvCrtrnPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = fvCrtrnPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !fvCrtrn.Descr.IsUnknown() && !fvCrtrn.Descr.IsNull() { - childMap["attributes"]["descr"] = fvCrtrn.Descr.ValueString() + if !fvCrtrnPlan.Descr.IsUnknown() && !fvCrtrnPlan.Descr.IsNull() { + childMap["attributes"]["descr"] = fvCrtrnPlan.Descr.ValueString() } - if !fvCrtrn.Match.IsUnknown() && !fvCrtrn.Match.IsNull() { - childMap["attributes"]["match"] = fvCrtrn.Match.ValueString() + if !fvCrtrnPlan.Match.IsUnknown() && !fvCrtrnPlan.Match.IsNull() { + childMap["attributes"]["match"] = fvCrtrnPlan.Match.ValueString() } - if !fvCrtrn.Name.IsUnknown() && !fvCrtrn.Name.IsNull() { - childMap["attributes"]["name"] = fvCrtrn.Name.ValueString() + if !fvCrtrnPlan.Name.IsUnknown() && !fvCrtrnPlan.Name.IsNull() { + childMap["attributes"]["name"] = fvCrtrnPlan.Name.ValueString() } - if !fvCrtrn.NameAlias.IsUnknown() && !fvCrtrn.NameAlias.IsNull() { - childMap["attributes"]["nameAlias"] = fvCrtrn.NameAlias.ValueString() + if !fvCrtrnPlan.NameAlias.IsUnknown() && !fvCrtrnPlan.NameAlias.IsNull() { + childMap["attributes"]["nameAlias"] = fvCrtrnPlan.NameAlias.ValueString() } - if !fvCrtrn.OwnerKey.IsUnknown() && !fvCrtrn.OwnerKey.IsNull() { - childMap["attributes"]["ownerKey"] = fvCrtrn.OwnerKey.ValueString() + if !fvCrtrnPlan.OwnerKey.IsUnknown() && !fvCrtrnPlan.OwnerKey.IsNull() { + childMap["attributes"]["ownerKey"] = fvCrtrnPlan.OwnerKey.ValueString() } - if !fvCrtrn.OwnerTag.IsUnknown() && !fvCrtrn.OwnerTag.IsNull() { - childMap["attributes"]["ownerTag"] = fvCrtrn.OwnerTag.ValueString() + if !fvCrtrnPlan.OwnerTag.IsUnknown() && !fvCrtrnPlan.OwnerTag.IsNull() { + childMap["attributes"]["ownerTag"] = fvCrtrnPlan.OwnerTag.ValueString() } - if !fvCrtrn.Prec.IsUnknown() && !fvCrtrn.Prec.IsNull() { - childMap["attributes"]["prec"] = fvCrtrn.Prec.ValueString() + if !fvCrtrnPlan.Prec.IsUnknown() && !fvCrtrnPlan.Prec.IsNull() { + childMap["attributes"]["prec"] = fvCrtrnPlan.Prec.ValueString() } - if !fvCrtrn.Scope.IsUnknown() && !fvCrtrn.Scope.IsNull() { - childMap["attributes"]["scope"] = fvCrtrn.Scope.ValueString() + if !fvCrtrnPlan.Scope.IsUnknown() && !fvCrtrnPlan.Scope.IsNull() { + childMap["attributes"]["scope"] = fvCrtrnPlan.Scope.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"fvCrtrn": childMap}) - } - if len(fvCrtrnPlan) == 0 && len(fvCrtrnState) == 1 { - childMap := map[string]map[string]interface{}{"attributes": {}} + } else { childMap["attributes"]["status"] = "deleted" - childPayloads = append(childPayloads, map[string]interface{}{"fvCrtrn": childMap}) } + childPayloads = append(childPayloads, map[string]interface{}{"fvCrtrn": childMap}) } else { - data.FvCrtrn = types.SetNull(data.FvCrtrn.ElementType(ctx)) + FvCrtrnObject, _ := types.ObjectValueFrom(ctx, FvCrtrnFvAEPgType, getEmptyFvCrtrnFvAEPgResourceModel()) + data.FvCrtrn = FvCrtrnObject } return childPayloads } -func getFvAEPgFvRsAEPgMonPolChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsAEPgMonPolPlan, fvRsAEPgMonPolState []FvRsAEPgMonPolFvAEPgResourceModel) []map[string]interface{} { +func getFvAEPgFvRsAEPgMonPolChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsAEPgMonPolPlan, fvRsAEPgMonPolState FvRsAEPgMonPolFvAEPgResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.FvRsAEPgMonPol.IsUnknown() { - for _, fvRsAEPgMonPol := range fvRsAEPgMonPolPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !fvRsAEPgMonPol.Annotation.IsUnknown() && !fvRsAEPgMonPol.Annotation.IsNull() { - childMap["attributes"]["annotation"] = fvRsAEPgMonPol.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.FvRsAEPgMonPol.Attributes()) { + if !fvRsAEPgMonPolPlan.Annotation.IsUnknown() && !fvRsAEPgMonPolPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = fvRsAEPgMonPolPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !fvRsAEPgMonPol.TnMonEPGPolName.IsUnknown() && !fvRsAEPgMonPol.TnMonEPGPolName.IsNull() { - childMap["attributes"]["tnMonEPGPolName"] = fvRsAEPgMonPol.TnMonEPGPolName.ValueString() + if !fvRsAEPgMonPolPlan.TnMonEPGPolName.IsUnknown() && !fvRsAEPgMonPolPlan.TnMonEPGPolName.IsNull() { + childMap["attributes"]["tnMonEPGPolName"] = fvRsAEPgMonPolPlan.TnMonEPGPolName.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"fvRsAEPgMonPol": childMap}) - } - if len(fvRsAEPgMonPolPlan) == 0 && len(fvRsAEPgMonPolState) == 1 { - childMap := map[string]map[string]interface{}{"attributes": {}} + } else { childMap["attributes"]["status"] = "deleted" - childPayloads = append(childPayloads, map[string]interface{}{"fvRsAEPgMonPol": childMap}) } + childPayloads = append(childPayloads, map[string]interface{}{"fvRsAEPgMonPol": childMap}) } else { - data.FvRsAEPgMonPol = types.SetNull(data.FvRsAEPgMonPol.ElementType(ctx)) + FvRsAEPgMonPolObject, _ := types.ObjectValueFrom(ctx, FvRsAEPgMonPolFvAEPgType, getEmptyFvRsAEPgMonPolFvAEPgResourceModel()) + data.FvRsAEPgMonPol = FvRsAEPgMonPolObject } return childPayloads } -func getFvAEPgFvRsBdChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsBdPlan, fvRsBdState []FvRsBdFvAEPgResourceModel) []map[string]interface{} { +func getFvAEPgFvRsBdChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsBdPlan, fvRsBdState FvRsBdFvAEPgResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.FvRsBd.IsUnknown() { - for _, fvRsBd := range fvRsBdPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !fvRsBd.Annotation.IsUnknown() && !fvRsBd.Annotation.IsNull() { - childMap["attributes"]["annotation"] = fvRsBd.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.FvRsBd.Attributes()) { + if !fvRsBdPlan.Annotation.IsUnknown() && !fvRsBdPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = fvRsBdPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !fvRsBd.TnFvBDName.IsUnknown() && !fvRsBd.TnFvBDName.IsNull() { - childMap["attributes"]["tnFvBDName"] = fvRsBd.TnFvBDName.ValueString() + if !fvRsBdPlan.TnFvBDName.IsUnknown() && !fvRsBdPlan.TnFvBDName.IsNull() { + childMap["attributes"]["tnFvBDName"] = fvRsBdPlan.TnFvBDName.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"fvRsBd": childMap}) - } - if len(fvRsBdPlan) == 0 && len(fvRsBdState) == 1 { - diags.AddError( - "FvRsBd object cannot be deleted", - "deletion of child is only possible upon deletion of the parent", - ) - return nil } + childPayloads = append(childPayloads, map[string]interface{}{"fvRsBd": childMap}) } else { - data.FvRsBd = types.SetNull(data.FvRsBd.ElementType(ctx)) + FvRsBdObject, _ := types.ObjectValueFrom(ctx, FvRsBdFvAEPgType, getEmptyFvRsBdFvAEPgResourceModel()) + data.FvRsBd = FvRsBdObject } return childPayloads @@ -4932,31 +4912,25 @@ func getFvAEPgFvRsConsIfChildPayloads(ctx context.Context, diags *diag.Diagnosti return childPayloads } -func getFvAEPgFvRsCustQosPolChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsCustQosPolPlan, fvRsCustQosPolState []FvRsCustQosPolFvAEPgResourceModel) []map[string]interface{} { +func getFvAEPgFvRsCustQosPolChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsCustQosPolPlan, fvRsCustQosPolState FvRsCustQosPolFvAEPgResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.FvRsCustQosPol.IsUnknown() { - for _, fvRsCustQosPol := range fvRsCustQosPolPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !fvRsCustQosPol.Annotation.IsUnknown() && !fvRsCustQosPol.Annotation.IsNull() { - childMap["attributes"]["annotation"] = fvRsCustQosPol.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.FvRsCustQosPol.Attributes()) { + if !fvRsCustQosPolPlan.Annotation.IsUnknown() && !fvRsCustQosPolPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = fvRsCustQosPolPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !fvRsCustQosPol.TnQosCustomPolName.IsUnknown() && !fvRsCustQosPol.TnQosCustomPolName.IsNull() { - childMap["attributes"]["tnQosCustomPolName"] = fvRsCustQosPol.TnQosCustomPolName.ValueString() + if !fvRsCustQosPolPlan.TnQosCustomPolName.IsUnknown() && !fvRsCustQosPolPlan.TnQosCustomPolName.IsNull() { + childMap["attributes"]["tnQosCustomPolName"] = fvRsCustQosPolPlan.TnQosCustomPolName.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"fvRsCustQosPol": childMap}) - } - if len(fvRsCustQosPolPlan) == 0 && len(fvRsCustQosPolState) == 1 { - diags.AddError( - "FvRsCustQosPol object cannot be deleted", - "deletion of child is only possible upon deletion of the parent", - ) - return nil } + childPayloads = append(childPayloads, map[string]interface{}{"fvRsCustQosPol": childMap}) } else { - data.FvRsCustQosPol = types.SetNull(data.FvRsCustQosPol.ElementType(ctx)) + FvRsCustQosPolObject, _ := types.ObjectValueFrom(ctx, FvRsCustQosPolFvAEPgType, getEmptyFvRsCustQosPolFvAEPgResourceModel()) + data.FvRsCustQosPol = FvRsCustQosPolObject } return childPayloads @@ -5074,29 +5048,27 @@ func getFvAEPgFvRsDomAttChildPayloads(ctx context.Context, diags *diag.Diagnosti return childPayloads } -func getFvAEPgFvRsDppPolChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsDppPolPlan, fvRsDppPolState []FvRsDppPolFvAEPgResourceModel) []map[string]interface{} { +func getFvAEPgFvRsDppPolChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsDppPolPlan, fvRsDppPolState FvRsDppPolFvAEPgResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.FvRsDppPol.IsUnknown() { - for _, fvRsDppPol := range fvRsDppPolPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !fvRsDppPol.Annotation.IsUnknown() && !fvRsDppPol.Annotation.IsNull() { - childMap["attributes"]["annotation"] = fvRsDppPol.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.FvRsDppPol.Attributes()) { + if !fvRsDppPolPlan.Annotation.IsUnknown() && !fvRsDppPolPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = fvRsDppPolPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !fvRsDppPol.TnQosDppPolName.IsUnknown() && !fvRsDppPol.TnQosDppPolName.IsNull() { - childMap["attributes"]["tnQosDppPolName"] = fvRsDppPol.TnQosDppPolName.ValueString() + if !fvRsDppPolPlan.TnQosDppPolName.IsUnknown() && !fvRsDppPolPlan.TnQosDppPolName.IsNull() { + childMap["attributes"]["tnQosDppPolName"] = fvRsDppPolPlan.TnQosDppPolName.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"fvRsDppPol": childMap}) - } - if len(fvRsDppPolPlan) == 0 && len(fvRsDppPolState) == 1 { - childMap := map[string]map[string]interface{}{"attributes": {}} + } else { childMap["attributes"]["status"] = "deleted" - childPayloads = append(childPayloads, map[string]interface{}{"fvRsDppPol": childMap}) } + childPayloads = append(childPayloads, map[string]interface{}{"fvRsDppPol": childMap}) } else { - data.FvRsDppPol = types.SetNull(data.FvRsDppPol.ElementType(ctx)) + FvRsDppPolObject, _ := types.ObjectValueFrom(ctx, FvRsDppPolFvAEPgType, getEmptyFvRsDppPolFvAEPgResourceModel()) + data.FvRsDppPol = FvRsDppPolObject } return childPayloads @@ -5430,29 +5402,27 @@ func getFvAEPgFvRsSecInheritedChildPayloads(ctx context.Context, diags *diag.Dia return childPayloads } -func getFvAEPgFvRsTrustCtrlChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsTrustCtrlPlan, fvRsTrustCtrlState []FvRsTrustCtrlFvAEPgResourceModel) []map[string]interface{} { +func getFvAEPgFvRsTrustCtrlChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvAEPgResourceModel, fvRsTrustCtrlPlan, fvRsTrustCtrlState FvRsTrustCtrlFvAEPgResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.FvRsTrustCtrl.IsUnknown() { - for _, fvRsTrustCtrl := range fvRsTrustCtrlPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !fvRsTrustCtrl.Annotation.IsUnknown() && !fvRsTrustCtrl.Annotation.IsNull() { - childMap["attributes"]["annotation"] = fvRsTrustCtrl.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.FvRsTrustCtrl.Attributes()) { + if !fvRsTrustCtrlPlan.Annotation.IsUnknown() && !fvRsTrustCtrlPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = fvRsTrustCtrlPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !fvRsTrustCtrl.TnFhsTrustCtrlPolName.IsUnknown() && !fvRsTrustCtrl.TnFhsTrustCtrlPolName.IsNull() { - childMap["attributes"]["tnFhsTrustCtrlPolName"] = fvRsTrustCtrl.TnFhsTrustCtrlPolName.ValueString() + if !fvRsTrustCtrlPlan.TnFhsTrustCtrlPolName.IsUnknown() && !fvRsTrustCtrlPlan.TnFhsTrustCtrlPolName.IsNull() { + childMap["attributes"]["tnFhsTrustCtrlPolName"] = fvRsTrustCtrlPlan.TnFhsTrustCtrlPolName.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"fvRsTrustCtrl": childMap}) - } - if len(fvRsTrustCtrlPlan) == 0 && len(fvRsTrustCtrlState) == 1 { - childMap := map[string]map[string]interface{}{"attributes": {}} + } else { childMap["attributes"]["status"] = "deleted" - childPayloads = append(childPayloads, map[string]interface{}{"fvRsTrustCtrl": childMap}) } + childPayloads = append(childPayloads, map[string]interface{}{"fvRsTrustCtrl": childMap}) } else { - data.FvRsTrustCtrl = types.SetNull(data.FvRsTrustCtrl.ElementType(ctx)) + FvRsTrustCtrlObject, _ := types.ObjectValueFrom(ctx, FvRsTrustCtrlFvAEPgType, getEmptyFvRsTrustCtrlFvAEPgResourceModel()) + data.FvRsTrustCtrl = FvRsTrustCtrlObject } return childPayloads @@ -5536,7 +5506,7 @@ func getFvAEPgTagTagChildPayloads(ctx context.Context, diags *diag.Diagnostics, return childPayloads } -func getFvAEPgCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvAEPgResourceModel, fvCrtrnPlan, fvCrtrnState []FvCrtrnFvAEPgResourceModel, fvRsAEPgMonPolPlan, fvRsAEPgMonPolState []FvRsAEPgMonPolFvAEPgResourceModel, fvRsBdPlan, fvRsBdState []FvRsBdFvAEPgResourceModel, fvRsConsPlan, fvRsConsState []FvRsConsFvAEPgResourceModel, fvRsConsIfPlan, fvRsConsIfState []FvRsConsIfFvAEPgResourceModel, fvRsCustQosPolPlan, fvRsCustQosPolState []FvRsCustQosPolFvAEPgResourceModel, fvRsDomAttPlan, fvRsDomAttState []FvRsDomAttFvAEPgResourceModel, fvRsDppPolPlan, fvRsDppPolState []FvRsDppPolFvAEPgResourceModel, fvRsFcPathAttPlan, fvRsFcPathAttState []FvRsFcPathAttFvAEPgResourceModel, fvRsIntraEpgPlan, fvRsIntraEpgState []FvRsIntraEpgFvAEPgResourceModel, fvRsNodeAttPlan, fvRsNodeAttState []FvRsNodeAttFvAEPgResourceModel, fvRsPathAttPlan, fvRsPathAttState []FvRsPathAttFvAEPgResourceModel, fvRsProtByPlan, fvRsProtByState []FvRsProtByFvAEPgResourceModel, fvRsProvPlan, fvRsProvState []FvRsProvFvAEPgResourceModel, fvRsSecInheritedPlan, fvRsSecInheritedState []FvRsSecInheritedFvAEPgResourceModel, fvRsTrustCtrlPlan, fvRsTrustCtrlState []FvRsTrustCtrlFvAEPgResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvAEPgResourceModel, tagTagPlan, tagTagState []TagTagFvAEPgResourceModel) *container.Container { +func getFvAEPgCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *FvAEPgResourceModel, fvCrtrnPlan, fvCrtrnState FvCrtrnFvAEPgResourceModel, fvRsAEPgMonPolPlan, fvRsAEPgMonPolState FvRsAEPgMonPolFvAEPgResourceModel, fvRsBdPlan, fvRsBdState FvRsBdFvAEPgResourceModel, fvRsConsPlan, fvRsConsState []FvRsConsFvAEPgResourceModel, fvRsConsIfPlan, fvRsConsIfState []FvRsConsIfFvAEPgResourceModel, fvRsCustQosPolPlan, fvRsCustQosPolState FvRsCustQosPolFvAEPgResourceModel, fvRsDomAttPlan, fvRsDomAttState []FvRsDomAttFvAEPgResourceModel, fvRsDppPolPlan, fvRsDppPolState FvRsDppPolFvAEPgResourceModel, fvRsFcPathAttPlan, fvRsFcPathAttState []FvRsFcPathAttFvAEPgResourceModel, fvRsIntraEpgPlan, fvRsIntraEpgState []FvRsIntraEpgFvAEPgResourceModel, fvRsNodeAttPlan, fvRsNodeAttState []FvRsNodeAttFvAEPgResourceModel, fvRsPathAttPlan, fvRsPathAttState []FvRsPathAttFvAEPgResourceModel, fvRsProtByPlan, fvRsProtByState []FvRsProtByFvAEPgResourceModel, fvRsProvPlan, fvRsProvState []FvRsProvFvAEPgResourceModel, fvRsSecInheritedPlan, fvRsSecInheritedState []FvRsSecInheritedFvAEPgResourceModel, fvRsTrustCtrlPlan, fvRsTrustCtrlState FvRsTrustCtrlFvAEPgResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationFvAEPgResourceModel, tagTagPlan, tagTagState []TagTagFvAEPgResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} diff --git a/internal/provider/resource_aci_application_epg_test.go b/internal/provider/resource_aci_application_epg_test.go index 89872e050..fce05ad0b 100644 --- a/internal/provider/resource_aci_application_epg_test.go +++ b/internal/provider/resource_aci_application_epg_test.go @@ -9,6 +9,9 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" ) func TestAccResourceFvAEPgWithFvAp(t *testing.T) { @@ -223,19 +226,19 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.0.value", "value_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.1.key", "key_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.1.value", "test_value"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.description", "description_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.match", "all"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.name", "criterion"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.name_alias", "name_alias_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.owner_key", "owner_key_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.owner_tag", "owner_tag_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.precedence", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.scope", "scope-bd"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.0.monitoring_policy_name", "monitoring_policy_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.0.bridge_domain_name", "bridge_domain_name_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.description", "description_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.match", "all"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.name", "criterion"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.name_alias", "name_alias_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.owner_key", "owner_key_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.owner_tag", "owner_tag_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.precedence", "1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.scope", "scope-bd"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.monitoring_policy_name", "monitoring_policy_name_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.bridge_domain_name", "bridge_domain_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.0.annotation", "annotation_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.0.contract_name", "contract_name_0"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.0.priority", "level1"), @@ -246,10 +249,10 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.0.target_dn", "uni/tn-test_tenant/ap-test_ap/epg-epg_2"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.1.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.1.target_dn", "uni/tn-test_tenant/ap-test_ap/epg-epg_3"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.0.custom_qos_policy_name", "custom_qos_policy_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.0.data_plane_policing_policy_name", "data_plane_policing_policy_name_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.custom_qos_policy_name", "custom_qos_policy_name_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.data_plane_policing_policy_name", "data_plane_policing_policy_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.0.annotation", "annotation_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.0.binding_type", "dynamicBinding"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.0.class_preference", "encap"), @@ -354,8 +357,8 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.0.taboo_contract_name", "taboo_contract_name_0"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.1.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.1.taboo_contract_name", "taboo_contract_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.0.trust_control_policy_name", "trust_control_policy_name_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.trust_control_policy_name", "trust_control_policy_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.1.key", "key_1"), @@ -384,22 +387,19 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.1.key", "key_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.1.value", "test_value"), resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.#", "2"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.description", "description_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.match", "all"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.name", "criterion"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.name_alias", "name_alias_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.owner_key", "owner_key_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.owner_tag", "owner_tag_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.precedence", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.0.scope", "scope-bd"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.#", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.0.monitoring_policy_name", "monitoring_policy_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.#", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.0.bridge_domain_name", "bridge_domain_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.#", "1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.description", "description_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.match", "all"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.name", "criterion"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.name_alias", "name_alias_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.owner_key", "owner_key_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.owner_tag", "owner_tag_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.precedence", "1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.scope", "scope-bd"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.monitoring_policy_name", "monitoring_policy_name_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.bridge_domain_name", "bridge_domain_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.0.annotation", "annotation_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.0.contract_name", "contract_name_0"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.0.priority", "level1"), @@ -412,12 +412,10 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.1.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.1.target_dn", "uni/tn-test_tenant/ap-test_ap/epg-epg_3"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.#", "2"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.0.custom_qos_policy_name", "custom_qos_policy_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.#", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.0.data_plane_policing_policy_name", "data_plane_policing_policy_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.#", "1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.custom_qos_policy_name", "custom_qos_policy_name_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.data_plane_policing_policy_name", "data_plane_policing_policy_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.0.annotation", "annotation_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.0.binding_type", "dynamicBinding"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.0.class_preference", "encap"), @@ -530,9 +528,8 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.1.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.1.taboo_contract_name", "taboo_contract_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.#", "2"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.0.trust_control_policy_name", "trust_control_policy_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.#", "1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.trust_control_policy_name", "trust_control_policy_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.1.key", "key_1"), @@ -549,11 +546,8 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.0.key", "key_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.0.value", "test_value"), resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.#", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.#", "0"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.#", "0"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.0.bridge_domain_name", "bridge_domain_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.#", "1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.bridge_domain_name", "bridge_domain_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.0.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.0.contract_name", "contract_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.0.priority", "level2"), @@ -561,10 +555,8 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.0.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.0.target_dn", "uni/tn-test_tenant/ap-test_ap/epg-epg_3"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.#", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.0.custom_qos_policy_name", "custom_qos_policy_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.#", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.#", "0"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.custom_qos_policy_name", "custom_qos_policy_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.0.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.0.binding_type", "ephemeral"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.0.class_preference", "useg"), @@ -625,11 +617,55 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.0.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.0.taboo_contract_name", "taboo_contract_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.#", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.#", "0"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.0.key", "key_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.0.value", "test_value"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.#", "1"), ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("aci_application_epg.test", + tfjsonpath.New("epg_useg_block_statement"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "description": knownvalue.Null(), + "match": knownvalue.Null(), + "name": knownvalue.Null(), + "name_alias": knownvalue.Null(), + "owner_key": knownvalue.Null(), + "owner_tag": knownvalue.Null(), + "precedence": knownvalue.Null(), + "scope": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_application_epg.test", + tfjsonpath.New("relation_to_application_epg_monitoring_policy"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "monitoring_policy_name": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_application_epg.test", + tfjsonpath.New("relation_to_data_plane_policing_policy"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "data_plane_policing_policy_name": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_application_epg.test", + tfjsonpath.New("relation_to_trust_control_policy"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "trust_control_policy_name": knownvalue.Null(), + }, + ), + ), + }, }, // Update with all children removed { @@ -638,17 +674,12 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet("aci_application_epg.test", "pc_tag"), resource.TestCheckResourceAttr("aci_application_epg.test", "annotations.#", "0"), - resource.TestCheckResourceAttr("aci_application_epg.test", "epg_useg_block_statement.#", "0"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_application_epg_monitoring_policy.#", "0"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.0.bridge_domain_name", "bridge_domain_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.#", "1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_bridge_domain.bridge_domain_name", "bridge_domain_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_consumed_contracts.#", "0"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_contract_masters.#", "0"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.0.custom_qos_policy_name", "custom_qos_policy_name_1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.#", "1"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_data_plane_policing_policy.#", "0"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_custom_qos_policy.custom_qos_policy_name", "custom_qos_policy_name_1"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_domains.#", "0"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_fibre_channel_paths.#", "0"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_imported_contracts.#", "0"), @@ -657,9 +688,53 @@ func TestAccResourceFvAEPgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_static_leafs.#", "0"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_static_paths.#", "0"), resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_taboo_contracts.#", "0"), - resource.TestCheckResourceAttr("aci_application_epg.test", "relation_to_trust_control_policy.#", "0"), resource.TestCheckResourceAttr("aci_application_epg.test", "tags.#", "0"), ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("aci_application_epg.test", + tfjsonpath.New("epg_useg_block_statement"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "description": knownvalue.Null(), + "match": knownvalue.Null(), + "name": knownvalue.Null(), + "name_alias": knownvalue.Null(), + "owner_key": knownvalue.Null(), + "owner_tag": knownvalue.Null(), + "precedence": knownvalue.Null(), + "scope": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_application_epg.test", + tfjsonpath.New("relation_to_application_epg_monitoring_policy"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "monitoring_policy_name": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_application_epg.test", + tfjsonpath.New("relation_to_data_plane_policing_policy"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "data_plane_policing_policy_name": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_application_epg.test", + tfjsonpath.New("relation_to_trust_control_policy"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "trust_control_policy_name": knownvalue.Null(), + }, + ), + ), + }, }, // Update with minimum config and custom type semantic equivalent values { @@ -757,31 +832,25 @@ resource "aci_application_epg" "test" { value = "test_value" }, ] - epg_useg_block_statement = [ - { - annotation = "annotation_1" - description = "description_1" - match = "all" - name = "criterion" - name_alias = "name_alias_1" - owner_key = "owner_key_1" - owner_tag = "owner_tag_1" - precedence = "1" - scope = "scope-bd" - }, - ] - relation_to_application_epg_monitoring_policy = [ - { - annotation = "annotation_1" - monitoring_policy_name = "monitoring_policy_name_1" - }, - ] - relation_to_bridge_domain = [ - { - annotation = "annotation_1" - bridge_domain_name = "bridge_domain_name_1" - }, - ] + epg_useg_block_statement = { + annotation = "annotation_1" + description = "description_1" + match = "all" + name = "criterion" + name_alias = "name_alias_1" + owner_key = "owner_key_1" + owner_tag = "owner_tag_1" + precedence = "1" + scope = "scope-bd" + } + relation_to_application_epg_monitoring_policy = { + annotation = "annotation_1" + monitoring_policy_name = "monitoring_policy_name_1" + } + relation_to_bridge_domain = { + annotation = "annotation_1" + bridge_domain_name = "bridge_domain_name_1" + } relation_to_consumed_contracts = [ { annotation = "annotation_1" @@ -804,18 +873,14 @@ resource "aci_application_epg" "test" { target_dn = aci_application_epg.test_application_epg_1.id }, ] - relation_to_custom_qos_policy = [ - { - annotation = "annotation_1" - custom_qos_policy_name = "custom_qos_policy_name_1" - }, - ] - relation_to_data_plane_policing_policy = [ - { - annotation = "annotation_1" - data_plane_policing_policy_name = "data_plane_policing_policy_name_1" - }, - ] + relation_to_custom_qos_policy = { + annotation = "annotation_1" + custom_qos_policy_name = "custom_qos_policy_name_1" + } + relation_to_data_plane_policing_policy = { + annotation = "annotation_1" + data_plane_policing_policy_name = "data_plane_policing_policy_name_1" + } relation_to_domains = [ { annotation = "annotation_1" @@ -968,12 +1033,10 @@ resource "aci_application_epg" "test" { taboo_contract_name = "taboo_contract_name_1" }, ] - relation_to_trust_control_policy = [ - { - annotation = "annotation_1" - trust_control_policy_name = "trust_control_policy_name_1" - }, - ] + relation_to_trust_control_policy = { + annotation = "annotation_1" + trust_control_policy_name = "trust_control_policy_name_1" + } tags = [ { key = "key_0" @@ -1004,8 +1067,8 @@ resource "aci_application_epg" "test" { value = "test_value" }, ] - epg_useg_block_statement = [] - relation_to_application_epg_monitoring_policy = [] + epg_useg_block_statement = {} + relation_to_application_epg_monitoring_policy = {} relation_to_consumed_contracts = [ { annotation = "annotation_2" @@ -1019,7 +1082,7 @@ resource "aci_application_epg" "test" { target_dn = aci_application_epg.test_application_epg_1.id }, ] - relation_to_data_plane_policing_policy = [] + relation_to_data_plane_policing_policy = {} relation_to_domains = [ { annotation = "annotation_2" @@ -1104,7 +1167,7 @@ resource "aci_application_epg" "test" { taboo_contract_name = "taboo_contract_name_1" }, ] - relation_to_trust_control_policy = [] + relation_to_trust_control_policy = {} tags = [ { key = "key_1" @@ -1119,11 +1182,11 @@ resource "aci_application_epg" "test" { parent_dn = aci_application_profile.test.id name = "test_name" annotations = [] - epg_useg_block_statement = [] - relation_to_application_epg_monitoring_policy = [] + epg_useg_block_statement = {} + relation_to_application_epg_monitoring_policy = {} relation_to_consumed_contracts = [] relation_to_contract_masters = [] - relation_to_data_plane_policing_policy = [] + relation_to_data_plane_policing_policy = {} relation_to_domains = [] relation_to_fibre_channel_paths = [] relation_to_imported_contracts = [] @@ -1132,7 +1195,7 @@ resource "aci_application_epg" "test" { relation_to_static_leafs = [] relation_to_static_paths = [] relation_to_taboo_contracts = [] - relation_to_trust_control_policy = [] + relation_to_trust_control_policy = {} tags = [] } ` diff --git a/internal/provider/resource_aci_endpoint_security_group.go b/internal/provider/resource_aci_endpoint_security_group.go index e8da35ba0..42e94397c 100644 --- a/internal/provider/resource_aci_endpoint_security_group.go +++ b/internal/provider/resource_aci_endpoint_security_group.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" @@ -63,7 +64,7 @@ type FvESgResourceModel struct { FvRsConsIf types.Set `tfsdk:"relation_to_imported_contracts"` FvRsIntraEpg types.Set `tfsdk:"relation_to_intra_epg_contracts"` FvRsProv types.Set `tfsdk:"relation_to_provided_contracts"` - FvRsScope types.Set `tfsdk:"relation_to_vrf"` + FvRsScope types.Object `tfsdk:"relation_to_vrf"` FvRsSecInherited types.Set `tfsdk:"relation_to_contract_masters"` TagAnnotation types.Set `tfsdk:"annotations"` TagTag types.Set `tfsdk:"tags"` @@ -121,11 +122,9 @@ func getEmptyFvESgResourceModel() *FvESgResourceModel { "contract_name": types.StringType, }, }), - FvRsScope: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "vrf_name": types.StringType, - }, + FvRsScope: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "vrf_name": types.StringType, }), FvRsSecInherited: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -231,6 +230,11 @@ func getEmptyFvRsScopeFvESgResourceModel() FvRsScopeFvESgResourceModel { } } +var FvRsScopeFvESgType = map[string]attr.Type{ + "annotation": types.StringType, + "vrf_name": types.StringType, +} + // FvRsSecInheritedFvESgResourceModel describes the resource data model for the children without relation ships. type FvRsSecInheritedFvESgResourceModel struct { Annotation types.String `tfsdk:"annotation"` @@ -580,20 +584,12 @@ func (r *FvESgResource) UpgradeState(ctx context.Context) map[int64]resource.Sta FvRsProvSet, _ := types.SetValueFrom(ctx, FvRsProvType, FvRsProvList) upgradedStateData.FvRsProv = FvRsProvSet - FvRsScopeList := make([]FvRsScopeFvESgResourceModel, 0) - FvRsScope := FvRsScopeFvESgResourceModel{ + FvRsScopeObject := FvRsScopeFvESgResourceModel{ Annotation: basetypes.NewStringNull(), TnFvCtxName: basetypes.NewStringValue(GetMOName(priorStateData.FvRsScope.ValueString())), } - FvRsScopeList = append(FvRsScopeList, FvRsScope) - FvRsScopeType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "vrf_name": basetypes.StringType{}, - }, - } - FvRsScopeSet, _ := types.SetValueFrom(ctx, FvRsScopeType, FvRsScopeList) - upgradedStateData.FvRsScope = FvRsScopeSet + fvRsScopeObject, _ := types.ObjectValueFrom(ctx, FvRsScopeFvESgType, FvRsScopeObject) + upgradedStateData.FvRsScope = fvRsScopeObject FvRsSecInheritedList := make([]FvRsSecInheritedFvESgResourceModel, 0) var priorStateDataFvRsSecInheritedList []string @@ -741,6 +737,12 @@ func (r *FvESgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanR return } } + if !configData.FvRsScope.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.FvRsScope.Attributes()) { + FvRsScopeObject, _ := types.ObjectValueFrom(ctx, FvRsScopeFvESgType, getEmptyFvRsScopeFvESgResourceModel()) + planData.FvRsScope = FvRsScopeObject + } + } if !configData.MatchT.IsNull() { planData.DeprecatedMatchT = configData.MatchT @@ -774,7 +776,6 @@ func (r *FvESgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanR planData.DeprecatedPrefGrMemb = stateData.DeprecatedPrefGrMemb } - // HasNamedProperties false if !configData.FvRsSecInherited.IsNull() && stateData != nil { var attributeValues []FvRsSecInheritedFvESgResourceModel var newAttributeValues, stateAttributeValues []string @@ -838,7 +839,6 @@ func (r *FvESgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanR planData.DeprecatedFvRsSecInherited = stateData.DeprecatedFvRsSecInherited } - // HasNamedProperties true if !configData.FvRsIntraEpg.IsNull() && stateData != nil { var attributeValues []FvRsIntraEpgFvESgResourceModel var newAttributeValues, stateAttributeValues []string @@ -903,28 +903,23 @@ func (r *FvESgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanR } if !configData.FvRsScope.IsNull() && stateData != nil { - var attributeValues []FvRsScopeFvESgResourceModel - configData.FvRsScope.ElementsAs(ctx, &attributeValues, false) - for _, attributeValue := range attributeValues { - if GetMOName(stateData.DeprecatedFvRsScope.ValueString()) == attributeValue.TnFvCtxName.ValueString() { + if IsEmptySingleNestedAttribute(configData.FvRsScope.Attributes()) { + planData.FvRsScope = configData.FvRsScope + planData.DeprecatedFvRsScope = basetypes.NewStringNull() + } else { + var attributeValues FvRsScopeFvESgResourceModel + configData.FvRsScope.As(ctx, &attributeValues, basetypes.ObjectAsOptions{}) + if GetMOName(stateData.DeprecatedFvRsScope.ValueString()) == attributeValues.TnFvCtxName.ValueString() && !attributeValues.TnFvCtxName.IsNull() { planData.DeprecatedFvRsScope = stateData.DeprecatedFvRsScope } } } else if !configData.DeprecatedFvRsScope.IsNull() { - FvRsScopeList := make([]FvRsScopeFvESgResourceModel, 0) FvRsScope := FvRsScopeFvESgResourceModel{ Annotation: planData.Annotation, TnFvCtxName: basetypes.NewStringValue(GetMOName(configData.DeprecatedFvRsScope.ValueString())), } - FvRsScopeList = append(FvRsScopeList, FvRsScope) - FvRsScopeType := types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": basetypes.StringType{}, - "vrf_name": basetypes.StringType{}, - }, - } - FvRsScopeSet, _ := types.SetValueFrom(ctx, FvRsScopeType, FvRsScopeList) - planData.FvRsScope = FvRsScopeSet + FvRsScopeObject, _ := types.ObjectValueFrom(ctx, FvRsScopeFvESgType, FvRsScope) + planData.FvRsScope = FvRsScopeObject } else if stateData != nil { // used to replace use state for unknown planData.DeprecatedFvRsScope = stateData.DeprecatedFvRsScope } @@ -1459,36 +1454,31 @@ func (r *FvESgResource) Schema(ctx context.Context, req resource.SchemaRequest, }, }, }, - "relation_to_vrf": schema.SetNestedAttribute{ + "relation_to_vrf": schema.SingleNestedAttribute{ MarkdownDescription: ``, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), - }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The annotation of the Relation To VRF object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "vrf_name": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.String{}, - MarkdownDescription: `The name of the VRF object.`, + Validators: []validator.String{}, + MarkdownDescription: `The annotation of the Relation To VRF object.`, + }, + "vrf_name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + Validators: []validator.String{}, + MarkdownDescription: `The name of the VRF object.`, }, }, }, @@ -1753,9 +1743,9 @@ func (r *FvESgResource) Create(ctx context.Context, req resource.CreateRequest, var fvRsProvPlan, fvRsProvState []FvRsProvFvESgResourceModel data.FvRsProv.ElementsAs(ctx, &fvRsProvPlan, false) stateData.FvRsProv.ElementsAs(ctx, &fvRsProvState, false) - var fvRsScopePlan, fvRsScopeState []FvRsScopeFvESgResourceModel - data.FvRsScope.ElementsAs(ctx, &fvRsScopePlan, false) - stateData.FvRsScope.ElementsAs(ctx, &fvRsScopeState, false) + var fvRsScopePlan, fvRsScopeState FvRsScopeFvESgResourceModel + data.FvRsScope.As(ctx, &fvRsScopePlan, basetypes.ObjectAsOptions{}) + stateData.FvRsScope.As(ctx, &fvRsScopeState, basetypes.ObjectAsOptions{}) var fvRsSecInheritedPlan, fvRsSecInheritedState []FvRsSecInheritedFvESgResourceModel data.FvRsSecInherited.ElementsAs(ctx, &fvRsSecInheritedPlan, false) stateData.FvRsSecInherited.ElementsAs(ctx, &fvRsSecInheritedState, false) @@ -1817,6 +1807,13 @@ func (r *FvESgResource) Update(ctx context.Context, req resource.UpdateRequest, // Read Terraform plan data into the model resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + // Error out when child object fvRsScope is being deleted + if IsEmptySingleNestedAttribute(data.FvRsScope.Attributes()) && !IsEmptySingleNestedAttribute(stateData.FvRsScope.Attributes()) { + resp.Diagnostics.AddError( + "FvRsScope object cannot be deleted", + "deletion of child is only possible upon deletion of the parent", + ) + } if resp.Diagnostics.HasError() { return @@ -1836,9 +1833,9 @@ func (r *FvESgResource) Update(ctx context.Context, req resource.UpdateRequest, var fvRsProvPlan, fvRsProvState []FvRsProvFvESgResourceModel data.FvRsProv.ElementsAs(ctx, &fvRsProvPlan, false) stateData.FvRsProv.ElementsAs(ctx, &fvRsProvState, false) - var fvRsScopePlan, fvRsScopeState []FvRsScopeFvESgResourceModel - data.FvRsScope.ElementsAs(ctx, &fvRsScopePlan, false) - stateData.FvRsScope.ElementsAs(ctx, &fvRsScopeState, false) + var fvRsScopePlan, fvRsScopeState FvRsScopeFvESgResourceModel + data.FvRsScope.As(ctx, &fvRsScopePlan, basetypes.ObjectAsOptions{}) + stateData.FvRsScope.As(ctx, &fvRsScopeState, basetypes.ObjectAsOptions{}) var fvRsSecInheritedPlan, fvRsSecInheritedState []FvRsSecInheritedFvESgResourceModel data.FvRsSecInherited.ElementsAs(ctx, &fvRsSecInheritedPlan, false) stateData.FvRsSecInherited.ElementsAs(ctx, &fvRsSecInheritedState, false) @@ -2082,8 +2079,13 @@ func getAndSetFvESgAttributes(ctx context.Context, diags *diag.Diagnostics, clie data.FvRsIntraEpg = fvRsIntraEpgSet fvRsProvSet, _ := types.SetValueFrom(ctx, data.FvRsProv.ElementType(ctx), FvRsProvFvESgList) data.FvRsProv = fvRsProvSet - fvRsScopeSet, _ := types.SetValueFrom(ctx, data.FvRsScope.ElementType(ctx), FvRsScopeFvESgList) - data.FvRsScope = fvRsScopeSet + if len(FvRsScopeFvESgList) == 1 { + fvRsScopeObject, _ := types.ObjectValueFrom(ctx, FvRsScopeFvESgType, FvRsScopeFvESgList[0]) + data.FvRsScope = fvRsScopeObject + } else { + fvRsScopeObject, _ := types.ObjectValueFrom(ctx, FvRsScopeFvESgType, getEmptyFvRsScopeFvESgResourceModel()) + data.FvRsScope = fvRsScopeObject + } fvRsSecInheritedSet, _ := types.SetValueFrom(ctx, data.FvRsSecInherited.ElementType(ctx), FvRsSecInheritedFvESgList) data.FvRsSecInherited = fvRsSecInheritedSet tagAnnotationSet, _ := types.SetValueFrom(ctx, data.TagAnnotation.ElementType(ctx), TagAnnotationFvESgList) @@ -2309,31 +2311,25 @@ func getFvESgFvRsProvChildPayloads(ctx context.Context, diags *diag.Diagnostics, return childPayloads } -func getFvESgFvRsScopeChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvESgResourceModel, fvRsScopePlan, fvRsScopeState []FvRsScopeFvESgResourceModel) []map[string]interface{} { +func getFvESgFvRsScopeChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvESgResourceModel, fvRsScopePlan, fvRsScopeState FvRsScopeFvESgResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.FvRsScope.IsUnknown() { - for _, fvRsScope := range fvRsScopePlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !fvRsScope.Annotation.IsUnknown() && !fvRsScope.Annotation.IsNull() { - childMap["attributes"]["annotation"] = fvRsScope.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.FvRsScope.Attributes()) { + if !fvRsScopePlan.Annotation.IsUnknown() && !fvRsScopePlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = fvRsScopePlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !fvRsScope.TnFvCtxName.IsUnknown() && !fvRsScope.TnFvCtxName.IsNull() { - childMap["attributes"]["tnFvCtxName"] = fvRsScope.TnFvCtxName.ValueString() + if !fvRsScopePlan.TnFvCtxName.IsUnknown() && !fvRsScopePlan.TnFvCtxName.IsNull() { + childMap["attributes"]["tnFvCtxName"] = fvRsScopePlan.TnFvCtxName.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"fvRsScope": childMap}) - } - if len(fvRsScopePlan) == 0 && len(fvRsScopeState) == 1 { - diags.AddError( - "FvRsScope object cannot be deleted", - "deletion of child is only possible upon deletion of the parent", - ) - return nil } + childPayloads = append(childPayloads, map[string]interface{}{"fvRsScope": childMap}) } else { - data.FvRsScope = types.SetNull(data.FvRsScope.ElementType(ctx)) + FvRsScopeObject, _ := types.ObjectValueFrom(ctx, FvRsScopeFvESgType, getEmptyFvRsScopeFvESgResourceModel()) + data.FvRsScope = FvRsScopeObject } return childPayloads @@ -2458,7 +2454,7 @@ func getFvESgTagTagChildPayloads(ctx context.Context, diags *diag.Diagnostics, d return childPayloads } -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 { +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{} diff --git a/internal/provider/resource_aci_endpoint_security_group_test.go b/internal/provider/resource_aci_endpoint_security_group_test.go index 1c2bcb4ad..831a2076c 100644 --- a/internal/provider/resource_aci_endpoint_security_group_test.go +++ b/internal/provider/resource_aci_endpoint_security_group_test.go @@ -211,8 +211,8 @@ func TestAccResourceFvESgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.1.contract_name", "contract_name_1"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.1.match_criteria", "AtleastOne"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.1.priority", "level2"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.0.vrf_name", "vrf_name_1"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.vrf_name", "vrf_name_1"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.1.key", "key_1"), @@ -274,9 +274,8 @@ func TestAccResourceFvESgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.1.match_criteria", "AtleastOne"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.1.priority", "level2"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.#", "2"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.0.vrf_name", "vrf_name_1"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.#", "1"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.vrf_name", "vrf_name_1"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.1.key", "key_1"), @@ -312,9 +311,8 @@ func TestAccResourceFvESgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.0.match_criteria", "AtleastOne"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.0.priority", "level2"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.#", "1"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.0.vrf_name", "vrf_name_1"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.#", "1"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.vrf_name", "vrf_name_1"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.0.key", "key_1"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.0.value", "test_value"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.#", "1"), @@ -332,9 +330,8 @@ func TestAccResourceFvESgWithFvAp(t *testing.T) { resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_imported_contracts.#", "0"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_intra_epg_contracts.#", "0"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_provided_contracts.#", "0"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.0.vrf_name", "vrf_name_1"), - resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.#", "1"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "relation_to_vrf.vrf_name", "vrf_name_1"), resource.TestCheckResourceAttr("aci_endpoint_security_group.test", "tags.#", "0"), ), }, @@ -473,12 +470,10 @@ resource "aci_endpoint_security_group" "test" { priority = "level2" }, ] - relation_to_vrf = [ - { - annotation = "annotation_1" - vrf_name = "vrf_name_1" - }, - ] + relation_to_vrf = { + annotation = "annotation_1" + vrf_name = "vrf_name_1" + } tags = [ { key = "key_0" diff --git a/internal/provider/resource_aci_netflow_exporter_policy.go b/internal/provider/resource_aci_netflow_exporter_policy.go index 9a43ed3db..765d0bc5e 100644 --- a/internal/provider/resource_aci_netflow_exporter_policy.go +++ b/internal/provider/resource_aci_netflow_exporter_policy.go @@ -15,13 +15,13 @@ import ( "github.com/CiscoDevNet/terraform-provider-aci/v2/internal/validators" "github.com/ciscoecosystem/aci-go-client/v2/client" "github.com/ciscoecosystem/aci-go-client/v2/container" - "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" @@ -61,8 +61,8 @@ type NetflowExporterPolResourceModel struct { SourceIpType types.String `tfsdk:"source_ip_type"` SrcAddr types.String `tfsdk:"source_ip_address"` Ver types.String `tfsdk:"version"` - NetflowRsExporterToCtx types.Set `tfsdk:"relation_to_vrf"` - NetflowRsExporterToEPg types.Set `tfsdk:"relation_to_epg"` + NetflowRsExporterToCtx types.Object `tfsdk:"relation_to_vrf"` + NetflowRsExporterToEPg types.Object `tfsdk:"relation_to_epg"` TagAnnotation types.Set `tfsdk:"annotations"` TagTag types.Set `tfsdk:"tags"` } @@ -83,17 +83,13 @@ func getEmptyNetflowExporterPolResourceModel() *NetflowExporterPolResourceModel SourceIpType: basetypes.NewStringNull(), SrcAddr: basetypes.NewStringNull(), Ver: basetypes.NewStringNull(), - NetflowRsExporterToCtx: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "target_dn": types.StringType, - }, + NetflowRsExporterToCtx: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "target_dn": types.StringType, }), - NetflowRsExporterToEPg: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "target_dn": types.StringType, - }, + NetflowRsExporterToEPg: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "target_dn": types.StringType, }), TagAnnotation: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -123,6 +119,11 @@ func getEmptyNetflowRsExporterToCtxNetflowExporterPolResourceModel() NetflowRsEx } } +var NetflowRsExporterToCtxNetflowExporterPolType = map[string]attr.Type{ + "annotation": types.StringType, + "target_dn": types.StringType, +} + // NetflowRsExporterToEPgNetflowExporterPolResourceModel describes the resource data model for the children without relation ships. type NetflowRsExporterToEPgNetflowExporterPolResourceModel struct { Annotation types.String `tfsdk:"annotation"` @@ -136,6 +137,11 @@ func getEmptyNetflowRsExporterToEPgNetflowExporterPolResourceModel() NetflowRsEx } } +var NetflowRsExporterToEPgNetflowExporterPolType = map[string]attr.Type{ + "annotation": types.StringType, + "target_dn": types.StringType, +} + // TagAnnotationNetflowExporterPolResourceModel describes the resource data model for the children without relation ships. type TagAnnotationNetflowExporterPolResourceModel struct { Key types.String `tfsdk:"key"` @@ -168,9 +174,10 @@ type NetflowExporterPolIdentifier struct { func (r *NetflowExporterPolResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { if !req.Plan.Raw.IsNull() { - var planData, stateData *NetflowExporterPolResourceModel + var planData, stateData, configData *NetflowExporterPolResourceModel 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 resp.Diagnostics.HasError() { return @@ -186,6 +193,18 @@ func (r *NetflowExporterPolResource) ModifyPlan(ctx context.Context, req resourc return } } + if !configData.NetflowRsExporterToCtx.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.NetflowRsExporterToCtx.Attributes()) { + NetflowRsExporterToCtxObject, _ := types.ObjectValueFrom(ctx, NetflowRsExporterToCtxNetflowExporterPolType, getEmptyNetflowRsExporterToCtxNetflowExporterPolResourceModel()) + planData.NetflowRsExporterToCtx = NetflowRsExporterToCtxObject + } + } + if !configData.NetflowRsExporterToEPg.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.NetflowRsExporterToEPg.Attributes()) { + NetflowRsExporterToEPgObject, _ := types.ObjectValueFrom(ctx, NetflowRsExporterToEPgNetflowExporterPolType, getEmptyNetflowRsExporterToEPgNetflowExporterPolResourceModel()) + planData.NetflowRsExporterToEPg = NetflowRsExporterToEPgObject + } + } resp.Diagnostics.Append(resp.Plan.Set(ctx, &planData)...) } @@ -348,65 +367,55 @@ func (r *NetflowExporterPolResource) Schema(ctx context.Context, req resource.Sc }, MarkdownDescription: `The NetFlow Exporter Version of the NetFlow Exporter Policy object.`, }, - "relation_to_vrf": schema.SetNestedAttribute{ + "relation_to_vrf": schema.SingleNestedAttribute{ MarkdownDescription: `Points to the Ctx behind which the Netflow Exporter Resides`, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The annotation of the Relation From NetFlow Exporter To VRF object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "target_dn": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The distinguished name of the target.`, + MarkdownDescription: `The annotation of the Relation From NetFlow Exporter To VRF object.`, + }, + "target_dn": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + MarkdownDescription: `The distinguished name of the target.`, }, }, }, - "relation_to_epg": schema.SetNestedAttribute{ + "relation_to_epg": schema.SingleNestedAttribute{ MarkdownDescription: `Points to the EPg behind which the Netflow Exporter Resides`, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The annotation of the Relation From NetFlow Exporter To EPG object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "target_dn": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The distinguished name of the target.`, + MarkdownDescription: `The annotation of the Relation From NetFlow Exporter To EPG object.`, + }, + "target_dn": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + MarkdownDescription: `The distinguished name of the target.`, }, }, }, @@ -521,12 +530,12 @@ func (r *NetflowExporterPolResource) Create(ctx context.Context, req resource.Cr tflog.Debug(ctx, fmt.Sprintf("Create of resource aci_netflow_exporter_policy with id '%s'", data.Id.ValueString())) - var netflowRsExporterToCtxPlan, netflowRsExporterToCtxState []NetflowRsExporterToCtxNetflowExporterPolResourceModel - data.NetflowRsExporterToCtx.ElementsAs(ctx, &netflowRsExporterToCtxPlan, false) - stateData.NetflowRsExporterToCtx.ElementsAs(ctx, &netflowRsExporterToCtxState, false) - var netflowRsExporterToEPgPlan, netflowRsExporterToEPgState []NetflowRsExporterToEPgNetflowExporterPolResourceModel - data.NetflowRsExporterToEPg.ElementsAs(ctx, &netflowRsExporterToEPgPlan, false) - stateData.NetflowRsExporterToEPg.ElementsAs(ctx, &netflowRsExporterToEPgState, false) + var netflowRsExporterToCtxPlan, netflowRsExporterToCtxState NetflowRsExporterToCtxNetflowExporterPolResourceModel + data.NetflowRsExporterToCtx.As(ctx, &netflowRsExporterToCtxPlan, basetypes.ObjectAsOptions{}) + stateData.NetflowRsExporterToCtx.As(ctx, &netflowRsExporterToCtxState, basetypes.ObjectAsOptions{}) + var netflowRsExporterToEPgPlan, netflowRsExporterToEPgState NetflowRsExporterToEPgNetflowExporterPolResourceModel + data.NetflowRsExporterToEPg.As(ctx, &netflowRsExporterToEPgPlan, basetypes.ObjectAsOptions{}) + stateData.NetflowRsExporterToEPg.As(ctx, &netflowRsExporterToEPgState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowExporterPolResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -592,12 +601,12 @@ func (r *NetflowExporterPolResource) Update(ctx context.Context, req resource.Up tflog.Debug(ctx, fmt.Sprintf("Update of resource aci_netflow_exporter_policy with id '%s'", data.Id.ValueString())) - var netflowRsExporterToCtxPlan, netflowRsExporterToCtxState []NetflowRsExporterToCtxNetflowExporterPolResourceModel - data.NetflowRsExporterToCtx.ElementsAs(ctx, &netflowRsExporterToCtxPlan, false) - stateData.NetflowRsExporterToCtx.ElementsAs(ctx, &netflowRsExporterToCtxState, false) - var netflowRsExporterToEPgPlan, netflowRsExporterToEPgState []NetflowRsExporterToEPgNetflowExporterPolResourceModel - data.NetflowRsExporterToEPg.ElementsAs(ctx, &netflowRsExporterToEPgPlan, false) - stateData.NetflowRsExporterToEPg.ElementsAs(ctx, &netflowRsExporterToEPgState, false) + var netflowRsExporterToCtxPlan, netflowRsExporterToCtxState NetflowRsExporterToCtxNetflowExporterPolResourceModel + data.NetflowRsExporterToCtx.As(ctx, &netflowRsExporterToCtxPlan, basetypes.ObjectAsOptions{}) + stateData.NetflowRsExporterToCtx.As(ctx, &netflowRsExporterToCtxState, basetypes.ObjectAsOptions{}) + var netflowRsExporterToEPgPlan, netflowRsExporterToEPgState NetflowRsExporterToEPgNetflowExporterPolResourceModel + data.NetflowRsExporterToEPg.As(ctx, &netflowRsExporterToEPgPlan, basetypes.ObjectAsOptions{}) + stateData.NetflowRsExporterToEPg.As(ctx, &netflowRsExporterToEPgState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowExporterPolResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -772,10 +781,20 @@ func getAndSetNetflowExporterPolAttributes(ctx context.Context, diags *diag.Diag } } } - netflowRsExporterToCtxSet, _ := types.SetValueFrom(ctx, data.NetflowRsExporterToCtx.ElementType(ctx), NetflowRsExporterToCtxNetflowExporterPolList) - data.NetflowRsExporterToCtx = netflowRsExporterToCtxSet - netflowRsExporterToEPgSet, _ := types.SetValueFrom(ctx, data.NetflowRsExporterToEPg.ElementType(ctx), NetflowRsExporterToEPgNetflowExporterPolList) - data.NetflowRsExporterToEPg = netflowRsExporterToEPgSet + if len(NetflowRsExporterToCtxNetflowExporterPolList) == 1 { + netflowRsExporterToCtxObject, _ := types.ObjectValueFrom(ctx, NetflowRsExporterToCtxNetflowExporterPolType, NetflowRsExporterToCtxNetflowExporterPolList[0]) + data.NetflowRsExporterToCtx = netflowRsExporterToCtxObject + } else { + netflowRsExporterToCtxObject, _ := types.ObjectValueFrom(ctx, NetflowRsExporterToCtxNetflowExporterPolType, getEmptyNetflowRsExporterToCtxNetflowExporterPolResourceModel()) + data.NetflowRsExporterToCtx = netflowRsExporterToCtxObject + } + if len(NetflowRsExporterToEPgNetflowExporterPolList) == 1 { + netflowRsExporterToEPgObject, _ := types.ObjectValueFrom(ctx, NetflowRsExporterToEPgNetflowExporterPolType, NetflowRsExporterToEPgNetflowExporterPolList[0]) + data.NetflowRsExporterToEPg = netflowRsExporterToEPgObject + } else { + netflowRsExporterToEPgObject, _ := types.ObjectValueFrom(ctx, NetflowRsExporterToEPgNetflowExporterPolType, getEmptyNetflowRsExporterToEPgNetflowExporterPolResourceModel()) + data.NetflowRsExporterToEPg = netflowRsExporterToEPgObject + } tagAnnotationSet, _ := types.SetValueFrom(ctx, data.TagAnnotation.ElementType(ctx), TagAnnotationNetflowExporterPolList) data.TagAnnotation = tagAnnotationSet tagTagSet, _ := types.SetValueFrom(ctx, data.TagTag.ElementType(ctx), TagTagNetflowExporterPolList) @@ -822,56 +841,52 @@ func setNetflowExporterPolId(ctx context.Context, data *NetflowExporterPolResour data.Id = types.StringValue(fmt.Sprintf("%s/%s", data.ParentDn.ValueString(), rn)) } -func getNetflowExporterPolNetflowRsExporterToCtxChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *NetflowExporterPolResourceModel, netflowRsExporterToCtxPlan, netflowRsExporterToCtxState []NetflowRsExporterToCtxNetflowExporterPolResourceModel) []map[string]interface{} { +func getNetflowExporterPolNetflowRsExporterToCtxChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *NetflowExporterPolResourceModel, netflowRsExporterToCtxPlan, netflowRsExporterToCtxState NetflowRsExporterToCtxNetflowExporterPolResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.NetflowRsExporterToCtx.IsUnknown() { - for _, netflowRsExporterToCtx := range netflowRsExporterToCtxPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !netflowRsExporterToCtx.Annotation.IsUnknown() && !netflowRsExporterToCtx.Annotation.IsNull() { - childMap["attributes"]["annotation"] = netflowRsExporterToCtx.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.NetflowRsExporterToCtx.Attributes()) { + if !netflowRsExporterToCtxPlan.Annotation.IsUnknown() && !netflowRsExporterToCtxPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = netflowRsExporterToCtxPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !netflowRsExporterToCtx.TDn.IsUnknown() && !netflowRsExporterToCtx.TDn.IsNull() { - childMap["attributes"]["tDn"] = netflowRsExporterToCtx.TDn.ValueString() + if !netflowRsExporterToCtxPlan.TDn.IsUnknown() && !netflowRsExporterToCtxPlan.TDn.IsNull() { + childMap["attributes"]["tDn"] = netflowRsExporterToCtxPlan.TDn.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"netflowRsExporterToCtx": childMap}) - } - if len(netflowRsExporterToCtxPlan) == 0 && len(netflowRsExporterToCtxState) == 1 { - childMap := map[string]map[string]interface{}{"attributes": {}} + } else { childMap["attributes"]["status"] = "deleted" - childPayloads = append(childPayloads, map[string]interface{}{"netflowRsExporterToCtx": childMap}) } + childPayloads = append(childPayloads, map[string]interface{}{"netflowRsExporterToCtx": childMap}) } else { - data.NetflowRsExporterToCtx = types.SetNull(data.NetflowRsExporterToCtx.ElementType(ctx)) + NetflowRsExporterToCtxObject, _ := types.ObjectValueFrom(ctx, NetflowRsExporterToCtxNetflowExporterPolType, getEmptyNetflowRsExporterToCtxNetflowExporterPolResourceModel()) + data.NetflowRsExporterToCtx = NetflowRsExporterToCtxObject } return childPayloads } -func getNetflowExporterPolNetflowRsExporterToEPgChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *NetflowExporterPolResourceModel, netflowRsExporterToEPgPlan, netflowRsExporterToEPgState []NetflowRsExporterToEPgNetflowExporterPolResourceModel) []map[string]interface{} { +func getNetflowExporterPolNetflowRsExporterToEPgChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *NetflowExporterPolResourceModel, netflowRsExporterToEPgPlan, netflowRsExporterToEPgState NetflowRsExporterToEPgNetflowExporterPolResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.NetflowRsExporterToEPg.IsUnknown() { - for _, netflowRsExporterToEPg := range netflowRsExporterToEPgPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !netflowRsExporterToEPg.Annotation.IsUnknown() && !netflowRsExporterToEPg.Annotation.IsNull() { - childMap["attributes"]["annotation"] = netflowRsExporterToEPg.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.NetflowRsExporterToEPg.Attributes()) { + if !netflowRsExporterToEPgPlan.Annotation.IsUnknown() && !netflowRsExporterToEPgPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = netflowRsExporterToEPgPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !netflowRsExporterToEPg.TDn.IsUnknown() && !netflowRsExporterToEPg.TDn.IsNull() { - childMap["attributes"]["tDn"] = netflowRsExporterToEPg.TDn.ValueString() + if !netflowRsExporterToEPgPlan.TDn.IsUnknown() && !netflowRsExporterToEPgPlan.TDn.IsNull() { + childMap["attributes"]["tDn"] = netflowRsExporterToEPgPlan.TDn.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"netflowRsExporterToEPg": childMap}) - } - if len(netflowRsExporterToEPgPlan) == 0 && len(netflowRsExporterToEPgState) == 1 { - childMap := map[string]map[string]interface{}{"attributes": {}} + } else { childMap["attributes"]["status"] = "deleted" - childPayloads = append(childPayloads, map[string]interface{}{"netflowRsExporterToEPg": childMap}) } + childPayloads = append(childPayloads, map[string]interface{}{"netflowRsExporterToEPg": childMap}) } else { - data.NetflowRsExporterToEPg = types.SetNull(data.NetflowRsExporterToEPg.ElementType(ctx)) + NetflowRsExporterToEPgObject, _ := types.ObjectValueFrom(ctx, NetflowRsExporterToEPgNetflowExporterPolType, getEmptyNetflowRsExporterToEPgNetflowExporterPolResourceModel()) + data.NetflowRsExporterToEPg = NetflowRsExporterToEPgObject } return childPayloads @@ -955,7 +970,7 @@ func getNetflowExporterPolTagTagChildPayloads(ctx context.Context, diags *diag.D return childPayloads } -func getNetflowExporterPolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *NetflowExporterPolResourceModel, netflowRsExporterToCtxPlan, netflowRsExporterToCtxState []NetflowRsExporterToCtxNetflowExporterPolResourceModel, netflowRsExporterToEPgPlan, netflowRsExporterToEPgState []NetflowRsExporterToEPgNetflowExporterPolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowExporterPolResourceModel, tagTagPlan, tagTagState []TagTagNetflowExporterPolResourceModel) *container.Container { +func getNetflowExporterPolCreateJsonPayload(ctx context.Context, diags *diag.Diagnostics, createType bool, data *NetflowExporterPolResourceModel, netflowRsExporterToCtxPlan, netflowRsExporterToCtxState NetflowRsExporterToCtxNetflowExporterPolResourceModel, netflowRsExporterToEPgPlan, netflowRsExporterToEPgState NetflowRsExporterToEPgNetflowExporterPolResourceModel, tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowExporterPolResourceModel, tagTagPlan, tagTagState []TagTagNetflowExporterPolResourceModel) *container.Container { payloadMap := map[string]interface{}{} payloadMap["attributes"] = map[string]string{} diff --git a/internal/provider/resource_aci_netflow_exporter_policy_test.go b/internal/provider/resource_aci_netflow_exporter_policy_test.go index 19b3cc5a7..e051df08b 100644 --- a/internal/provider/resource_aci_netflow_exporter_policy_test.go +++ b/internal/provider/resource_aci_netflow_exporter_policy_test.go @@ -9,6 +9,9 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" ) func TestAccResourceNetflowExporterPolWithFvTenant(t *testing.T) { @@ -202,10 +205,10 @@ func TestAccResourceNetflowExporterPolWithFvTenant(t *testing.T) { resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.0.value", "value_1"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.1.key", "key_1"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.1.value", "test_value"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.0.target_dn", "uni/tn-test_tenant/ap-test_ap/epg-test_epg"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.0.target_dn", "uni/tn-test_tenant/ctx-test_vrf"), + resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.target_dn", "uni/tn-test_tenant/ap-test_ap/epg-test_epg"), + resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.target_dn", "uni/tn-test_tenant/ctx-test_vrf"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.1.key", "key_1"), @@ -233,12 +236,10 @@ func TestAccResourceNetflowExporterPolWithFvTenant(t *testing.T) { resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.1.key", "key_1"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.1.value", "test_value"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.#", "2"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.0.target_dn", "uni/tn-test_tenant/ap-test_ap/epg-test_epg"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.#", "1"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.0.target_dn", "uni/tn-test_tenant/ctx-test_vrf"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.#", "1"), + resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.target_dn", "uni/tn-test_tenant/ap-test_ap/epg-test_epg"), + resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.target_dn", "uni/tn-test_tenant/ctx-test_vrf"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.1.key", "key_1"), @@ -254,12 +255,30 @@ func TestAccResourceNetflowExporterPolWithFvTenant(t *testing.T) { resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.0.key", "key_1"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.0.value", "test_value"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.#", "1"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.#", "0"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.#", "0"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.0.key", "key_1"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.0.value", "test_value"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.#", "1"), ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("aci_netflow_exporter_policy.test", + tfjsonpath.New("relation_to_epg"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "target_dn": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_netflow_exporter_policy.test", + tfjsonpath.New("relation_to_vrf"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "target_dn": knownvalue.Null(), + }, + ), + ), + }, }, // Update with all children removed { @@ -267,10 +286,28 @@ func TestAccResourceNetflowExporterPolWithFvTenant(t *testing.T) { ExpectNonEmptyPlan: false, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "annotations.#", "0"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_epg.#", "0"), - resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "relation_to_vrf.#", "0"), resource.TestCheckResourceAttr("aci_netflow_exporter_policy.test", "tags.#", "0"), ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("aci_netflow_exporter_policy.test", + tfjsonpath.New("relation_to_epg"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "target_dn": knownvalue.Null(), + }, + ), + ), + statecheck.ExpectKnownValue("aci_netflow_exporter_policy.test", + tfjsonpath.New("relation_to_vrf"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "target_dn": knownvalue.Null(), + }, + ), + ), + }, }, // Update with minimum config and custom type semantic equivalent values { @@ -371,18 +408,14 @@ resource "aci_netflow_exporter_policy" "test" { value = "test_value" }, ] - relation_to_epg = [ - { - annotation = "annotation_1" - target_dn = "uni/tn-test_tenant/ap-test_ap/epg-test_epg" - }, - ] - relation_to_vrf = [ - { - annotation = "annotation_1" - target_dn = "uni/tn-test_tenant/ctx-test_vrf" - }, - ] + relation_to_epg = { + annotation = "annotation_1" + target_dn = "uni/tn-test_tenant/ap-test_ap/epg-test_epg" + } + relation_to_vrf = { + annotation = "annotation_1" + target_dn = "uni/tn-test_tenant/ctx-test_vrf" + } tags = [ { key = "key_0" @@ -419,8 +452,8 @@ resource "aci_netflow_exporter_policy" "test" { value = "test_value" }, ] - relation_to_epg = [] - relation_to_vrf = [] + relation_to_epg = {} + relation_to_vrf = {} tags = [ { key = "key_1" @@ -438,8 +471,8 @@ resource "aci_netflow_exporter_policy" "test" { name = "netfow_exporter" source_ip_address = "1.1.1.1/10" annotations = [] - relation_to_epg = [] - relation_to_vrf = [] + relation_to_epg = {} + relation_to_vrf = {} tags = [] } ` diff --git a/internal/provider/resource_aci_netflow_monitor_policy.go b/internal/provider/resource_aci_netflow_monitor_policy.go index d5bbdeb29..0bfe6610f 100644 --- a/internal/provider/resource_aci_netflow_monitor_policy.go +++ b/internal/provider/resource_aci_netflow_monitor_policy.go @@ -13,17 +13,16 @@ import ( "github.com/ciscoecosystem/aci-go-client/v2/client" "github.com/ciscoecosystem/aci-go-client/v2/container" - "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-log/tflog" @@ -53,7 +52,7 @@ type NetflowMonitorPolResourceModel struct { OwnerKey types.String `tfsdk:"owner_key"` OwnerTag types.String `tfsdk:"owner_tag"` NetflowRsMonitorToExporter types.Set `tfsdk:"relation_to_netflow_exporters"` - NetflowRsMonitorToRecord types.Set `tfsdk:"relation_to_netflow_record"` + NetflowRsMonitorToRecord types.Object `tfsdk:"relation_to_netflow_record"` TagAnnotation types.Set `tfsdk:"annotations"` TagTag types.Set `tfsdk:"tags"` } @@ -74,11 +73,9 @@ func getEmptyNetflowMonitorPolResourceModel() *NetflowMonitorPolResourceModel { "netflow_exporter_policy_name": types.StringType, }, }), - NetflowRsMonitorToRecord: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "netflow_record_policy_name": types.StringType, - }, + NetflowRsMonitorToRecord: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "netflow_record_policy_name": types.StringType, }), TagAnnotation: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -121,6 +118,11 @@ func getEmptyNetflowRsMonitorToRecordNetflowMonitorPolResourceModel() NetflowRsM } } +var NetflowRsMonitorToRecordNetflowMonitorPolType = map[string]attr.Type{ + "annotation": types.StringType, + "netflow_record_policy_name": types.StringType, +} + // TagAnnotationNetflowMonitorPolResourceModel describes the resource data model for the children without relation ships. type TagAnnotationNetflowMonitorPolResourceModel struct { Key types.String `tfsdk:"key"` @@ -153,9 +155,10 @@ type NetflowMonitorPolIdentifier struct { func (r *NetflowMonitorPolResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { if !req.Plan.Raw.IsNull() { - var planData, stateData *NetflowMonitorPolResourceModel + var planData, stateData, configData *NetflowMonitorPolResourceModel 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 resp.Diagnostics.HasError() { return @@ -171,11 +174,193 @@ func (r *NetflowMonitorPolResource) ModifyPlan(ctx context.Context, req resource return } } + if !configData.NetflowRsMonitorToRecord.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.NetflowRsMonitorToRecord.Attributes()) { + NetflowRsMonitorToRecordObject, _ := types.ObjectValueFrom(ctx, NetflowRsMonitorToRecordNetflowMonitorPolType, getEmptyNetflowRsMonitorToRecordNetflowMonitorPolResourceModel()) + planData.NetflowRsMonitorToRecord = NetflowRsMonitorToRecordObject + } + } resp.Diagnostics.Append(resp.Plan.Set(ctx, &planData)...) } } +type NetflowMonitorPolResourceModelV0 struct { + Id types.String `tfsdk:"id"` + ParentDn types.String `tfsdk:"parent_dn"` + Annotation types.String `tfsdk:"annotation"` + Descr types.String `tfsdk:"description"` + Name types.String `tfsdk:"name"` + NameAlias types.String `tfsdk:"name_alias"` + OwnerKey types.String `tfsdk:"owner_key"` + OwnerTag types.String `tfsdk:"owner_tag"` + NetflowRsMonitorToExporter types.Set `tfsdk:"relation_to_netflow_exporters"` + NetflowRsMonitorToRecord types.Set `tfsdk:"relation_to_netflow_record"` + TagAnnotation types.Set `tfsdk:"annotations"` + TagTag types.Set `tfsdk:"tags"` +} + +func (r *NetflowMonitorPolResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader { + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: &schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + "parent_dn": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + "annotation": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + "description": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + "name": schema.StringAttribute{ + Required: true, + Optional: true, + Computed: true, + }, + "name_alias": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + "owner_key": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + "owner_tag": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + "relation_to_netflow_exporters": schema.SetNestedAttribute{ + Required: false, + Optional: true, + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + "netflow_exporter_policy_name": schema.StringAttribute{ + Required: true, + Optional: false, + Computed: false, + }, + }, + }, + }, + "relation_to_netflow_record": schema.SetNestedAttribute{ + Required: false, + Optional: true, + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + "netflow_record_policy_name": schema.StringAttribute{ + Required: false, + Optional: true, + Computed: true, + }, + }, + }, + }, + "annotations": schema.SetNestedAttribute{ + Required: false, + Optional: true, + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "key": schema.StringAttribute{ + Required: true, + Optional: false, + Computed: false, + }, + "value": schema.StringAttribute{ + Required: true, + Optional: false, + Computed: false, + }, + }, + }, + }, + "tags": schema.SetNestedAttribute{ + Required: false, + Optional: true, + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "key": schema.StringAttribute{ + Required: true, + Optional: false, + Computed: false, + }, + "value": schema.StringAttribute{ + Required: true, + Optional: false, + Computed: false, + }, + }, + }, + }, + }, + }, + StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + var priorStateData NetflowMonitorPolResourceModelV0 + + resp.Diagnostics.Append(req.State.Get(ctx, &priorStateData)...) + + if resp.Diagnostics.HasError() { + return + } + + upgradedStateData := NetflowMonitorPolResourceModel{ + Id: priorStateData.Id, + ParentDn: priorStateData.ParentDn, + Annotation: priorStateData.Annotation, + Descr: priorStateData.Descr, + Name: priorStateData.Name, + NameAlias: priorStateData.NameAlias, + OwnerKey: priorStateData.OwnerKey, + OwnerTag: priorStateData.OwnerTag, + NetflowRsMonitorToExporter: priorStateData.NetflowRsMonitorToExporter, + NetflowRsMonitorToRecord: types.ObjectNull(NetflowRsMonitorToRecordNetflowMonitorPolType), + TagAnnotation: priorStateData.TagAnnotation, + TagTag: priorStateData.TagTag, + } + + var NetflowRsMonitorToRecordNetflowMonitorPolList []NetflowRsMonitorToRecordNetflowMonitorPolResourceModel + priorStateData.NetflowRsMonitorToRecord.ElementsAs(ctx, &NetflowRsMonitorToRecordNetflowMonitorPolList, false) + + if len(NetflowRsMonitorToRecordNetflowMonitorPolList) > 0 { + NetflowRsMonitorToRecordObject, _ := types.ObjectValueFrom(ctx, NetflowRsMonitorToRecordNetflowMonitorPolType, NetflowRsMonitorToRecordNetflowMonitorPolList[0]) + upgradedStateData.NetflowRsMonitorToRecord = NetflowRsMonitorToRecordObject + } + + resp.Diagnostics.Append(resp.State.Set(ctx, upgradedStateData)...) + }, + }, + } +} + 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" @@ -187,6 +372,7 @@ func (r *NetflowMonitorPolResource) Schema(ctx context.Context, req resource.Sch resp.Schema = schema.Schema{ // This description is used by the documentation generator and the language server. MarkdownDescription: "The netflow_monitor_policy resource for the 'netflowMonitorPol' class", + Version: 1, Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -288,34 +474,29 @@ func (r *NetflowMonitorPolResource) Schema(ctx context.Context, req resource.Sch }, }, }, - "relation_to_netflow_record": schema.SetNestedAttribute{ + "relation_to_netflow_record": schema.SingleNestedAttribute{ MarkdownDescription: ``, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), - }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The annotation of the Relation To NetFlow Record object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "netflow_record_policy_name": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `Name.`, + MarkdownDescription: `The annotation of the Relation To NetFlow Record object.`, + }, + "netflow_record_policy_name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + MarkdownDescription: `Name.`, }, }, }, @@ -433,9 +614,9 @@ func (r *NetflowMonitorPolResource) Create(ctx context.Context, req resource.Cre var netflowRsMonitorToExporterPlan, netflowRsMonitorToExporterState []NetflowRsMonitorToExporterNetflowMonitorPolResourceModel data.NetflowRsMonitorToExporter.ElementsAs(ctx, &netflowRsMonitorToExporterPlan, false) stateData.NetflowRsMonitorToExporter.ElementsAs(ctx, &netflowRsMonitorToExporterState, false) - var netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState []NetflowRsMonitorToRecordNetflowMonitorPolResourceModel - data.NetflowRsMonitorToRecord.ElementsAs(ctx, &netflowRsMonitorToRecordPlan, false) - stateData.NetflowRsMonitorToRecord.ElementsAs(ctx, &netflowRsMonitorToRecordState, false) + var netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState NetflowRsMonitorToRecordNetflowMonitorPolResourceModel + data.NetflowRsMonitorToRecord.As(ctx, &netflowRsMonitorToRecordPlan, basetypes.ObjectAsOptions{}) + stateData.NetflowRsMonitorToRecord.As(ctx, &netflowRsMonitorToRecordState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowMonitorPolResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -494,6 +675,13 @@ func (r *NetflowMonitorPolResource) Update(ctx context.Context, req resource.Upd // Read Terraform plan data into the model resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) resp.Diagnostics.Append(req.State.Get(ctx, &stateData)...) + // Error out when child object netflowRsMonitorToRecord is being deleted + if IsEmptySingleNestedAttribute(data.NetflowRsMonitorToRecord.Attributes()) && !IsEmptySingleNestedAttribute(stateData.NetflowRsMonitorToRecord.Attributes()) { + resp.Diagnostics.AddError( + "NetflowRsMonitorToRecord object cannot be deleted", + "deletion of child is only possible upon deletion of the parent", + ) + } if resp.Diagnostics.HasError() { return @@ -504,9 +692,9 @@ func (r *NetflowMonitorPolResource) Update(ctx context.Context, req resource.Upd var netflowRsMonitorToExporterPlan, netflowRsMonitorToExporterState []NetflowRsMonitorToExporterNetflowMonitorPolResourceModel data.NetflowRsMonitorToExporter.ElementsAs(ctx, &netflowRsMonitorToExporterPlan, false) stateData.NetflowRsMonitorToExporter.ElementsAs(ctx, &netflowRsMonitorToExporterState, false) - var netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState []NetflowRsMonitorToRecordNetflowMonitorPolResourceModel - data.NetflowRsMonitorToRecord.ElementsAs(ctx, &netflowRsMonitorToRecordPlan, false) - stateData.NetflowRsMonitorToRecord.ElementsAs(ctx, &netflowRsMonitorToRecordState, false) + var netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState NetflowRsMonitorToRecordNetflowMonitorPolResourceModel + data.NetflowRsMonitorToRecord.As(ctx, &netflowRsMonitorToRecordPlan, basetypes.ObjectAsOptions{}) + stateData.NetflowRsMonitorToRecord.As(ctx, &netflowRsMonitorToRecordState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationNetflowMonitorPolResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -665,8 +853,13 @@ func getAndSetNetflowMonitorPolAttributes(ctx context.Context, diags *diag.Diagn } netflowRsMonitorToExporterSet, _ := types.SetValueFrom(ctx, data.NetflowRsMonitorToExporter.ElementType(ctx), NetflowRsMonitorToExporterNetflowMonitorPolList) data.NetflowRsMonitorToExporter = netflowRsMonitorToExporterSet - netflowRsMonitorToRecordSet, _ := types.SetValueFrom(ctx, data.NetflowRsMonitorToRecord.ElementType(ctx), NetflowRsMonitorToRecordNetflowMonitorPolList) - data.NetflowRsMonitorToRecord = netflowRsMonitorToRecordSet + if len(NetflowRsMonitorToRecordNetflowMonitorPolList) == 1 { + netflowRsMonitorToRecordObject, _ := types.ObjectValueFrom(ctx, NetflowRsMonitorToRecordNetflowMonitorPolType, NetflowRsMonitorToRecordNetflowMonitorPolList[0]) + data.NetflowRsMonitorToRecord = netflowRsMonitorToRecordObject + } else { + netflowRsMonitorToRecordObject, _ := types.ObjectValueFrom(ctx, NetflowRsMonitorToRecordNetflowMonitorPolType, getEmptyNetflowRsMonitorToRecordNetflowMonitorPolResourceModel()) + data.NetflowRsMonitorToRecord = netflowRsMonitorToRecordObject + } tagAnnotationSet, _ := types.SetValueFrom(ctx, data.TagAnnotation.ElementType(ctx), TagAnnotationNetflowMonitorPolList) data.TagAnnotation = tagAnnotationSet tagTagSet, _ := types.SetValueFrom(ctx, data.TagTag.ElementType(ctx), TagTagNetflowMonitorPolList) @@ -754,31 +947,25 @@ func getNetflowMonitorPolNetflowRsMonitorToExporterChildPayloads(ctx context.Con return childPayloads } -func getNetflowMonitorPolNetflowRsMonitorToRecordChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *NetflowMonitorPolResourceModel, netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState []NetflowRsMonitorToRecordNetflowMonitorPolResourceModel) []map[string]interface{} { +func getNetflowMonitorPolNetflowRsMonitorToRecordChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *NetflowMonitorPolResourceModel, netflowRsMonitorToRecordPlan, netflowRsMonitorToRecordState NetflowRsMonitorToRecordNetflowMonitorPolResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.NetflowRsMonitorToRecord.IsUnknown() { - for _, netflowRsMonitorToRecord := range netflowRsMonitorToRecordPlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !netflowRsMonitorToRecord.Annotation.IsUnknown() && !netflowRsMonitorToRecord.Annotation.IsNull() { - childMap["attributes"]["annotation"] = netflowRsMonitorToRecord.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.NetflowRsMonitorToRecord.Attributes()) { + if !netflowRsMonitorToRecordPlan.Annotation.IsUnknown() && !netflowRsMonitorToRecordPlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = netflowRsMonitorToRecordPlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !netflowRsMonitorToRecord.TnNetflowRecordPolName.IsUnknown() && !netflowRsMonitorToRecord.TnNetflowRecordPolName.IsNull() { - childMap["attributes"]["tnNetflowRecordPolName"] = netflowRsMonitorToRecord.TnNetflowRecordPolName.ValueString() + if !netflowRsMonitorToRecordPlan.TnNetflowRecordPolName.IsUnknown() && !netflowRsMonitorToRecordPlan.TnNetflowRecordPolName.IsNull() { + childMap["attributes"]["tnNetflowRecordPolName"] = netflowRsMonitorToRecordPlan.TnNetflowRecordPolName.ValueString() } - childPayloads = append(childPayloads, map[string]interface{}{"netflowRsMonitorToRecord": childMap}) - } - if len(netflowRsMonitorToRecordPlan) == 0 && len(netflowRsMonitorToRecordState) == 1 { - diags.AddError( - "NetflowRsMonitorToRecord object cannot be deleted", - "deletion of child is only possible upon deletion of the parent", - ) - return nil } + childPayloads = append(childPayloads, map[string]interface{}{"netflowRsMonitorToRecord": childMap}) } else { - data.NetflowRsMonitorToRecord = types.SetNull(data.NetflowRsMonitorToRecord.ElementType(ctx)) + NetflowRsMonitorToRecordObject, _ := types.ObjectValueFrom(ctx, NetflowRsMonitorToRecordNetflowMonitorPolType, getEmptyNetflowRsMonitorToRecordNetflowMonitorPolResourceModel()) + data.NetflowRsMonitorToRecord = NetflowRsMonitorToRecordObject } return childPayloads @@ -862,7 +1049,7 @@ func getNetflowMonitorPolTagTagChildPayloads(ctx context.Context, diags *diag.Di return childPayloads } -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 { +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{} diff --git a/internal/provider/resource_aci_netflow_monitor_policy_test.go b/internal/provider/resource_aci_netflow_monitor_policy_test.go index 335f468ae..8fc442d82 100644 --- a/internal/provider/resource_aci_netflow_monitor_policy_test.go +++ b/internal/provider/resource_aci_netflow_monitor_policy_test.go @@ -155,8 +155,8 @@ func TestAccResourceNetflowMonitorPolWithFvTenant(t *testing.T) { resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.0.netflow_exporter_policy_name", "netflow_exporter_policy_name_0"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.1.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.1.netflow_exporter_policy_name", "netflow_exporter_policy_name_1"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.0.netflow_record_policy_name", "netflow_record_policy_name_1"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.netflow_record_policy_name", "netflow_record_policy_name_1"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.1.key", "key_1"), @@ -189,9 +189,8 @@ func TestAccResourceNetflowMonitorPolWithFvTenant(t *testing.T) { resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.1.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.1.netflow_exporter_policy_name", "netflow_exporter_policy_name_1"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.#", "2"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.0.netflow_record_policy_name", "netflow_record_policy_name_1"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.#", "1"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.netflow_record_policy_name", "netflow_record_policy_name_1"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.0.key", "key_0"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.1.key", "key_1"), @@ -210,9 +209,8 @@ func TestAccResourceNetflowMonitorPolWithFvTenant(t *testing.T) { resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.0.annotation", "annotation_2"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.0.netflow_exporter_policy_name", "netflow_exporter_policy_name_1"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.#", "1"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.0.netflow_record_policy_name", "netflow_record_policy_name_1"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.#", "1"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.netflow_record_policy_name", "netflow_record_policy_name_1"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.0.key", "key_1"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.0.value", "test_value"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.#", "1"), @@ -225,9 +223,8 @@ func TestAccResourceNetflowMonitorPolWithFvTenant(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "annotations.#", "0"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_exporters.#", "0"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.0.netflow_record_policy_name", "netflow_record_policy_name_1"), - resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.#", "1"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "relation_to_netflow_record.netflow_record_policy_name", "netflow_record_policy_name_1"), resource.TestCheckResourceAttr("aci_netflow_monitor_policy.test", "tags.#", "0"), ), }, @@ -301,12 +298,10 @@ resource "aci_netflow_monitor_policy" "test" { netflow_exporter_policy_name = "netflow_exporter_policy_name_1" }, ] - relation_to_netflow_record = [ - { - annotation = "annotation_1" - netflow_record_policy_name = "netflow_record_policy_name_1" - }, - ] + relation_to_netflow_record = { + annotation = "annotation_1" + netflow_record_policy_name = "netflow_record_policy_name_1" + } tags = [ { key = "key_0" diff --git a/internal/provider/resource_aci_vrf_fallback_route_group.go b/internal/provider/resource_aci_vrf_fallback_route_group.go index 3b80910de..61b23eb91 100644 --- a/internal/provider/resource_aci_vrf_fallback_route_group.go +++ b/internal/provider/resource_aci_vrf_fallback_route_group.go @@ -13,12 +13,12 @@ import ( "github.com/ciscoecosystem/aci-go-client/v2/client" "github.com/ciscoecosystem/aci-go-client/v2/container" - "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" @@ -51,7 +51,7 @@ type FvFBRGroupResourceModel struct { Name types.String `tfsdk:"name"` NameAlias types.String `tfsdk:"name_alias"` FvFBRMember types.Set `tfsdk:"vrf_fallback_route_group_members"` - FvFBRoute types.Set `tfsdk:"vrf_fallback_routes"` + FvFBRoute types.Object `tfsdk:"vrf_fallback_route"` TagAnnotation types.Set `tfsdk:"annotations"` TagTag types.Set `tfsdk:"tags"` } @@ -73,14 +73,12 @@ func getEmptyFvFBRGroupResourceModel() *FvFBRGroupResourceModel { "fallback_member": types.StringType, }, }), - FvFBRoute: types.SetNull(types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "annotation": types.StringType, - "description": types.StringType, - "prefix_address": types.StringType, - "name": types.StringType, - "name_alias": types.StringType, - }, + FvFBRoute: types.ObjectNull(map[string]attr.Type{ + "annotation": types.StringType, + "description": types.StringType, + "prefix_address": types.StringType, + "name": types.StringType, + "name_alias": types.StringType, }), TagAnnotation: types.SetNull(types.ObjectType{ AttrTypes: map[string]attr.Type{ @@ -135,6 +133,14 @@ func getEmptyFvFBRouteFvFBRGroupResourceModel() FvFBRouteFvFBRGroupResourceModel } } +var FvFBRouteFvFBRGroupType = map[string]attr.Type{ + "annotation": types.StringType, + "description": types.StringType, + "prefix_address": types.StringType, + "name": types.StringType, + "name_alias": types.StringType, +} + // TagAnnotationFvFBRGroupResourceModel describes the resource data model for the children without relation ships. type TagAnnotationFvFBRGroupResourceModel struct { Key types.String `tfsdk:"key"` @@ -167,9 +173,10 @@ type FvFBRGroupIdentifier struct { func (r *FvFBRGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { if !req.Plan.Raw.IsNull() { - var planData, stateData *FvFBRGroupResourceModel + var planData, stateData, configData *FvFBRGroupResourceModel 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 resp.Diagnostics.HasError() { return @@ -185,6 +192,12 @@ func (r *FvFBRGroupResource) ModifyPlan(ctx context.Context, req resource.Modify return } } + if !configData.FvFBRoute.IsNull() && stateData != nil { + if IsEmptySingleNestedAttribute(configData.FvFBRoute.Attributes()) { + FvFBRouteObject, _ := types.ObjectValueFrom(ctx, FvFBRouteFvFBRGroupType, getEmptyFvFBRouteFvFBRGroupResourceModel()) + planData.FvFBRoute = FvFBRouteObject + } + } resp.Diagnostics.Append(resp.Plan.Set(ctx, &planData)...) } @@ -306,57 +319,56 @@ func (r *FvFBRGroupResource) Schema(ctx context.Context, req resource.SchemaRequ }, }, }, - "vrf_fallback_routes": schema.SetNestedAttribute{ + "vrf_fallback_route": schema.SingleNestedAttribute{ MarkdownDescription: ``, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Set{ - setplanmodifier.UseStateForUnknown(), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), }, - Validators: []validator.Set{ - setvalidator.SizeAtMost(1), + Validators: []validator.Object{ + MakeSingleNestedAttributeRequiredAttributesNotProvidedValidator("vrf_fallback_route", []string{"prefix_address"}), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "annotation": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The annotation of the VRF Fallback Route object.`, + Attributes: map[string]schema.Attribute{ + "annotation": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "description": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The description of the VRF Fallback Route object.`, + MarkdownDescription: `The annotation of the VRF Fallback Route object.`, + }, + "description": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "prefix_address": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The prefix address of the VRF Fallback Route object.`, + MarkdownDescription: `The description of the VRF Fallback Route object.`, + }, + "prefix_address": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "name": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The name of the VRF Fallback Route object.`, + MarkdownDescription: `The prefix address of the VRF Fallback Route object.`, + }, + "name": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, - "name_alias": schema.StringAttribute{ - Optional: true, - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: `The name alias of the VRF Fallback Route object.`, + MarkdownDescription: `The name of the VRF Fallback Route object.`, + }, + "name_alias": schema.StringAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, + MarkdownDescription: `The name alias of the VRF Fallback Route object.`, }, }, }, @@ -474,9 +486,9 @@ func (r *FvFBRGroupResource) Create(ctx context.Context, req resource.CreateRequ var fvFBRMemberPlan, fvFBRMemberState []FvFBRMemberFvFBRGroupResourceModel data.FvFBRMember.ElementsAs(ctx, &fvFBRMemberPlan, false) stateData.FvFBRMember.ElementsAs(ctx, &fvFBRMemberState, false) - var fvFBRoutePlan, fvFBRouteState []FvFBRouteFvFBRGroupResourceModel - data.FvFBRoute.ElementsAs(ctx, &fvFBRoutePlan, false) - stateData.FvFBRoute.ElementsAs(ctx, &fvFBRouteState, false) + var fvFBRoutePlan, fvFBRouteState FvFBRouteFvFBRGroupResourceModel + data.FvFBRoute.As(ctx, &fvFBRoutePlan, basetypes.ObjectAsOptions{}) + stateData.FvFBRoute.As(ctx, &fvFBRouteState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationFvFBRGroupResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -545,9 +557,9 @@ func (r *FvFBRGroupResource) Update(ctx context.Context, req resource.UpdateRequ var fvFBRMemberPlan, fvFBRMemberState []FvFBRMemberFvFBRGroupResourceModel data.FvFBRMember.ElementsAs(ctx, &fvFBRMemberPlan, false) stateData.FvFBRMember.ElementsAs(ctx, &fvFBRMemberState, false) - var fvFBRoutePlan, fvFBRouteState []FvFBRouteFvFBRGroupResourceModel - data.FvFBRoute.ElementsAs(ctx, &fvFBRoutePlan, false) - stateData.FvFBRoute.ElementsAs(ctx, &fvFBRouteState, false) + var fvFBRoutePlan, fvFBRouteState FvFBRouteFvFBRGroupResourceModel + data.FvFBRoute.As(ctx, &fvFBRoutePlan, basetypes.ObjectAsOptions{}) + stateData.FvFBRoute.As(ctx, &fvFBRouteState, basetypes.ObjectAsOptions{}) var tagAnnotationPlan, tagAnnotationState []TagAnnotationFvFBRGroupResourceModel data.TagAnnotation.ElementsAs(ctx, &tagAnnotationPlan, false) stateData.TagAnnotation.ElementsAs(ctx, &tagAnnotationState, false) @@ -718,8 +730,13 @@ func getAndSetFvFBRGroupAttributes(ctx context.Context, diags *diag.Diagnostics, } fvFBRMemberSet, _ := types.SetValueFrom(ctx, data.FvFBRMember.ElementType(ctx), FvFBRMemberFvFBRGroupList) data.FvFBRMember = fvFBRMemberSet - fvFBRouteSet, _ := types.SetValueFrom(ctx, data.FvFBRoute.ElementType(ctx), FvFBRouteFvFBRGroupList) - data.FvFBRoute = fvFBRouteSet + if len(FvFBRouteFvFBRGroupList) == 1 { + fvFBRouteObject, _ := types.ObjectValueFrom(ctx, FvFBRouteFvFBRGroupType, FvFBRouteFvFBRGroupList[0]) + data.FvFBRoute = fvFBRouteObject + } else { + fvFBRouteObject, _ := types.ObjectValueFrom(ctx, FvFBRouteFvFBRGroupType, getEmptyFvFBRouteFvFBRGroupResourceModel()) + data.FvFBRoute = fvFBRouteObject + } tagAnnotationSet, _ := types.SetValueFrom(ctx, data.TagAnnotation.ElementType(ctx), TagAnnotationFvFBRGroupList) data.TagAnnotation = tagAnnotationSet tagTagSet, _ := types.SetValueFrom(ctx, data.TagTag.ElementType(ctx), TagTagFvFBRGroupList) @@ -816,52 +833,37 @@ func getFvFBRGroupFvFBRMemberChildPayloads(ctx context.Context, diags *diag.Diag return childPayloads } -func getFvFBRGroupFvFBRouteChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvFBRGroupResourceModel, fvFBRoutePlan, fvFBRouteState []FvFBRouteFvFBRGroupResourceModel) []map[string]interface{} { +func getFvFBRGroupFvFBRouteChildPayloads(ctx context.Context, diags *diag.Diagnostics, data *FvFBRGroupResourceModel, fvFBRoutePlan, fvFBRouteState FvFBRouteFvFBRGroupResourceModel) []map[string]interface{} { childPayloads := []map[string]interface{}{} if !data.FvFBRoute.IsUnknown() { - fvFBRouteIdentifiers := []FvFBRouteIdentifier{} - for _, fvFBRoute := range fvFBRoutePlan { - childMap := map[string]map[string]interface{}{"attributes": {}} - if !fvFBRoute.Annotation.IsUnknown() && !fvFBRoute.Annotation.IsNull() { - childMap["attributes"]["annotation"] = fvFBRoute.Annotation.ValueString() + childMap := map[string]map[string]interface{}{"attributes": {}} + if !IsEmptySingleNestedAttribute(data.FvFBRoute.Attributes()) { + if !fvFBRoutePlan.Annotation.IsUnknown() && !fvFBRoutePlan.Annotation.IsNull() { + childMap["attributes"]["annotation"] = fvFBRoutePlan.Annotation.ValueString() } else { childMap["attributes"]["annotation"] = globalAnnotation } - if !fvFBRoute.Descr.IsUnknown() && !fvFBRoute.Descr.IsNull() { - childMap["attributes"]["descr"] = fvFBRoute.Descr.ValueString() - } - if !fvFBRoute.FbrPrefix.IsUnknown() && !fvFBRoute.FbrPrefix.IsNull() { - childMap["attributes"]["fbrPrefix"] = fvFBRoute.FbrPrefix.ValueString() + if !fvFBRoutePlan.Descr.IsUnknown() && !fvFBRoutePlan.Descr.IsNull() { + childMap["attributes"]["descr"] = fvFBRoutePlan.Descr.ValueString() } - if !fvFBRoute.Name.IsUnknown() && !fvFBRoute.Name.IsNull() { - childMap["attributes"]["name"] = fvFBRoute.Name.ValueString() + if !fvFBRoutePlan.FbrPrefix.IsUnknown() && !fvFBRoutePlan.FbrPrefix.IsNull() { + childMap["attributes"]["fbrPrefix"] = fvFBRoutePlan.FbrPrefix.ValueString() } - if !fvFBRoute.NameAlias.IsUnknown() && !fvFBRoute.NameAlias.IsNull() { - childMap["attributes"]["nameAlias"] = fvFBRoute.NameAlias.ValueString() - } - childPayloads = append(childPayloads, map[string]interface{}{"fvFBRoute": childMap}) - fvFBRouteIdentifier := FvFBRouteIdentifier{} - fvFBRouteIdentifier.FbrPrefix = fvFBRoute.FbrPrefix - fvFBRouteIdentifiers = append(fvFBRouteIdentifiers, fvFBRouteIdentifier) - } - for _, fvFBRoute := range fvFBRouteState { - delete := true - for _, fvFBRouteIdentifier := range fvFBRouteIdentifiers { - if fvFBRouteIdentifier.FbrPrefix == fvFBRoute.FbrPrefix { - delete = false - break - } + if !fvFBRoutePlan.Name.IsUnknown() && !fvFBRoutePlan.Name.IsNull() { + childMap["attributes"]["name"] = fvFBRoutePlan.Name.ValueString() } - if delete { - childMap := map[string]map[string]interface{}{"attributes": {}} - childMap["attributes"]["status"] = "deleted" - childMap["attributes"]["fbrPrefix"] = fvFBRoute.FbrPrefix.ValueString() - childPayloads = append(childPayloads, map[string]interface{}{"fvFBRoute": childMap}) + if !fvFBRoutePlan.NameAlias.IsUnknown() && !fvFBRoutePlan.NameAlias.IsNull() { + childMap["attributes"]["nameAlias"] = fvFBRoutePlan.NameAlias.ValueString() } + } else { + childMap["attributes"]["fbrPrefix"] = fvFBRouteState.FbrPrefix.ValueString() + childMap["attributes"]["status"] = "deleted" } + childPayloads = append(childPayloads, map[string]interface{}{"fvFBRoute": childMap}) } else { - data.FvFBRoute = types.SetNull(data.FvFBRoute.ElementType(ctx)) + FvFBRouteObject, _ := types.ObjectValueFrom(ctx, FvFBRouteFvFBRGroupType, getEmptyFvFBRouteFvFBRGroupResourceModel()) + data.FvFBRoute = FvFBRouteObject } return childPayloads @@ -945,7 +947,7 @@ func getFvFBRGroupTagTagChildPayloads(ctx context.Context, diags *diag.Diagnosti return childPayloads } -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 { +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{} 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 b882719bb..882c47906 100644 --- a/internal/provider/resource_aci_vrf_fallback_route_group_test.go +++ b/internal/provider/resource_aci_vrf_fallback_route_group_test.go @@ -9,6 +9,9 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" ) func TestAccResourceFvFBRGroupWithFvCtx(t *testing.T) { @@ -139,6 +142,11 @@ func TestAccResourceFvFBRGroupWithFvCtx(t *testing.T) { resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "tags.0.value", "value_1"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "tags.1.key", "key_1"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "tags.1.value", "test_value"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.description", "description_1"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.name", "name_1"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.name_alias", "name_alias_1"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.prefix_address", "2.2.2.2/24"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.0.annotation", "annotation_1"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.0.description", "description_1"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.0.fallback_member", "2.2.2.2"), @@ -149,11 +157,6 @@ func TestAccResourceFvFBRGroupWithFvCtx(t *testing.T) { resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.1.fallback_member", "2.2.2.3"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.1.name", "name_2"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.1.name_alias", "name_alias_2"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.description", "description_1"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.name", "name_1"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.name_alias", "name_alias_1"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.prefix_address", "2.2.2.2/24"), ), }, // Refresh State before import testing to ensure that the state is up to date @@ -182,6 +185,11 @@ func TestAccResourceFvFBRGroupWithFvCtx(t *testing.T) { resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "tags.1.key", "key_1"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "tags.1.value", "test_value"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "tags.#", "2"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.annotation", "annotation_1"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.description", "description_1"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.name", "name_1"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.name_alias", "name_alias_1"), + resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route.prefix_address", "2.2.2.2/24"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.0.annotation", "annotation_1"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.0.description", "description_1"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.0.fallback_member", "2.2.2.2"), @@ -193,12 +201,6 @@ func TestAccResourceFvFBRGroupWithFvCtx(t *testing.T) { resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.1.name", "name_2"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.1.name_alias", "name_alias_2"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.#", "2"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.annotation", "annotation_1"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.description", "description_1"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.name", "name_1"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.name_alias", "name_alias_1"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.0.prefix_address", "2.2.2.2/24"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.#", "1"), ), }, // Update with children first child removed @@ -218,8 +220,21 @@ func TestAccResourceFvFBRGroupWithFvCtx(t *testing.T) { resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.0.name", "name_2"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.0.name_alias", "name_alias_2"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.#", "1"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.#", "0"), ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("aci_vrf_fallback_route_group.test", + tfjsonpath.New("vrf_fallback_route"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "description": knownvalue.Null(), + "name": knownvalue.Null(), + "name_alias": knownvalue.Null(), + "prefix_address": knownvalue.Null(), + }, + ), + ), + }, }, // Update with all children removed { @@ -229,8 +244,21 @@ func TestAccResourceFvFBRGroupWithFvCtx(t *testing.T) { resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "annotations.#", "0"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "tags.#", "0"), resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_route_group_members.#", "0"), - resource.TestCheckResourceAttr("aci_vrf_fallback_route_group.test", "vrf_fallback_routes.#", "0"), ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("aci_vrf_fallback_route_group.test", + tfjsonpath.New("vrf_fallback_route"), + knownvalue.MapExact( + map[string]knownvalue.Check{ + "annotation": knownvalue.Null(), + "description": knownvalue.Null(), + "name": knownvalue.Null(), + "name_alias": knownvalue.Null(), + "prefix_address": knownvalue.Null(), + }, + ), + ), + }, }, }, }) @@ -298,6 +326,13 @@ resource "aci_vrf_fallback_route_group" "test" { value = "test_value" }, ] + vrf_fallback_route = { + annotation = "annotation_1" + description = "description_1" + name = "name_1" + name_alias = "name_alias_1" + prefix_address = "2.2.2.2/24" + } vrf_fallback_route_group_members = [ { annotation = "annotation_1" @@ -314,15 +349,6 @@ resource "aci_vrf_fallback_route_group" "test" { name_alias = "name_alias_2" }, ] - vrf_fallback_routes = [ - { - annotation = "annotation_1" - description = "description_1" - name = "name_1" - name_alias = "name_alias_1" - prefix_address = "2.2.2.2/24" - }, - ] } ` @@ -349,6 +375,7 @@ resource "aci_vrf_fallback_route_group" "test" { value = "test_value" }, ] + vrf_fallback_route = {} vrf_fallback_route_group_members = [ { annotation = "annotation_2" @@ -358,7 +385,6 @@ resource "aci_vrf_fallback_route_group" "test" { name_alias = "name_alias_2" }, ] - vrf_fallback_routes = [] } ` @@ -368,7 +394,7 @@ resource "aci_vrf_fallback_route_group" "test" { name = "fallback_route_group" annotations = [] tags = [] + vrf_fallback_route = {} vrf_fallback_route_group_members = [] - vrf_fallback_routes = [] } ` diff --git a/internal/provider/utils.go b/internal/provider/utils.go index 8518cfeed..cb797732f 100644 --- a/internal/provider/utils.go +++ b/internal/provider/utils.go @@ -9,6 +9,7 @@ import ( "github.com/ciscoecosystem/aci-go-client/v2/client" "github.com/ciscoecosystem/aci-go-client/v2/container" "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -16,6 +17,24 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" ) +func IsEmptySingleNestedAttribute(attributes map[string]attr.Value) bool { + for _, value := range attributes { + if !value.IsNull() { + return false + } + } + return true +} + +func SingleNestedAttributeRequiredAttributesNotProvided(attributes map[string]attr.Value, requiredAttributes []string) bool { + for _, requiredAttribute := range requiredAttributes { + if attributes[requiredAttribute].IsNull() { + return true + } + } + return false +} + func ContainsString(strings []string, matchString string) bool { for _, stringValue := range strings { if stringValue == matchString { @@ -206,3 +225,45 @@ func (v MakeStringRequiredValidator) ValidateString(ctx context.Context, req val func MakeStringRequired() validator.String { return MakeStringRequiredValidator{} } + +// SingleNestedAttributeRequiredAttributesNotProvidedValidator validates that all required attributes are provided when it is not {} +// {} logic is needed in order to remove the child object from APIC + +var _ validator.Object = SingleNestedAttributeRequiredAttributesNotProvidedValidator{} + +type SingleNestedAttributeRequiredAttributesNotProvidedValidator struct { + attributeName string + requiredAttributes []string +} + +func (v SingleNestedAttributeRequiredAttributesNotProvidedValidator) Description(_ context.Context) string { + return "is required" +} + +func (v SingleNestedAttributeRequiredAttributesNotProvidedValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v SingleNestedAttributeRequiredAttributesNotProvidedValidator) ValidateObject(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) { + if req.ConfigValue.IsNull() { + return + } + + if !IsEmptySingleNestedAttribute(req.ConfigValue.Attributes()) && SingleNestedAttributeRequiredAttributesNotProvided(req.ConfigValue.Attributes(), v.requiredAttributes) { + errMessage := fmt.Sprintf("Inappropriate value for attribute \"%s\": attribute \"%s\" is required.", v.attributeName, strings.Join(v.requiredAttributes, ", ")) + if len(v.requiredAttributes) > 1 { + errMessage = fmt.Sprintf("Inappropriate values for attribute \"%s\": attributes \"%s\" are required.", v.attributeName, strings.Join(v.requiredAttributes, "\", \"")) + } + resp.Diagnostics.AddError( + "Incorrect attribute value type", + errMessage, + ) + } +} + +func MakeSingleNestedAttributeRequiredAttributesNotProvidedValidator(atributeName string, requiredAttributes []string) validator.Object { + return SingleNestedAttributeRequiredAttributesNotProvidedValidator{ + attributeName: atributeName, + requiredAttributes: requiredAttributes, + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/doc.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/doc.go new file mode 100644 index 000000000..e0352e03d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/doc.go @@ -0,0 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package objectplanmodifier provides plan modifiers for types.Object attributes. +package objectplanmodifier diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace.go new file mode 100644 index 000000000..0154948a7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package objectplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplace returns a plan modifier that conditionally requires +// resource replacement if: +// +// - The resource is planned for update. +// - The plan and state values are not equal. +// +// Use RequiresReplaceIfConfigured if the resource replacement should +// only occur if there is a configuration value (ignore unconfigured drift +// detection changes). Use RequiresReplaceIf if the resource replacement +// should check provider-defined conditional logic. +func RequiresReplace() planmodifier.Object { + return RequiresReplaceIf( + func(_ context.Context, _ planmodifier.ObjectRequest, resp *RequiresReplaceIfFuncResponse) { + resp.RequiresReplace = true + }, + "If the value of this attribute changes, Terraform will destroy and recreate the resource.", + "If the value of this attribute changes, Terraform will destroy and recreate the resource.", + ) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace_if.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace_if.go new file mode 100644 index 000000000..ea82beb5f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace_if.go @@ -0,0 +1,73 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package objectplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplaceIf returns a plan modifier that conditionally requires +// resource replacement if: +// +// - The resource is planned for update. +// - The plan and state values are not equal. +// - The given function returns true. Returning false will not unset any +// prior resource replacement. +// +// Use RequiresReplace if the resource replacement should always occur on value +// changes. Use RequiresReplaceIfConfigured if the resource replacement should +// occur on value changes, but only if there is a configuration value (ignore +// unconfigured drift detection changes). +func RequiresReplaceIf(f RequiresReplaceIfFunc, description, markdownDescription string) planmodifier.Object { + return requiresReplaceIfModifier{ + ifFunc: f, + description: description, + markdownDescription: markdownDescription, + } +} + +// requiresReplaceIfModifier is an plan modifier that sets RequiresReplace +// on the attribute if a given function is true. +type requiresReplaceIfModifier struct { + ifFunc RequiresReplaceIfFunc + description string + markdownDescription string +} + +// Description returns a human-readable description of the plan modifier. +func (m requiresReplaceIfModifier) Description(_ context.Context) string { + return m.description +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m requiresReplaceIfModifier) MarkdownDescription(_ context.Context) string { + return m.markdownDescription +} + +// PlanModifyObject implements the plan modification logic. +func (m requiresReplaceIfModifier) PlanModifyObject(ctx context.Context, req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse) { + // Do not replace on resource creation. + if req.State.Raw.IsNull() { + return + } + + // Do not replace on resource destroy. + if req.Plan.Raw.IsNull() { + return + } + + // Do not replace if the plan and state values are equal. + if req.PlanValue.Equal(req.StateValue) { + return + } + + ifFuncResp := &RequiresReplaceIfFuncResponse{} + + m.ifFunc(ctx, req, ifFuncResp) + + resp.Diagnostics.Append(ifFuncResp.Diagnostics...) + resp.RequiresReplace = ifFuncResp.RequiresReplace +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace_if_configured.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace_if_configured.go new file mode 100644 index 000000000..8dfc1c0ff --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace_if_configured.go @@ -0,0 +1,34 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package objectplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplaceIfConfigured returns a plan modifier that conditionally requires +// resource replacement if: +// +// - The resource is planned for update. +// - The plan and state values are not equal. +// - The configuration value is not null. +// +// Use RequiresReplace if the resource replacement should occur regardless of +// the presence of a configuration value. Use RequiresReplaceIf if the resource +// replacement should check provider-defined conditional logic. +func RequiresReplaceIfConfigured() planmodifier.Object { + return RequiresReplaceIf( + func(_ context.Context, req planmodifier.ObjectRequest, resp *RequiresReplaceIfFuncResponse) { + if req.ConfigValue.IsNull() { + return + } + + resp.RequiresReplace = true + }, + "If the value of this attribute is configured and changes, Terraform will destroy and recreate the resource.", + "If the value of this attribute is configured and changes, Terraform will destroy and recreate the resource.", + ) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace_if_func.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace_if_func.go new file mode 100644 index 000000000..e7b30ddfa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/requires_replace_if_func.go @@ -0,0 +1,25 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package objectplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// RequiresReplaceIfFunc is a conditional function used in the RequiresReplaceIf +// plan modifier to determine whether the attribute requires replacement. +type RequiresReplaceIfFunc func(context.Context, planmodifier.ObjectRequest, *RequiresReplaceIfFuncResponse) + +// RequiresReplaceIfFuncResponse is the response type for a RequiresReplaceIfFunc. +type RequiresReplaceIfFuncResponse struct { + // Diagnostics report errors or warnings related to this logic. An empty + // or unset slice indicates success, with no warnings or errors generated. + Diagnostics diag.Diagnostics + + // RequiresReplace should be enabled if the resource should be replaced. + RequiresReplace bool +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/use_state_for_unknown.go b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/use_state_for_unknown.go new file mode 100644 index 000000000..67b13f18b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier/use_state_for_unknown.go @@ -0,0 +1,55 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package objectplanmodifier + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" +) + +// UseStateForUnknown returns a plan modifier that copies a known prior state +// value into the planned value. Use this when it is known that an unconfigured +// value will remain the same after a resource update. +// +// To prevent Terraform errors, the framework automatically sets unconfigured +// and Computed attributes to an unknown value "(known after apply)" on update. +// Using this plan modifier will instead display the prior state value in the +// plan, unless a prior plan modifier adjusts the value. +func UseStateForUnknown() planmodifier.Object { + return useStateForUnknownModifier{} +} + +// useStateForUnknownModifier implements the plan modifier. +type useStateForUnknownModifier struct{} + +// Description returns a human-readable description of the plan modifier. +func (m useStateForUnknownModifier) Description(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m useStateForUnknownModifier) MarkdownDescription(_ context.Context) string { + return "Once set, the value of this attribute in state will not change." +} + +// PlanModifyObject implements the plan modification logic. +func (m useStateForUnknownModifier) PlanModifyObject(_ context.Context, req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse) { + // Do nothing if there is no state value. + if req.StateValue.IsNull() { + return + } + + // Do nothing if there is a known planned value. + if !req.PlanValue.IsUnknown() { + return + } + + // Do nothing if there is an unknown configuration value, otherwise interpolation gets messed up. + if req.ConfigValue.IsUnknown() { + return + } + + resp.PlanValue = req.StateValue +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 0b2af3c7a..5e5a3a38e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -171,6 +171,7 @@ github.com/hashicorp/terraform-plugin-framework/resource/schema github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault github.com/hashicorp/terraform-plugin-framework/resource/schema/defaults github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier +github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault