Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

**Breaking Change** eliminate resource pool utilization attributes from resource pool resources #739

Merged
merged 5 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions apstra/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,27 @@ package tfapstra

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/resource"
)

var (
ResourceAgentProfile = resourceAgentProfile{}
ResourceAsnPool = resourceAsnPool{}
ResourceDatacenterGenericSystem = resourceDatacenterGenericSystem{}
ResourceDatacenterIpLinkAddressing = resourceDatacenterIpLinkAddressing{}
ResourceDatacenterRoutingZone = resourceDatacenterRoutingZone{}
ResourceIntegerPool = resourceIntegerPool{}
ResourceIpv4Pool = resourceIpv4Pool{}
ResourceIpv6Pool = resourceIpv6Pool{}
ResourceFreeformConfigTemplate = resourceFreeformConfigTemplate{}
ResourceFreeformLink = resourceFreeformLink{}
ResourceFreeformSystem = resourceFreeformSystem{}
ResourceFreeformPropertySet = resourceFreeformPropertySet{}
ResourceIpv4Pool = resourceIpv4Pool{}
ResourceTemplatePodBased = resourceTemplatePodBased{}
ResourceTemplateCollapsed = resourceTemplateCollapsed{}
ResourceDatacenterIpLinkAddressing = resourceDatacenterIpLinkAddressing{}
ResourceVniPool = resourceVniPool{}
)

