From ae8544ccf9a9337cec75521b1e47c01ae5b49030 Mon Sep 17 00:00:00 2001 From: bwjuniper Date: Tue, 27 Aug 2024 14:57:06 -0700 Subject: [PATCH 1/3] implement resource assignment --- apstra/data_source_freeform_resource.go | 12 ++++ apstra/freeform/resource.go | 13 +++++ apstra/resource_freeform_resource.go | 50 +++++++++++++++++ ...ource_freeform_resource_integraion_test.go | 56 ++++++++++++++++++- apstra/utils/slice_utils.go | 22 ++++++++ apstra/utils/slice_utils_test.go | 52 +++++++++++++++++ docs/data-sources/freeform_resource.md | 1 + docs/resources/freeform_resource.md | 1 + go.mod | 2 +- go.sum | 4 +- 10 files changed, 209 insertions(+), 4 deletions(-) diff --git a/apstra/data_source_freeform_resource.go b/apstra/data_source_freeform_resource.go index 3c8b19d3..6d5fa696 100644 --- a/apstra/data_source_freeform_resource.go +++ b/apstra/data_source_freeform_resource.go @@ -92,6 +92,18 @@ func (o *dataSourceFreeformResource) Read(ctx context.Context, req datasource.Re return } + // Read the resource assignments + assignedTo, err := bp.ListResourceAssignments(ctx, api.Id) + if err != nil { + resp.Diagnostics.AddError("error reading Resource Assignments", err.Error()) + return + } + + config.AssignedTo = utils.SetValueOrNull(ctx, types.StringType, assignedTo, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + // Set state resp.Diagnostics.Append(resp.State.Set(ctx, &config)...) } diff --git a/apstra/freeform/resource.go b/apstra/freeform/resource.go index a57ba680..b1874bda 100644 --- a/apstra/freeform/resource.go +++ b/apstra/freeform/resource.go @@ -3,6 +3,7 @@ package freeform import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "regexp" "strconv" "strings" @@ -33,6 +34,7 @@ type Resource struct { Ipv4Value cidrtypes.IPv4Prefix `tfsdk:"ipv4_value"` Ipv6Value cidrtypes.IPv6Prefix `tfsdk:"ipv6_value"` GeneratorId types.String `tfsdk:"generator_id"` + AssignedTo types.Set `tfsdk:"assigned_to"` } func (o Resource) DataSourceAttributes() map[string]dataSourceSchema.Attribute { @@ -108,6 +110,11 @@ func (o Resource) DataSourceAttributes() map[string]dataSourceSchema.Attribute { MarkdownDescription: "ID of the group generator that created the group, if any.", Computed: true, }, + "assigned_to": dataSourceSchema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + MarkdownDescription: "Set of node IDs to which the resource is assigned", + }, } } @@ -202,6 +209,12 @@ func (o Resource) ResourceAttributes() map[string]resourceSchema.Attribute { "Always `null` because groups created via resource declaration were not generated.", Computed: true, }, + "assigned_to": resourceSchema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "Set of node IDs to which the resource is assigned", + Validators: []validator.Set{setvalidator.SizeAtLeast(1)}, + }, } } diff --git a/apstra/resource_freeform_resource.go b/apstra/resource_freeform_resource.go index 95f2babc..7820e63b 100644 --- a/apstra/resource_freeform_resource.go +++ b/apstra/resource_freeform_resource.go @@ -168,6 +168,21 @@ func (o *resourceFreeformResource) Create(ctx context.Context, req resource.Crea return } + // set the resource assignments, if any + if !plan.AssignedTo.IsNull() { + var assignments []apstra.ObjectId + resp.Diagnostics.Append(plan.AssignedTo.ElementsAs(ctx, &assignments, false)...) + if resp.Diagnostics.HasError() { + return + } + + err = bp.UpdateResourceAssignments(ctx, apstra.ObjectId(plan.Id.ValueString()), assignments) + if err != nil { + resp.Diagnostics.AddError("error updating Resource Assignments", err.Error()) + return + } + } + // Read the resource back from Apstra to get computed values api, err := bp.GetRaResource(ctx, id) if err != nil { @@ -219,6 +234,18 @@ func (o *resourceFreeformResource) Read(ctx context.Context, req resource.ReadRe return } + // Read the resource assignments + assignedTo, err := bp.ListResourceAssignments(ctx, api.Id) + if err != nil { + resp.Diagnostics.AddError("error reading Resource Assignments", err.Error()) + return + } + + state.AssignedTo = utils.SetValueOrNull(ctx, types.StringType, assignedTo, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + // Set state resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } @@ -231,6 +258,13 @@ func (o *resourceFreeformResource) Update(ctx context.Context, req resource.Upda return } + // Get state values + var state freeform.Resource + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + // get a client for the Freeform reference design bp, err := o.getBpClientFunc(ctx, plan.BlueprintId.ValueString()) if err != nil { @@ -264,6 +298,22 @@ func (o *resourceFreeformResource) Update(ctx context.Context, req resource.Upda return } + var planAssignments, stateAssignments []apstra.ObjectId + resp.Diagnostics.Append(plan.AssignedTo.ElementsAs(ctx, &planAssignments, false)...) + resp.Diagnostics.Append(state.AssignedTo.ElementsAs(ctx, &stateAssignments, false)...) + if resp.Diagnostics.HasError() { + return + } + + // update the assignments if necessary + if !utils.SlicesAreEqualSets(planAssignments, stateAssignments) { + err = bp.UpdateResourceAssignments(ctx, apstra.ObjectId(plan.Id.ValueString()), planAssignments) + if err != nil { + resp.Diagnostics.AddError("error updating Resource Assignments", err.Error()) + return + } + } + // Read the resource back from Apstra to get computed values api, err := bp.GetRaResource(ctx, apstra.ObjectId(plan.Id.ValueString())) if err != nil { diff --git a/apstra/resource_freeform_resource_integraion_test.go b/apstra/resource_freeform_resource_integraion_test.go index f614a9c7..7567f46a 100644 --- a/apstra/resource_freeform_resource_integraion_test.go +++ b/apstra/resource_freeform_resource_integraion_test.go @@ -31,6 +31,7 @@ resource %q %q { ipv4_value = %s ipv6_value = %s allocated_from = %s + assigned_to = %s } ` ) @@ -44,6 +45,7 @@ type resourceFreeformResource struct { ipv4Value net.IPNet ipv6Value net.IPNet allocatedFrom apstra.ObjectId + assignedTo []string } func (o resourceFreeformResource) render(rType, rName string) string { @@ -57,6 +59,7 @@ func (o resourceFreeformResource) render(rType, rName string) string { ipOrNull(o.ipv4Value), ipOrNull(o.ipv6Value), stringOrNull(o.allocatedFrom.String()), + stringSetOrNull(o.assignedTo), ) } @@ -99,6 +102,12 @@ func (o resourceFreeformResource) testChecks(t testing.TB, rType, rName string) } else { result.append(t, "TestCheckResourceAttr", "allocated_from", o.allocatedFrom.String()) } + if len(o.assignedTo) > 0 { + result.append(t, "TestCheckResourceAttr", "assigned_to.#", strconv.Itoa(len(o.assignedTo))) + for _, assignedTo := range o.assignedTo { + result.append(t, "TestCheckTypeSetElemAttr", "assigned_to.*", assignedTo) + } + } return result } @@ -109,7 +118,15 @@ func TestResourceFreeformResource(t *testing.T) { apiVersion := version.Must(version.NewVersion(client.ApiVersion())) // create a blueprint and a group... - bp, groupId := testutils.FfBlueprintC(t, ctx) + intSysCount := 5 + extSysCount := 5 + sysCount := intSysCount + extSysCount + bp, intSysIds, extSysIds := testutils.FfBlueprintB(t, ctx, intSysCount, extSysCount) + require.Equal(t, intSysCount, len(intSysIds)) + require.Equal(t, extSysCount, len(extSysIds)) + + groupId, err := bp.CreateRaGroup(ctx, &apstra.FreeformRaGroupData{Label: acctest.RandString(6)}) + require.NoError(t, err) newIpv4AllocationGroup := func(t testing.TB) apstra.ObjectId { t.Helper() @@ -237,6 +254,37 @@ func TestResourceFreeformResource(t *testing.T) { return allocGroup } + var allSysIDs []string + randomSysIds := func(t testing.TB, count int) []string { + if count > intSysCount+extSysCount { + t.Fatalf("caller requested %d systemIDs blueprint only has %d internal and %d external", count, intSysCount, extSysCount) + } + + if allSysIDs == nil { + allSysIDs = make([]string, intSysCount+extSysCount) + for i, id := range intSysIds { + allSysIDs[i] = id.String() + } + for i, id := range extSysIds { + allSysIDs[i+intSysCount] = id.String() + } + } + + resultMap := make(map[string]struct{}, count) + for len(resultMap) < count { + resultMap[allSysIDs[rand.IntN(len(allSysIDs))]] = struct{}{} + } + + result := make([]string, len(resultMap)) + var i int + for k := range resultMap { + result[i] = k + i++ + } + + return result + } + type testStep struct { config resourceFreeformResource } @@ -283,6 +331,7 @@ func TestResourceFreeformResource(t *testing.T) { groupId: groupId, resourceType: apstra.FFResourceTypeAsn, allocatedFrom: newAsnAllocationGroup(t), + assignedTo: randomSysIds(t, sysCount/3), }, }, { @@ -305,6 +354,7 @@ func TestResourceFreeformResource(t *testing.T) { groupId: groupId, resourceType: apstra.FFResourceTypeVni, integerValue: utils.ToPtr(4498), + assignedTo: randomSysIds(t, sysCount/3), }, }, }, @@ -318,6 +368,7 @@ func TestResourceFreeformResource(t *testing.T) { groupId: groupId, resourceType: apstra.FFResourceTypeInt, integerValue: utils.ToPtr(4498), + assignedTo: randomSysIds(t, sysCount/3), }, }, { @@ -327,6 +378,7 @@ func TestResourceFreeformResource(t *testing.T) { groupId: groupId, resourceType: apstra.FFResourceTypeInt, allocatedFrom: newIntAllocationGroup(t), + assignedTo: randomSysIds(t, sysCount/3), }, }, { @@ -381,6 +433,7 @@ func TestResourceFreeformResource(t *testing.T) { groupId: groupId, resourceType: apstra.FFResourceTypeIpv6, ipv6Value: randomSlash127(t, "2001:db8::/32"), + assignedTo: randomSysIds(t, sysCount/3), }, }, { @@ -440,6 +493,7 @@ func TestResourceFreeformResource(t *testing.T) { groupId: groupId, resourceType: apstra.FFResourceTypeVni, allocatedFrom: newVniAllocationGroup(t), + assignedTo: randomSysIds(t, sysCount/3), }, }, }, diff --git a/apstra/utils/slice_utils.go b/apstra/utils/slice_utils.go index 21a061c6..574f96df 100644 --- a/apstra/utils/slice_utils.go +++ b/apstra/utils/slice_utils.go @@ -153,3 +153,25 @@ func Sort[A constraints.Ordered](in []A) { return in[i] < in[j] }) } + +func SlicesAreEqualSets[A comparable](a, b []A) bool { + // first check to see if they are the same length + if len(a) != len(b) { + return false + } + + // make a map of booleans and fill it with true(s) + ma := make(map[A]bool, len(a)) + for _, v := range a { + ma[v] = true + } + + for _, v := range b { + if !ma[v] { + // element from []b not found return false + return false + } + } + + return true +} diff --git a/apstra/utils/slice_utils_test.go b/apstra/utils/slice_utils_test.go index bf7241cb..ea9898eb 100644 --- a/apstra/utils/slice_utils_test.go +++ b/apstra/utils/slice_utils_test.go @@ -3,6 +3,7 @@ package utils import ( "fmt" "github.com/hashicorp/terraform-plugin-framework/types" + "strconv" "testing" ) @@ -412,3 +413,54 @@ func TestSliceIntersectionOfAB(t *testing.T) { } } } + +func TestSlicesAreEqualSets(t *testing.T) { + type testCase[T comparable] struct { + a []T + b []T + e bool + } + + testCases := []testCase[int]{ + { + a: []int{}, + b: []int{}, + e: true, + }, + { + a: []int{1, 2, 3, 4}, + b: []int{1, 2, 3, 4}, + e: true, + }, + { + a: []int{1, 2, 3, 4}, + b: []int{1, 2, 3, 4, 5}, + e: false, + }, + { + a: []int{1, 2, 3, 4, 5}, + b: []int{1, 2, 3, 4}, + e: false, + }, + { + a: []int{1, 2, 3, 4}, + b: []int{1, 2, 3, 5}, + e: false, + }, + { + a: []int{2, 4, 6, 8}, + b: []int{1, 3, 5, 7}, + e: false, + }, + } + + for i, tc := range testCases { + t.Run(strconv.Itoa(i), func(t *testing.T) { + t.Parallel() + r := SlicesAreEqualSets(tc.a, tc.b) + if r != tc.e { + t.Fatalf("test case %d: expected %t, got %t", i, tc.e, r) + } + }) + } +} diff --git a/docs/data-sources/freeform_resource.md b/docs/data-sources/freeform_resource.md index f289f19a..8ff521cf 100644 --- a/docs/data-sources/freeform_resource.md +++ b/docs/data-sources/freeform_resource.md @@ -66,6 +66,7 @@ output "test_resource_out" { value = data.apstra_freeform_resource.test } ### Read-Only - `allocated_from` (String) ID of the node from which this resource has been sourced. This could be an ID of resource allocation group or another resource (in case of IP or Host IP allocations). This also can be empty. In that case it is required that value for this resource is provided by thex user. +- `assigned_to` (Set of String) Set of node IDs to which the resource is assigned - `generator_id` (String) ID of the group generator that created the group, if any. - `group_id` (String) Resource Group the Resource belongs to - `integer_value` (Number) Value used by integer type resources (`asn`, `integer`, `vlan`, `vni`). Also used by IP prefix resources (`ipv4` and `ipv6`) to indicate the required prefix size for automatic allocations from another object or a resource pool. diff --git a/docs/resources/freeform_resource.md b/docs/resources/freeform_resource.md index 85718799..8cd58e85 100644 --- a/docs/resources/freeform_resource.md +++ b/docs/resources/freeform_resource.md @@ -60,6 +60,7 @@ output "test_resource_out" { value = data.apstra_freeform_resource.test } ### Optional - `allocated_from` (String) ID of the node to be used as a source for this resource. This could be an ID of resource allocation group or another resource (in case of IP or Host IP allocations). This also can be empty. In that case it is required that value for this resource is provided by the user. +- `assigned_to` (Set of String) Set of node IDs to which the resource is assigned - `integer_value` (Number) Value used by integer type resources (`asn`, `integer`, `vlan`, `vni`). Also used by IP prefix resources (`ipv4` and `ipv6`) to indicate the required prefix size for automatic allocations from another object or a resource pool. - `ipv4_value` (String) Value used by resources with type `ipv4` or `host_ipv4`. Must be CIDR notation. - `ipv6_value` (String) Value used by resources with type `ipv6` or `host_ipv6`. Must be CIDR notation. diff --git a/go.mod b/go.mod index d73ad39e..69d6ca30 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ go 1.22.5 require ( github.com/IBM/netaddr v1.5.0 - github.com/Juniper/apstra-go-sdk v0.0.0-20240820185158-04105b6c891e + github.com/Juniper/apstra-go-sdk v0.0.0-20240827214729-1f8d4ab953cf github.com/chrismarget-j/go-licenses v0.0.0-20240224210557-f22f3e06d3d4 github.com/google/go-cmp v0.6.0 github.com/hashicorp/go-version v1.7.0 diff --git a/go.sum b/go.sum index d106531b..66aa716c 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0 github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= 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-20240820185158-04105b6c891e h1:vL/vwdNcbCUqmILhSqtcsemU6ekofAwCWCDWje2Qb/k= -github.com/Juniper/apstra-go-sdk v0.0.0-20240820185158-04105b6c891e/go.mod h1:cSUzaIIQzZysIVKgJnt2/jO2EKeAB60Xgbx8yBGwJ8Y= +github.com/Juniper/apstra-go-sdk v0.0.0-20240827214729-1f8d4ab953cf h1:bAOJtOEAk0JSDCpTBwWMJg+2a3A2YoflVWUPi7gKonQ= +github.com/Juniper/apstra-go-sdk v0.0.0-20240827214729-1f8d4ab953cf/go.mod h1:cSUzaIIQzZysIVKgJnt2/jO2EKeAB60Xgbx8yBGwJ8Y= github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0= github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= From a1a39ec7cd11a7f6eb0a2f7ffc9e7cf54e5604a6 Mon Sep 17 00:00:00 2001 From: Chris Marget Date: Wed, 28 Aug 2024 15:13:24 -0400 Subject: [PATCH 2/3] resource assignments review --- apstra/freeform/resource.go | 27 ++++++++-- apstra/resource_freeform_resource.go | 33 +++++++----- ...ource_freeform_resource_integraion_test.go | 53 +++++++++++-------- 3 files changed, 76 insertions(+), 37 deletions(-) diff --git a/apstra/freeform/resource.go b/apstra/freeform/resource.go index b1874bda..8f75c977 100644 --- a/apstra/freeform/resource.go +++ b/apstra/freeform/resource.go @@ -3,7 +3,6 @@ package freeform import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "regexp" "strconv" "strings" @@ -12,6 +11,7 @@ import ( apstravalidator "github.com/Juniper/terraform-provider-apstra/apstra/apstra_validator" "github.com/Juniper/terraform-provider-apstra/apstra/utils" "github.com/hashicorp/terraform-plugin-framework-nettypes/cidrtypes" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" dataSourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -64,7 +64,7 @@ func (o Resource) DataSourceAttributes() map[string]dataSourceSchema.Attribute { Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, }, "group_id": dataSourceSchema.StringAttribute{ - MarkdownDescription: "Resource Group the Resource belongs to", + MarkdownDescription: "Resource Group the Resource belongs to.", Computed: true, }, "type": dataSourceSchema.StringAttribute{ @@ -113,7 +113,7 @@ func (o Resource) DataSourceAttributes() map[string]dataSourceSchema.Attribute { "assigned_to": dataSourceSchema.SetAttribute{ ElementType: types.StringType, Computed: true, - MarkdownDescription: "Set of node IDs to which the resource is assigned", + MarkdownDescription: "Set of node IDs to which the resource is assigned.", }, } } @@ -293,3 +293,24 @@ func (o *Resource) LoadApiData(_ context.Context, in *apstra.FreeformRaResourceD } } } + +func (o Resource) NeedsUpdate(state Resource) bool { + switch { + case !o.Type.Equal(state.Type): + return true + case !o.Name.Equal(state.Name): + return true + case !o.AllocatedFrom.Equal(state.AllocatedFrom): + return true + case !o.GroupId.Equal(state.GroupId): + return true + case !o.IntValue.Equal(state.IntValue): + return true + case !o.Ipv4Value.Equal(state.Ipv4Value): + return true + case !o.Ipv6Value.Equal(state.Ipv6Value): + return true + } + + return false +} diff --git a/apstra/resource_freeform_resource.go b/apstra/resource_freeform_resource.go index 7820e63b..4f87da9c 100644 --- a/apstra/resource_freeform_resource.go +++ b/apstra/resource_freeform_resource.go @@ -168,6 +168,13 @@ func (o *resourceFreeformResource) Create(ctx context.Context, req resource.Crea return } + // record the id and provisionally set the state + plan.Id = types.StringValue(id.String()) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + // set the resource assignments, if any if !plan.AssignedTo.IsNull() { var assignments []apstra.ObjectId @@ -176,7 +183,7 @@ func (o *resourceFreeformResource) Create(ctx context.Context, req resource.Crea return } - err = bp.UpdateResourceAssignments(ctx, apstra.ObjectId(plan.Id.ValueString()), assignments) + err = bp.UpdateResourceAssignments(ctx, id, assignments) if err != nil { resp.Diagnostics.AddError("error updating Resource Assignments", err.Error()) return @@ -191,7 +198,6 @@ func (o *resourceFreeformResource) Create(ctx context.Context, req resource.Crea } // load state objects - plan.Id = types.StringValue(id.String()) plan.LoadApiData(ctx, api.Data, &resp.Diagnostics) if resp.Diagnostics.HasError() { return @@ -285,17 +291,20 @@ func (o *resourceFreeformResource) Update(ctx context.Context, req resource.Upda return } - // Convert the plan into an API Request - request := plan.Request(ctx, &resp.Diagnostics) - if resp.Diagnostics.HasError() { - return - } + // Update the resource if necessary + if plan.NeedsUpdate(state) { + // Convert the plan into an API Request + request := plan.Request(ctx, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } - // Update the Resource - err = bp.UpdateRaResource(ctx, apstra.ObjectId(plan.Id.ValueString()), request) - if err != nil { - resp.Diagnostics.AddError("error updating Freeform Resource", err.Error()) - return + // Update the Resource + err = bp.UpdateRaResource(ctx, apstra.ObjectId(plan.Id.ValueString()), request) + if err != nil { + resp.Diagnostics.AddError("error updating Freeform Resource", err.Error()) + return + } } var planAssignments, stateAssignments []apstra.ObjectId diff --git a/apstra/resource_freeform_resource_integraion_test.go b/apstra/resource_freeform_resource_integraion_test.go index 7567f46a..c59fc74b 100644 --- a/apstra/resource_freeform_resource_integraion_test.go +++ b/apstra/resource_freeform_resource_integraion_test.go @@ -31,7 +31,7 @@ resource %q %q { ipv4_value = %s ipv6_value = %s allocated_from = %s - assigned_to = %s + assigned_to = %s } ` ) @@ -107,6 +107,8 @@ func (o resourceFreeformResource) testChecks(t testing.TB, rType, rName string) for _, assignedTo := range o.assignedTo { result.append(t, "TestCheckTypeSetElemAttr", "assigned_to.*", assignedTo) } + } else { + result.append(t, "TestCheckNoResourceAttr", "assigned_to") } return result @@ -117,6 +119,8 @@ func TestResourceFreeformResource(t *testing.T) { client := testutils.GetTestClient(t, ctx) apiVersion := version.Must(version.NewVersion(client.ApiVersion())) + var err error + // create a blueprint and a group... intSysCount := 5 extSysCount := 5 @@ -125,8 +129,13 @@ func TestResourceFreeformResource(t *testing.T) { require.Equal(t, intSysCount, len(intSysIds)) require.Equal(t, extSysCount, len(extSysIds)) - groupId, err := bp.CreateRaGroup(ctx, &apstra.FreeformRaGroupData{Label: acctest.RandString(6)}) - require.NoError(t, err) + // create resource groups + groupCount := 2 + groupIds := make([]apstra.ObjectId, groupCount) + for i := range groupCount { + groupIds[i], err = bp.CreateRaGroup(ctx, &apstra.FreeformRaGroupData{Label: acctest.RandString(6)}) + require.NoError(t, err) + } newIpv4AllocationGroup := func(t testing.TB) apstra.ObjectId { t.Helper() @@ -301,7 +310,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeAsn, integerValue: utils.ToPtr(65535), }, @@ -310,7 +319,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[1], resourceType: apstra.FFResourceTypeAsn, integerValue: utils.ToPtr(65536), }, @@ -319,7 +328,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeAsn, allocatedFrom: newAsnAllocationGroup(t), }, @@ -328,7 +337,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[1], resourceType: apstra.FFResourceTypeAsn, allocatedFrom: newAsnAllocationGroup(t), assignedTo: randomSysIds(t, sysCount/3), @@ -338,7 +347,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeAsn, integerValue: utils.ToPtr(65536), }, @@ -351,7 +360,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeVni, integerValue: utils.ToPtr(4498), assignedTo: randomSysIds(t, sysCount/3), @@ -365,7 +374,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeInt, integerValue: utils.ToPtr(4498), assignedTo: randomSysIds(t, sysCount/3), @@ -375,7 +384,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[1], resourceType: apstra.FFResourceTypeInt, allocatedFrom: newIntAllocationGroup(t), assignedTo: randomSysIds(t, sysCount/3), @@ -385,7 +394,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeInt, integerValue: utils.ToPtr(459), }, @@ -398,7 +407,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeIpv4, ipv4Value: randomSlash31(t, "192.168.2.0/24"), }, @@ -407,7 +416,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[1], resourceType: apstra.FFResourceTypeIpv4, integerValue: utils.ToPtr(30), allocatedFrom: newIpv4AllocationGroup(t), @@ -417,7 +426,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeIpv4, ipv4Value: randomSlash31(t, "10.168.2.0/24"), }, @@ -430,7 +439,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeIpv6, ipv6Value: randomSlash127(t, "2001:db8::/32"), assignedTo: randomSysIds(t, sysCount/3), @@ -440,7 +449,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[1], resourceType: apstra.FFResourceTypeIpv6, integerValue: utils.ToPtr(64), allocatedFrom: newIpv6AllocationGroup(t), @@ -450,7 +459,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeIpv6, ipv6Value: randomSlash127(t, "2001:db8::/32"), }, @@ -463,7 +472,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeVni, allocatedFrom: newVniAllocationGroup(t), }, @@ -472,7 +481,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[1], resourceType: apstra.FFResourceTypeVni, integerValue: utils.ToPtr(rand.IntN(10000) + 4096), }, @@ -481,7 +490,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[0], resourceType: apstra.FFResourceTypeVni, integerValue: utils.ToPtr(rand.IntN(10000) + 4096), }, @@ -490,7 +499,7 @@ func TestResourceFreeformResource(t *testing.T) { config: resourceFreeformResource{ blueprintId: bp.Id().String(), name: acctest.RandString(6), - groupId: groupId, + groupId: groupIds[1], resourceType: apstra.FFResourceTypeVni, allocatedFrom: newVniAllocationGroup(t), assignedTo: randomSysIds(t, sysCount/3), From 337eed8b02373f4a22c5a00c235221a0b180cfbe Mon Sep 17 00:00:00 2001 From: Chris Marget Date: Wed, 28 Aug 2024 15:15:54 -0400 Subject: [PATCH 3/3] make docs --- docs/data-sources/freeform_resource.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/data-sources/freeform_resource.md b/docs/data-sources/freeform_resource.md index 8ff521cf..a90bf4e9 100644 --- a/docs/data-sources/freeform_resource.md +++ b/docs/data-sources/freeform_resource.md @@ -66,9 +66,9 @@ output "test_resource_out" { value = data.apstra_freeform_resource.test } ### Read-Only - `allocated_from` (String) ID of the node from which this resource has been sourced. This could be an ID of resource allocation group or another resource (in case of IP or Host IP allocations). This also can be empty. In that case it is required that value for this resource is provided by thex user. -- `assigned_to` (Set of String) Set of node IDs to which the resource is assigned +- `assigned_to` (Set of String) Set of node IDs to which the resource is assigned. - `generator_id` (String) ID of the group generator that created the group, if any. -- `group_id` (String) Resource Group the Resource belongs to +- `group_id` (String) Resource Group the Resource belongs to. - `integer_value` (Number) Value used by integer type resources (`asn`, `integer`, `vlan`, `vni`). Also used by IP prefix resources (`ipv4` and `ipv6`) to indicate the required prefix size for automatic allocations from another object or a resource pool. - `ipv4_value` (String) Value used by resources with type `ipv4` or `host_ipv4`. - `ipv6_value` (String) Value used by resources with type `ipv6` or `host_ipv6`.