diff --git a/apstra/blueprint/datacenter_generic_system.go b/apstra/blueprint/datacenter_generic_system.go index ab7a9ebc..0a3e7798 100644 --- a/apstra/blueprint/datacenter_generic_system.go +++ b/apstra/blueprint/datacenter_generic_system.go @@ -2,6 +2,7 @@ package blueprint import ( "context" + "errors" "fmt" "github.com/Juniper/apstra-go-sdk/apstra" apiversions "github.com/Juniper/terraform-provider-apstra/apstra/api_versions" @@ -30,19 +31,20 @@ import ( ) type DatacenterGenericSystem struct { - Id types.String `tfsdk:"id"` - BlueprintId types.String `tfsdk:"blueprint_id"` - Name types.String `tfsdk:"name"` - Hostname types.String `tfsdk:"hostname"` - Tags types.Set `tfsdk:"tags"` - Links types.Set `tfsdk:"links"` - Asn types.Int64 `tfsdk:"asn"` - LoopbackIpv4 cidrtypes.IPv4Prefix `tfsdk:"loopback_ipv4"` - LoopbackIpv6 cidrtypes.IPv6Prefix `tfsdk:"loopback_ipv6"` - PortChannelIdMin types.Int64 `tfsdk:"port_channel_id_min"` - PortChannelIdMax types.Int64 `tfsdk:"port_channel_id_max"` - External types.Bool `tfsdk:"external"` - DeployMode types.String `tfsdk:"deploy_mode"` + Id types.String `tfsdk:"id"` + BlueprintId types.String `tfsdk:"blueprint_id"` + Name types.String `tfsdk:"name"` + Hostname types.String `tfsdk:"hostname"` + Tags types.Set `tfsdk:"tags"` + Links types.Set `tfsdk:"links"` + Asn types.Int64 `tfsdk:"asn"` + LoopbackIpv4 cidrtypes.IPv4Prefix `tfsdk:"loopback_ipv4"` + LoopbackIpv6 cidrtypes.IPv6Prefix `tfsdk:"loopback_ipv6"` + PortChannelIdMin types.Int64 `tfsdk:"port_channel_id_min"` + PortChannelIdMax types.Int64 `tfsdk:"port_channel_id_max"` + External types.Bool `tfsdk:"external"` + DeployMode types.String `tfsdk:"deploy_mode"` + ClearCtsOnDestroy types.Bool `tfsdk:"clear_cts_on_destroy"` } func (o DatacenterGenericSystem) ResourceAttributes() map[string]resourceSchema.Attribute { @@ -149,6 +151,12 @@ func (o DatacenterGenericSystem) ResourceAttributes() map[string]resourceSchema. Default: stringdefault.StaticString(apstra.NodeDeployModeDeploy.String()), Validators: []validator.String{stringvalidator.OneOf(utils.AllNodeDeployModes()...)}, }, + "clear_cts_on_destroy": resourceSchema.BoolAttribute{ + MarkdownDescription: "When `true`, Link deletion in `destroy` phase and `apply` phase (where a Link has " + + "been removed from the configuration) will automatically clear Connectivity Template assignments " + + "from interfaces associated with those Links.", + Optional: true, + }, } } @@ -497,11 +505,39 @@ func (o *DatacenterGenericSystem) deleteLinksFromSystem(ctx context.Context, lin return } + var ace apstra.ClientErr + var pendingDiags diag.Diagnostics + err := bp.DeleteLinksFromSystem(ctx, linkIdsToDelete) - if err != nil { - diags.AddError( + if err == nil { + return // success! + } else { + pendingDiags.AddError( fmt.Sprintf("failed deleting links %v from generic system %s", linkIdsToDelete, o.Id), err.Error()) + if !(errors.As(err, &ace) && ace.Type() == apstra.ErrCtAssignedToLink && ace.Detail() != nil && o.ClearCtsOnDestroy.ValueBool()) { + // we cannot handle the error + diags.Append(pendingDiags...) + return + } + } + + // we got here because some links have CTs attached. + + // try to clear the connectivity templates from the problem links + o.ClearConnectivityTemplatesFromLinks(ctx, ace.Detail().(apstra.ErrCtAssignedToLinkDetail).LinkIds, bp, diags) + if diags.HasError() { + diags.Append(pendingDiags...) + return + } + + // try deleting the links again + err = bp.DeleteLinksFromSystem(ctx, linkIdsToDelete) + if err != nil { + diags.AddError("failed second attempt to delete links after attempting to handle the link deletion error", + err.Error()) + diags.Append(pendingDiags...) + return } } @@ -799,3 +835,84 @@ func (o *DatacenterGenericSystem) setPortChannelIdMinMax(ctx context.Context, bp diags.AddError("failed setting generic system Port Channel Id Min and Max", err.Error()) } } + +func interfacesFromLinkIds(ctx context.Context, linkIds []apstra.ObjectId, bp *apstra.TwoStageL3ClosClient) ([]apstra.ObjectId, error) { + linkIdStringVals := make(apstra.QEStringValIsIn, len(linkIds)) + for i, linkId := range linkIds { + linkIdStringVals[i] = linkId.String() + } + + query := new(apstra.PathQuery). + SetBlueprintId(bp.Id()). + SetClient(bp.Client()). + Node([]apstra.QEEAttribute{ + apstra.NodeTypeLink.QEEAttribute(), + {Key: "id", Value: linkIdStringVals}, + }). + In([]apstra.QEEAttribute{apstra.RelationshipTypeLink.QEEAttribute()}). + Node([]apstra.QEEAttribute{ + apstra.NodeTypeInterface.QEEAttribute(), + {Key: "name", Value: apstra.QEStringVal("n_interface")}, + }) + + var queryResult struct { + Items []struct { + Interface struct { + Id apstra.ObjectId `json:"id"` + } `json:"n_interface"` + } `json:"items"` + } + + err := query.Do(ctx, &queryResult) + if err != nil { + return nil, fmt.Errorf("failed while querying for interfaces from link IDs - %w", err) + } + + result := make([]apstra.ObjectId, len(queryResult.Items)) + for i, item := range queryResult.Items { + result[i] = item.Interface.Id + } + + return result, nil +} + +func (o *DatacenterGenericSystem) ClearConnectivityTemplatesFromLinks(ctx context.Context, linkIds []apstra.ObjectId, bp *apstra.TwoStageL3ClosClient, diags *diag.Diagnostics) { + // first learn the interface IDs from the link IDs. This will give us both ends of each link, but that's okay. + interfaceIds, err := interfacesFromLinkIds(ctx, linkIds, bp) + if err != nil { + diags.AddError( + "failed determining interface ids from link ids while attempting to handle the link deletion error", + err.Error()) + return + } + + // now collect all interface-to-CT assignments + interfaceToCts, err := bp.GetAllInterfacesConnectivityTemplates(ctx) + if err != nil { + diags.AddError( + "failed determining current connectivity template assignments while attempting to handle the link deletion error", + err.Error()) + return + } + + // create a new assignments map which will clear the problem CTs + newAssignments := make(map[apstra.ObjectId]map[apstra.ObjectId]bool) + for _, interfaceId := range interfaceIds { + if ctIds, ok := interfaceToCts[interfaceId]; ok { + assignment := make(map[apstra.ObjectId]bool, len(ctIds)) + for _, ctId := range ctIds { + assignment[ctId] = false + } + newAssignments[interfaceId] = assignment + } + } + + // send the new assignments to apstra + err = bp.SetApplicationPointsConnectivityTemplates(ctx, newAssignments) + if err != nil { + diags.AddError( + "failed clearing connectivity templates from interfaces while attempting to handle the link deletion error", + err.Error()) + return + } +} diff --git a/apstra/data_source_datacenter_routing_zone_test.go b/apstra/data_source_datacenter_routing_zone_test.go index 9b2bb85a..223d4f5f 100644 --- a/apstra/data_source_datacenter_routing_zone_test.go +++ b/apstra/data_source_datacenter_routing_zone_test.go @@ -34,16 +34,10 @@ func TestDataSourceDatacenterRoutingZone_A(t *testing.T) { } }() - szId, szDelete, err := testutils.SecurityZoneA(ctx, bpClient) + szId := testutils.SecurityZoneA(t, ctx, bpClient) if err != nil { - t.Fatal(errors.Join(err, szDelete(ctx))) + t.Fatal(err) } - defer func() { - err = szDelete(ctx) - if err != nil { - t.Error(err) - } - }() sz, err := bpClient.GetSecurityZone(ctx, szId) if err != nil { diff --git a/apstra/resource_datacenter_generic_system.go b/apstra/resource_datacenter_generic_system.go index 2464b559..0a4729bf 100644 --- a/apstra/resource_datacenter_generic_system.go +++ b/apstra/resource_datacenter_generic_system.go @@ -2,10 +2,12 @@ package tfapstra import ( "context" + "errors" "fmt" "github.com/Juniper/apstra-go-sdk/apstra" "github.com/Juniper/terraform-provider-apstra/apstra/blueprint" "github.com/Juniper/terraform-provider-apstra/apstra/utils" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" @@ -249,7 +251,29 @@ func (o *resourceDatacenterGenericSystem) Delete(ctx context.Context, req resour if utils.IsApstra404(err) { return // 404 is okay } - resp.Diagnostics.AddError("failed to delete generic system", err.Error()) + + var pendingDiags diag.Diagnostics + pendingDiags.AddError("failed to delete generic system", err.Error()) + + var ace apstra.ClientErr + if !(errors.As(err, &ace) && ace.Type() == apstra.ErrCtAssignedToLink && ace.Detail() != nil && state.ClearCtsOnDestroy.ValueBool()) { + resp.Diagnostics.Append(pendingDiags...) // cannot handle error + return + } + + // attempt to handle error by clearing CTs + state.ClearConnectivityTemplatesFromLinks(ctx, ace.Detail().(apstra.ErrCtAssignedToLinkDetail).LinkIds, bp, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + resp.Diagnostics.Append(pendingDiags...) + return + } + + // try again + err = bp.DeleteGenericSystem(ctx, apstra.ObjectId(state.Id.ValueString())) + if err != nil { + resp.Diagnostics.AddError("failed to delete generic system after clearing CTs from interfaces", err.Error()) + resp.Diagnostics.Append(pendingDiags...) + } } } diff --git a/apstra/resource_datacenter_generic_system_test.go b/apstra/resource_datacenter_generic_system_test.go index 6ff013b2..9a92d342 100644 --- a/apstra/resource_datacenter_generic_system_test.go +++ b/apstra/resource_datacenter_generic_system_test.go @@ -6,6 +6,7 @@ import ( "github.com/Juniper/apstra-go-sdk/apstra" testutils "github.com/Juniper/terraform-provider-apstra/apstra/test_utils" "github.com/Juniper/terraform-provider-apstra/apstra/utils" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "net" "strconv" @@ -16,17 +17,18 @@ import ( const ( resourceDataCenterGenericSystemHCL = ` resource "apstra_datacenter_generic_system" "test" { - blueprint_id = %s - name = %s - hostname = %s - asn = %s - loopback_ipv4 = %s - loopback_ipv6 = %s - tags = %s - deploy_mode = %s - port_channel_id_min = %s - port_channel_id_max = %s - links = [ + blueprint_id = %s + name = %s + hostname = %s + asn = %s + loopback_ipv4 = %s + loopback_ipv6 = %s + tags = %s + deploy_mode = %s + port_channel_id_min = %s + port_channel_id_max = %s + clear_cts_on_destroy = %t + links = [ %s ] } ` @@ -91,7 +93,7 @@ func TestResourceDatacenterGenericSystem_A(t *testing.T) { type link struct { tags []string lagMode apstra.RackLinkLagMode - targetSwitchId string + targetSwitchId apstra.ObjectId targetSwitchIf string targetSwitchTf int groupLabel string @@ -100,7 +102,7 @@ func TestResourceDatacenterGenericSystem_A(t *testing.T) { return fmt.Sprintf(resourceDataCenterGenericSystemLinkHCL, renderTags(in.tags), stringOrNull(in.lagMode.String()), - stringOrNull(in.targetSwitchId), + stringOrNull(in.targetSwitchId.String()), stringOrNull(in.targetSwitchIf), in.targetSwitchTf, stringOrNull(in.groupLabel), @@ -115,17 +117,18 @@ func TestResourceDatacenterGenericSystem_A(t *testing.T) { } type genericSystem struct { - bpId string - name string - hostname string - asn *int - loopback4 *net.IPNet - loopback6 *net.IPNet - tags tagSlice - deployMode string - portChannelIdMin int - portChannelIdMax int - links []link + bpId string + name string + hostname string + asn *int + loopback4 *net.IPNet + loopback6 *net.IPNet + tags tagSlice + deployMode string + portChannelIdMin int + portChannelIdMax int + clearCtsOnDestroy bool + links []link } renderGenericSystem := func(in genericSystem) string { return fmt.Sprintf(resourceDataCenterGenericSystemHCL, @@ -139,47 +142,71 @@ func TestResourceDatacenterGenericSystem_A(t *testing.T) { stringOrNull(in.deployMode), zeroAsNull(in.portChannelIdMin), zeroAsNull(in.portChannelIdMax), + in.clearCtsOnDestroy, renderLinks(in.links), ) } - _ = renderGenericSystem - leafQuery := new(apstra.PathQuery). - SetBlueprintType(apstra.BlueprintTypeStaging). - SetBlueprintId(bpClient.Id()). - SetClient(bpClient.Client()). - Node([]apstra.QEEAttribute{ - apstra.NodeTypeSystem.QEEAttribute(), - {Key: "role", Value: apstra.QEStringVal("leaf")}, - {Key: "name", Value: apstra.QEStringVal("n_leaf")}, - }) - var leafQueryResult struct { - Items []struct { - Leaf struct { - Id string `json:"id"` - } `json:"n_leaf"` - } `json:"items"` + type systemNode struct { + Label string `json:"label"` + Id apstra.ObjectId `json:"id"` + Role string `json:"role"` } - err = leafQuery.Do(ctx, &leafQueryResult) + response := struct { + Nodes map[string]systemNode `json:"nodes"` + }{} + err = bpClient.Client().GetNodes(ctx, bpClient.Id(), apstra.NodeTypeSystem, &response) if err != nil { t.Fatal(err) } - leafIds := make([]string, len(leafQueryResult.Items)) - for i, item := range leafQueryResult.Items { - leafIds[i] = item.Leaf.Id + var leafIds []apstra.ObjectId + for _, system := range response.Nodes { + if system.Role == "leaf" { + leafIds = append(leafIds, system.Id) + } + } + if len(leafIds) == 0 { + t.Fatal("no leafs found") } // assign the leaf switch interface map err = bpClient.SetInterfaceMapAssignments(ctx, apstra.SystemIdToInterfaceMapAssignment{ - leafIds[0]: "Juniper_QFX5100-48T_Junos__AOS-48x10_6x40-1", + leafIds[0].String(): "Juniper_QFX5100-48T_Junos__AOS-48x10_6x40-1", }) if err != nil { t.Fatal(err) } - type testCase struct { - genericSystem genericSystem - testCheckFunc resource.TestCheckFunc + // discover the routing zones + szs, err := bpClient.GetAllSecurityZones(ctx) + if err != nil { + t.Fatal(err) + } + + // create a connectivity template + ct := apstra.ConnectivityTemplate{ + Label: acctest.RandString(5), + Subpolicies: []*apstra.ConnectivityTemplatePrimitive{ + { + Label: "", + Attributes: &apstra.ConnectivityTemplatePrimitiveAttributesAttachLogicalLink{ + SecurityZone: &szs[0].Id, + IPv4AddressingType: apstra.CtPrimitiveIPv4AddressingTypeNumbered, + }, + }, + }, + } + err = ct.SetIds() + if err != nil { + t.Fatal(err) + } + err = ct.SetUserData() + if err != nil { + t.Fatal(err) + } + err = bpClient.CreateConnectivityTemplate(ctx, &ct) + if err != nil { + t.Fatal(err) } // "A" and "B" represent first and second config state in multi-step test @@ -211,325 +238,443 @@ func TestResourceDatacenterGenericSystem_A(t *testing.T) { Mask: net.CIDRMask(64, 128), } - testCases := []testCase{ - { - genericSystem: genericSystem{ - // name: "", - // hostname: "", - // tags: []string{}, - links: []link{ - { - // lagMode: apstra.RackLinkLagModeNone, - // groupLabel: "", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/6", - targetSwitchTf: 1, + attachCtToPort := func(portName string) { + query := new(apstra.PathQuery). + SetBlueprintId(bpClient.Id()). + SetClient(bpClient.Client()). + Node([]apstra.QEEAttribute{{Key: "id", Value: apstra.QEStringVal(leafIds[0])}}). + Out([]apstra.QEEAttribute{apstra.RelationshipTypeHostedInterfaces.QEEAttribute()}). + Node([]apstra.QEEAttribute{ + apstra.NodeTypeInterface.QEEAttribute(), + {Key: "if_name", Value: apstra.QEStringVal(portName)}, + {Key: "name", Value: apstra.QEStringVal("n_interface")}, + }) + var response struct { + Items []struct { + Interface struct { + Id apstra.ObjectId `json:"id"` + } `json:"n_interface"` + } `json:"items"` + } + err := query.Do(context.Background(), &response) + if err != nil { + t.Fatal(err) + } + err = bpClient.SetApplicationPointConnectivityTemplates(context.Background(), response.Items[0].Interface.Id, []apstra.ObjectId{*ct.Id}) + if err != nil { + t.Fatal(err) + } + } + + type testStep struct { + genericSystem genericSystem + testCheckFunc resource.TestCheckFunc + preConfig func() + } + + type testCase struct { + steps []testStep + } + + testCases := map[string]testCase{ + "lots_of_changes": { + steps: []testStep{ + { + genericSystem: genericSystem{ + // name: "", + // hostname: "", // tags: []string{}, + links: []link{ + { + // lagMode: apstra.RackLinkLagModeNone, + // groupLabel: "", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/6", + targetSwitchTf: 1, + // tags: []string{}, + }, + }, }, + testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ + resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "id"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "blueprint_id", bpClient.Id().String()), + resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "name"), + resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "hostname"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "tags"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_id", leafIds[0].String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags"), + }...), }, - }, - testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ - resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "id"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "blueprint_id", bpClient.Id().String()), - resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "name"), - resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "hostname"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "tags"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_id", leafIds[0]), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags"), - }...), - }, - { - genericSystem: genericSystem{ - name: "foo", - hostname: "foo.com", - asn: &asnA, - loopback4: &lo4A, - loopback6: &lo6A, - tags: []string{"a"}, - portChannelIdMin: portChannelIdMinA, - portChannelIdMax: portChannelIdMaxA, - links: []link{ - { - lagMode: apstra.RackLinkLagModeActive, - groupLabel: "foo", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/6", - targetSwitchTf: 1, - tags: []string{"b"}, + { + genericSystem: genericSystem{ + name: "foo", + hostname: "foo.com", + asn: &asnA, + loopback4: &lo4A, + loopback6: &lo6A, + tags: []string{"a"}, + portChannelIdMin: portChannelIdMinA, + portChannelIdMax: portChannelIdMaxA, + links: []link{ + { + lagMode: apstra.RackLinkLagModeActive, + groupLabel: "foo", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/6", + targetSwitchTf: 1, + tags: []string{"b"}, + }, + }, }, + testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ + resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "id"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "blueprint_id", bpClient.Id().String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "name", "foo"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "hostname", "foo.com"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "tags.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "tags.0", "a"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "foo"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModeActive.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_id", leafIds[0].String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.0", "b"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "port_channel_id_min", fmt.Sprint(portChannelIdMinA)), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "port_channel_id_max", fmt.Sprint(portChannelIdMaxA)), + }...), }, - }, - testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ - resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "id"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "blueprint_id", bpClient.Id().String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "name", "foo"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "hostname", "foo.com"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "tags.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "tags.0", "a"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "foo"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModeActive.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_id", leafIds[0]), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.0", "b"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "port_channel_id_min", fmt.Sprint(portChannelIdMinA)), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "port_channel_id_max", fmt.Sprint(portChannelIdMaxA)), - }...), - }, - { - genericSystem: genericSystem{ - // name: "foo", - // hostname: "foo.com", - asn: &asnB, - loopback4: &lo4B, - loopback6: &lo6B, - // tags: []string{"a"}, - portChannelIdMin: portChannelIdMinB, - portChannelIdMax: portChannelIdMaxB, - links: []link{ - { - // lagMode: apstra.RackLinkLagModeActive, - // groupLabel: "foo", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/6", - targetSwitchTf: 1, + { + genericSystem: genericSystem{ + // name: "foo", + // hostname: "foo.com", + asn: &asnB, + loopback4: &lo4B, + loopback6: &lo6B, + // tags: []string{"a"}, + portChannelIdMin: portChannelIdMinB, + portChannelIdMax: portChannelIdMaxB, + links: []link{ + { + // lagMode: apstra.RackLinkLagModeActive, + // groupLabel: "foo", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/6", + targetSwitchTf: 1, - // tags: []string{"b"}, + // tags: []string{"b"}, + }, + }, }, + testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ + resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "id"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "blueprint_id", bpClient.Id().String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "name", "foo"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "hostname", "foo.com"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "asn", strconv.Itoa(asnB)), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv4", lo4B.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv6", lo6B.String()), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "tags"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "1"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_id", leafIds[0].String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "port_channel_id_min", fmt.Sprint(portChannelIdMinB)), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "port_channel_id_max", fmt.Sprint(portChannelIdMaxB)), + }...), }, - }, - testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ - resource.TestCheckResourceAttrSet("apstra_datacenter_generic_system.test", "id"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "blueprint_id", bpClient.Id().String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "name", "foo"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "hostname", "foo.com"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "asn", strconv.Itoa(asnB)), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv4", lo4B.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv6", lo6B.String()), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "tags"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "1"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_id", leafIds[0]), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "port_channel_id_min", fmt.Sprint(portChannelIdMinB)), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "port_channel_id_max", fmt.Sprint(portChannelIdMaxB)), - }...), - }, - { - genericSystem: genericSystem{ - // name: "foo", - // hostname: "foo.com", - // tags: []string{"a"}, - links: []link{ - { - lagMode: apstra.RackLinkLagModePassive, - groupLabel: "bar", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/6", - targetSwitchTf: 1, - tags: []string{"c"}, + { + genericSystem: genericSystem{ + // name: "foo", + // hostname: "foo.com", + // tags: []string{"a"}, + links: []link{ + { + lagMode: apstra.RackLinkLagModePassive, + groupLabel: "bar", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/6", + targetSwitchTf: 1, + tags: []string{"c"}, + }, + { + lagMode: apstra.RackLinkLagModePassive, + groupLabel: "bar", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/7", + targetSwitchTf: 1, + tags: []string{"c"}, + }, + }, }, - { - lagMode: apstra.RackLinkLagModePassive, - groupLabel: "bar", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/7", - targetSwitchTf: 1, - tags: []string{"c"}, + testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "asn"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv4"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv6"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "2"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "bar"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModePassive.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.0", "c"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.group_label", "bar"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.lag_mode", apstra.RackLinkLagModePassive.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_name", "xe-0/0/7"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_transform_id", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.0", "c"), + }...), + }, + { + genericSystem: genericSystem{ + // name: "foo", + // hostname: "foo.com", + // tags: []string{"a"}, + deployMode: apstra.NodeDeployModeReady.String(), + links: []link{ + { + lagMode: apstra.RackLinkLagModePassive, + groupLabel: "bar", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/6", + targetSwitchTf: 1, + tags: []string{"c"}, + }, + { + lagMode: apstra.RackLinkLagModePassive, + groupLabel: "bar", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/7", + targetSwitchTf: 1, + tags: []string{"c"}, + }, + }, }, + testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "asn"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv4"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv6"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "deploy_mode", apstra.NodeDeployModeReady.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "2"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "bar"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModePassive.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.0", "c"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.group_label", "bar"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.lag_mode", apstra.RackLinkLagModePassive.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_name", "xe-0/0/7"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_transform_id", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.0", "c"), + }...), }, - }, - testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "asn"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv4"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv6"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "2"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "bar"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModePassive.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.0", "c"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.group_label", "bar"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.lag_mode", apstra.RackLinkLagModePassive.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_name", "xe-0/0/7"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_transform_id", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.0", "c"), - }...), - }, - { - genericSystem: genericSystem{ - // name: "foo", - // hostname: "foo.com", - // tags: []string{"a"}, - deployMode: apstra.NodeDeployModeReady.String(), - links: []link{ - { - lagMode: apstra.RackLinkLagModePassive, - groupLabel: "bar", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/6", - targetSwitchTf: 1, - tags: []string{"c"}, + { + genericSystem: genericSystem{ + // name: "foo", + // hostname: "foo.com", + // tags: []string{"a"}, + deployMode: apstra.NodeDeployModeDeploy.String(), + links: []link{ + { + lagMode: apstra.RackLinkLagModePassive, + groupLabel: "bar", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/6", + targetSwitchTf: 1, + tags: []string{"c"}, + }, + { + lagMode: apstra.RackLinkLagModePassive, + groupLabel: "bar", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/7", + targetSwitchTf: 1, + tags: []string{"c"}, + }, + }, + }, + testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "asn"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv4"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv6"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "deploy_mode", apstra.NodeDeployModeDeploy.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "2"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "bar"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModePassive.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.0", "c"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.group_label", "bar"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.lag_mode", apstra.RackLinkLagModePassive.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_name", "xe-0/0/7"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_transform_id", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.#", "1"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.0", "c"), + }...), + }, + { + genericSystem: genericSystem{ + // name: "foo", + // hostname: "foo.com", + // tags: []string{"a"}, + links: []link{ + { + lagMode: apstra.RackLinkLagModeStatic, + groupLabel: "baz", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/6", + targetSwitchTf: 1, + // tags: []string{"c"}, + }, + { + lagMode: apstra.RackLinkLagModeStatic, + groupLabel: "baz", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/7", + targetSwitchTf: 1, + // tags: []string{"c"}, + }, + }, }, - { - lagMode: apstra.RackLinkLagModePassive, - groupLabel: "bar", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/7", - targetSwitchTf: 1, - tags: []string{"c"}, + testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "2"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "baz"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModeStatic.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.group_label", "baz"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.lag_mode", apstra.RackLinkLagModeStatic.String()), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_transform_id", "1"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags"), + }...), + }, + { + genericSystem: genericSystem{ + // name: "foo", + // hostname: "foo.com", + // tags: []string{"a"}, + links: []link{ + { + // lagMode: apstra.RackLinkLagModeStatic, + // groupLabel: "baz", + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/8", + targetSwitchTf: 1, + // tags: []string{"c"}, + }, + }, }, + testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "1"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/8"), + resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), + resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags"), + }...), }, }, - testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "asn"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv4"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv6"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "deploy_mode", apstra.NodeDeployModeReady.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "2"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "bar"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModePassive.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.0", "c"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.group_label", "bar"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.lag_mode", apstra.RackLinkLagModePassive.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_name", "xe-0/0/7"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_transform_id", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.0", "c"), - }...), }, - { - genericSystem: genericSystem{ - // name: "foo", - // hostname: "foo.com", - // tags: []string{"a"}, - deployMode: apstra.NodeDeployModeDeploy.String(), - links: []link{ - { - lagMode: apstra.RackLinkLagModePassive, - groupLabel: "bar", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/6", - targetSwitchTf: 1, - tags: []string{"c"}, + "destroy_with_attached_ct": { + steps: []testStep{ + { + genericSystem: genericSystem{ + clearCtsOnDestroy: true, + links: []link{ + { + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/7", + targetSwitchTf: 1, + }, + }, }, - { - lagMode: apstra.RackLinkLagModePassive, - groupLabel: "bar", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/7", - targetSwitchTf: 1, - tags: []string{"c"}, + }, + { + preConfig: func() { + attachCtToPort("xe-0/0/7") + }, + genericSystem: genericSystem{ + clearCtsOnDestroy: true, + links: []link{ + { + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/7", + targetSwitchTf: 1, + }, + }, }, }, }, - testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "asn"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv4"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "loopback_ipv6"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "deploy_mode", apstra.NodeDeployModeDeploy.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "2"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "bar"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModePassive.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/6"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags.0", "c"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.group_label", "bar"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.lag_mode", apstra.RackLinkLagModePassive.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_name", "xe-0/0/7"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_transform_id", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.#", "1"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags.0", "c"), - }...), }, - { - genericSystem: genericSystem{ - // name: "foo", - // hostname: "foo.com", - // tags: []string{"a"}, - links: []link{ - { - lagMode: apstra.RackLinkLagModeStatic, - groupLabel: "baz", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/6", - targetSwitchTf: 1, - // tags: []string{"c"}, + "remove_link_with_attached_ct": { + steps: []testStep{ + { + preConfig: func() { + t.Log("foo") }, - { - lagMode: apstra.RackLinkLagModeStatic, - groupLabel: "baz", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/7", - targetSwitchTf: 1, - // tags: []string{"c"}, + genericSystem: genericSystem{ + clearCtsOnDestroy: true, + links: []link{ + { + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/8", + targetSwitchTf: 1, + }, + { + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/9", + targetSwitchTf: 1, + }, + }, }, }, - }, - testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "2"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label", "baz"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode", apstra.RackLinkLagModeStatic.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.group_label", "baz"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.lag_mode", apstra.RackLinkLagModeStatic.String()), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.1.target_switch_if_transform_id", "1"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.1.tags"), - }...), - }, - { - genericSystem: genericSystem{ - // name: "foo", - // hostname: "foo.com", - // tags: []string{"a"}, - links: []link{ - { - // lagMode: apstra.RackLinkLagModeStatic, - // groupLabel: "baz", - targetSwitchId: leafIds[0], - targetSwitchIf: "xe-0/0/8", - targetSwitchTf: 1, - // tags: []string{"c"}, + { + preConfig: func() { + attachCtToPort("xe-0/0/8") + }, + genericSystem: genericSystem{ + clearCtsOnDestroy: true, + links: []link{ + { + targetSwitchId: leafIds[0], + targetSwitchIf: "xe-0/0/9", + targetSwitchTf: 1, + }, + }, }, }, }, - testCheckFunc: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{ - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.#", "1"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.group_label"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.lag_mode"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_name", "xe-0/0/8"), - resource.TestCheckResourceAttr("apstra_datacenter_generic_system.test", "links.0.target_switch_if_transform_id", "1"), - resource.TestCheckNoResourceAttr("apstra_datacenter_generic_system.test", "links.0.tags"), - }...), }, } - steps := make([]resource.TestStep, len(testCases)) - for i, tc := range testCases { - tc.genericSystem.bpId = bpClient.Id().String() - steps[i] = resource.TestStep{ - Config: insecureProviderConfigHCL + renderGenericSystem(tc.genericSystem), - Check: tc.testCheckFunc, - } + for tName, tCase := range testCases { + tName, tCase := tName, tCase + t.Run(tName, func(t *testing.T) { + t.Parallel() + steps := make([]resource.TestStep, len(tCase.steps)) + for i, step := range tCase.steps { + step.genericSystem.bpId = bpClient.Id().String() + steps[i] = resource.TestStep{ + Config: insecureProviderConfigHCL + renderGenericSystem(step.genericSystem), + Check: step.testCheckFunc, + PreConfig: step.preConfig, + } + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: steps, + }) + }) } - - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: steps, - }) } diff --git a/apstra/resource_datacenter_virtual_network_test.go b/apstra/resource_datacenter_virtual_network_test.go index fc30e6a7..28cb00e6 100644 --- a/apstra/resource_datacenter_virtual_network_test.go +++ b/apstra/resource_datacenter_virtual_network_test.go @@ -48,7 +48,7 @@ func TestAccDatacenterVirtualNetwork_A(t *testing.T) { }() // security zone will evaporate when blueprint is deleted - szId, _, err := testutils.SecurityZoneA(ctx, bp) + szId := testutils.SecurityZoneA(t, ctx, bp) type node struct { Label string `json:"label"` diff --git a/apstra/test_utils/routing_zone.go b/apstra/test_utils/routing_zone.go index 7021ee3e..ee6e70ca 100644 --- a/apstra/test_utils/routing_zone.go +++ b/apstra/test_utils/routing_zone.go @@ -4,10 +4,12 @@ import ( "context" "github.com/Juniper/apstra-go-sdk/apstra" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "testing" ) -func SecurityZoneA(ctx context.Context, client *apstra.TwoStageL3ClosClient) (apstra.ObjectId, func(context.Context) error, error) { - deleteFunc := func(_ context.Context) error { return nil } +func SecurityZoneA(t testing.TB, ctx context.Context, client *apstra.TwoStageL3ClosClient) apstra.ObjectId { + t.Helper() + name := acctest.RandString(10) id, err := client.CreateSecurityZone(ctx, &apstra.SecurityZoneData{ Label: name, @@ -20,11 +22,15 @@ func SecurityZoneA(ctx context.Context, client *apstra.TwoStageL3ClosClient) (ap VniId: nil, }) if err != nil { - return "", deleteFunc, err - } - deleteFunc = func(ctx context.Context) error { - return client.DeleteSecurityZone(ctx, id) + t.Fatal(err) } - return id, deleteFunc, nil + t.Cleanup(func() { + err := client.DeleteSecurityZone(ctx, id) + if err != nil { + t.Fatal(err) + } + }) + + return id } diff --git a/docs/resources/datacenter_generic_system.md b/docs/resources/datacenter_generic_system.md index 4c53c4bc..30c666ce 100644 --- a/docs/resources/datacenter_generic_system.md +++ b/docs/resources/datacenter_generic_system.md @@ -84,6 +84,7 @@ resource "apstra_datacenter_generic_system" "example" { ### Optional - `asn` (Number) AS number of the Generic System. Note that in some circumstances Apstra may assign an ASN to the generic system even when none is supplied via this attribute. The automaticallyassigned value will be overwritten by Terraform during a subsequent apply operation. +- `clear_cts_on_destroy` (Boolean) When `true`, Link deletion in `destroy` phase and `apply` phase (where a Link has been removed from the configuration) will automatically clear Connectivity Template assignments from interfaces associated with those Links. - `deploy_mode` (String) Set the Apstra Deploy Mode for this Generic System. Default: `deploy` - `external` (Boolean) Set `true` to create an External Generic System - `hostname` (String) System hostname. diff --git a/go.mod b/go.mod index 05caee6a..455eedf4 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,12 @@ toolchain go1.21.1 require ( github.com/IBM/netaddr v1.5.0 - github.com/Juniper/apstra-go-sdk v0.0.0-20240216015330-52c20601d36e + github.com/Juniper/apstra-go-sdk v0.0.0-20240218003908-762bac644a2b github.com/chrismarget-j/go-licenses v0.0.0-20230424163011-d60082a506e0 github.com/google/go-cmp v0.6.0 github.com/goreleaser/goreleaser v1.23.0 github.com/hashicorp/go-version v1.6.0 + github.com/hashicorp/hcl/v2 v2.19.1 github.com/hashicorp/terraform-plugin-docs v0.13.0 github.com/hashicorp/terraform-plugin-framework v1.5.0 github.com/hashicorp/terraform-plugin-framework-jsontypes v0.1.0 @@ -180,7 +181,6 @@ require ( github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/hc-install v0.6.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.19.0 // indirect github.com/hashicorp/terraform-json v0.17.1 // indirect diff --git a/go.sum b/go.sum index 19553d53..b9bb6ff5 100644 --- a/go.sum +++ b/go.sum @@ -132,8 +132,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/IBM/netaddr v1.5.0 h1:IJlFZe1+nFs09TeMB/HOP4+xBnX2iM/xgiDOgZgTJq0= github.com/IBM/netaddr v1.5.0/go.mod h1:DDBPeYgbFzoXHjSz9Jwk7K8wmWV4+a/Kv0LqRnb8we4= -github.com/Juniper/apstra-go-sdk v0.0.0-20240216015330-52c20601d36e h1:ezqlcSN/BmR7xXhh4wGqOzmYUjDZB47N6OUdbTKUOAs= -github.com/Juniper/apstra-go-sdk v0.0.0-20240216015330-52c20601d36e/go.mod h1:Oi2i6iTT5B6U5Qn9ST6RyuSq9Ur3yfNToZ5GFvP6oAU= +github.com/Juniper/apstra-go-sdk v0.0.0-20240218003908-762bac644a2b h1:Nrdqc+FshM+t6/vzWjCudKtnIuzB1jk1+6dXgqXel9E= +github.com/Juniper/apstra-go-sdk v0.0.0-20240218003908-762bac644a2b/go.mod h1:Oi2i6iTT5B6U5Qn9ST6RyuSq9Ur3yfNToZ5GFvP6oAU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=