func ResourceName(ctx context.Context, r resource.Resource) string {
Expand Down
8 changes: 4 additions & 4 deletions apstra/resource_agent_profile_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ package tfapstra_test
import (
"context"
"fmt"
"strconv"
"testing"

"github.com/Juniper/apstra-go-sdk/apstra"
tfapstra "github.com/Juniper/terraform-provider-apstra/apstra"
testutils "github.com/Juniper/terraform-provider-apstra/apstra/test_utils"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/stretchr/testify/require"
"strconv"
"testing"
)

const (
Expand Down Expand Up @@ -170,7 +171,6 @@ func TestResourceAgentProfile(t *testing.T) {
for tName, tCase := range testCases {
tName, tCase := tName, tCase
t.Run(tName, func(t *testing.T) {

steps := make([]resource.TestStep, len(tCase.steps))
for i, step := range tCase.steps {
config := step.config.render(resourceType, tName)
Expand All @@ -183,7 +183,7 @@ func TestResourceAgentProfile(t *testing.T) {

// add extractions
for _, ex := range step.extractions {
checks.extractFromState(t, ex.attribute, tNameToApId)
checks.extractFromState(t, resourceType+"."+tName, ex.attribute, tNameToApId)
}

chkLog := checks.string()
Expand Down
56 changes: 11 additions & 45 deletions apstra/resource_asn_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tfapstra
import (
"context"
"fmt"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/resources"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
Expand All @@ -12,9 +13,11 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
)

var _ resource.ResourceWithConfigure = &resourceAsnPool{}
var _ resource.ResourceWithValidateConfig = &resourceAsnPool{}
var _ resourceWithSetClient = &resourceAsnPool{}
var (
_ resource.ResourceWithConfigure = &resourceAsnPool{}
_ resource.ResourceWithValidateConfig = &resourceAsnPool{}
_ resourceWithSetClient = &resourceAsnPool{}
)

type resourceAsnPool struct {
client *apstra.Client
Expand Down Expand Up @@ -114,35 +117,14 @@ func (o *resourceAsnPool) Create(ctx context.Context, req resource.CreateRequest
return
}

// read pool back from Apstra to get usage statistics
var pool *apstra.AsnPool
for {
pool, err = o.client.GetAsnPool(ctx, id)
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("id"),
"ASN Pool not found",
fmt.Sprintf("Just-created ASN Pool with ID %q not found", id))
return
}
resp.Diagnostics.AddError("Error retrieving ASN Pool", err.Error())
return
}
if pool.Status != apstra.PoolStatusCreating {
break
}
}

// create state object
var state resources.AsnPool
state.LoadApiData(ctx, pool, &resp.Diagnostics)
plan.Id = types.StringValue(id.String())
plan.SetMutablesToNull(ctx, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}

// set state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}

func (o *resourceAsnPool) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
Expand Down Expand Up @@ -195,29 +177,13 @@ func (o *resourceAsnPool) Update(ctx context.Context, req resource.UpdateRequest
return
}

// read pool back from Apstra to get usage statistics
p, err := o.client.GetAsnPool(ctx, apstra.ObjectId(plan.Id.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("id"),
"ASN Pool not found",
fmt.Sprintf("Recently updated ASN Pool with ID %q not found", plan.Id.ValueString()))
return
}
resp.Diagnostics.AddError("Error retrieving ASN Pool", err.Error())
return
}

// create new state object
var state resources.AsnPool
state.LoadApiData(ctx, p, &resp.Diagnostics)
plan.SetMutablesToNull(ctx, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}

// set state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}

func (o *resourceAsnPool) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
Expand Down
224 changes: 224 additions & 0 deletions apstra/resource_asn_pool_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
//go:build integration

package tfapstra_test

import (
"context"
"fmt"
"math/rand"
"sort"
"strconv"
"strings"
"testing"

tfapstra "github.com/Juniper/terraform-provider-apstra/apstra"
testutils "github.com/Juniper/terraform-provider-apstra/apstra/test_utils"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

const (
dataAsnPoolByNameHCL = `
data %q "%s_by_name" {
name = %s
}
`
dataAsnPoolByIdHCL = `
data %q "%s_by_id" {
id = %s
}
`
resourceAsnPoolHCL = `
resource %q %q {
name = "%s"
ranges = [%s
]
}
`
resourceAsnPoolRangeHCL = "\n {first = %d, last = %d},"
)

type asnRange struct {
first int
last int
}

type resourceAsnPool struct {
name string
asnRanges []asnRange
}

func (o resourceAsnPool) render(rType, rName string) string {
asnRanges := make([]string, len(o.asnRanges))
for i, asnRange := range o.asnRanges {
asnRanges[i] = fmt.Sprintf(resourceAsnPoolRangeHCL, asnRange.first, asnRange.last)
}

return "" +
fmt.Sprintf(resourceAsnPoolHCL,
rType, rName,
o.name,
strings.Join(asnRanges, ""),
) +
fmt.Sprintf(dataAsnPoolByIdHCL, rType, rName, fmt.Sprintf("%s.%s.id", rType, rName)) +
fmt.Sprintf(dataAsnPoolByNameHCL, rType, rName, fmt.Sprintf("%s.%s.name", rType, rName))
}

func (o resourceAsnPool) testChecks(t testing.TB, rType, rName string) testChecks {
checks := newTestChecks(rType + "." + rName)

// required and computed attributes can always be checked
checks.append(t, "TestCheckResourceAttrSet", "id")
checks.append(t, "TestCheckResourceAttr", "name", o.name)
checks.append(t, "TestCheckNoResourceAttr", "total")
checks.append(t, "TestCheckNoResourceAttr", "status")
checks.append(t, "TestCheckNoResourceAttr", "used")
checks.append(t, "TestCheckNoResourceAttr", "used_percentage")

checks.append(t, "TestCheckResourceAttr", "ranges.#", strconv.Itoa(len(o.asnRanges)))

for _, r := range o.asnRanges {
checks.appendSetNestedCheck(t, "ranges.*", map[string]string{
"first": strconv.Itoa(r.first),
"last": strconv.Itoa(r.last),
})
}

checks.setPath("data." + rType + "." + rName + "_by_id")
var total int
for _, r := range o.asnRanges {
thisRangeTotal := 1 + r.last - r.first

checks.appendSetNestedCheck(t, "ranges.*", map[string]string{
"first": strconv.Itoa(r.first),
"last": strconv.Itoa(r.last),
"total": strconv.Itoa(thisRangeTotal),
"status": "pool_element_available",
"used": "0",
"used_percentage": "0",
})

total += thisRangeTotal
}

checks.append(t, "TestCheckResourceAttrSet", "id")
checks.append(t, "TestCheckResourceAttr", "name", o.name)
checks.append(t, "TestCheckResourceAttr", "total", strconv.Itoa(total))
checks.append(t, "TestCheckResourceAttr", "status", "not_in_use")
checks.append(t, "TestCheckResourceAttr", "used", "0")
checks.append(t, "TestCheckResourceAttr", "used_percentage", "0")

checks.setPath("data." + rType + "." + rName + "_by_name")
for _, r := range o.asnRanges {
thisRangeTotal := 1 + r.last - r.first

checks.appendSetNestedCheck(t, "ranges.*", map[string]string{
"first": strconv.Itoa(r.first),
"last": strconv.Itoa(r.last),
"total": strconv.Itoa(thisRangeTotal),
"status": "pool_element_available",
"used": "0",
"used_percentage": "0",
})
}

checks.append(t, "TestCheckResourceAttrSet", "id")
checks.append(t, "TestCheckResourceAttr", "name", o.name)
checks.append(t, "TestCheckResourceAttr", "total", strconv.Itoa(total))
checks.append(t, "TestCheckResourceAttr", "status", "not_in_use")
checks.append(t, "TestCheckResourceAttr", "used", "0")
checks.append(t, "TestCheckResourceAttr", "used_percentage", "0")

return checks
}

func TestAccResourceAsnPool(t *testing.T) {
ctx := context.Background()
client := testutils.GetTestClient(t, ctx)
apiVersion := version.Must(version.NewVersion(client.ApiVersion()))

randomRanges := func(t testing.TB, count int) []asnRange {
ints := randIntSet(t, 10000, 19999, count*2)
sort.Ints(ints)

result := make([]asnRange, count)
for i := range count {
result[i] = asnRange{
first: ints[(2 * i)],
last: ints[(2*i)+1],
}
}

return result
}

type testStep struct {
config resourceAsnPool
}

type testCase struct {
apiVersionConstraints version.Constraints
steps []testStep
}

testCases := map[string]testCase{
"simple_case": {
steps: []testStep{
{
config: resourceAsnPool{
name: acctest.RandString(6),
asnRanges: randomRanges(t, rand.Intn(5)+1),
},
},
{
config: resourceAsnPool{
name: acctest.RandString(6),
asnRanges: randomRanges(t, rand.Intn(5)+1),
},
},
{
config: resourceAsnPool{
name: acctest.RandString(6),
asnRanges: randomRanges(t, rand.Intn(5)+1),
},
},
},
},
}

resourceType := tfapstra.ResourceName(ctx, &tfapstra.ResourceAsnPool)

for tName, tCase := range testCases {
tName, tCase := tName, tCase
t.Run(tName, func(t *testing.T) {
t.Parallel()
if !tCase.apiVersionConstraints.Check(apiVersion) {
t.Skipf("test case %s requires Apstra %s", tName, tCase.apiVersionConstraints.String())
}

steps := make([]resource.TestStep, len(tCase.steps))
for i, step := range tCase.steps {
config := step.config.render(resourceType, tName)
checks := step.config.testChecks(t, resourceType, tName)

chkLog := checks.string()
stepName := fmt.Sprintf("test case %q step %d", tName, i+1)

t.Logf("\n// ------ begin config for %s ------\n%s// -------- end config for %s ------\n\n", stepName, config, stepName)
t.Logf("\n// ------ begin checks for %s ------\n%s// -------- end checks for %s ------\n\n", stepName, chkLog, stepName)

steps[i] = resource.TestStep{
Config: insecureProviderConfigHCL + config,
Check: resource.ComposeAggregateTestCheckFunc(checks.checks...),
}
}

resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: steps,
})
})
}
}
Loading
Loading