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

Add tests and supporting helper functions for Freeform Config Template #696

Merged
Show file tree
Hide file tree
Changes from all 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
29 changes: 10 additions & 19 deletions apstra/blueprint/freeform_config_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"regexp"
)

type FreeformConfigTemplate struct {
Id types.String `tfsdk:"id"`
BlueprintId types.String `tfsdk:"blueprint_id"`
Name types.String `tfsdk:"name"`
Text types.String `tfsdk:"text"`
TemplateId types.String `tfsdk:"template_id"`
Tags types.Set `tfsdk:"tags"`
}

Expand All @@ -45,11 +45,6 @@ func (o FreeformConfigTemplate) DataSourceAttributes() map[string]dataSourceSche
}...),
},
},
"template_id": dataSourceSchema.StringAttribute{
MarkdownDescription: "The Template ID of the configuration template in global catalog\n",
Computed: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"name": dataSourceSchema.StringAttribute{
MarkdownDescription: "Populate this field to look up an imported Config Template by `name`. Required when `id` is omitted.",
Optional: true,
Expand Down Expand Up @@ -81,17 +76,15 @@ func (o FreeformConfigTemplate) ResourceAttributes() map[string]resourceSchema.A
"id": resourceSchema.StringAttribute{
MarkdownDescription: "ID of the Config Template.",
Computed: true,
},
"template_id": resourceSchema.StringAttribute{
MarkdownDescription: "The template ID of the config template in the global catalog.",
Optional: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
},
"name": resourceSchema.StringAttribute{
MarkdownDescription: "Config Template name as shown in the Web UI.",
MarkdownDescription: "Config Template name as shown in the Web UI. Must end with `.jinja`.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
Validators: []validator.String{
stringvalidator.LengthAtLeast(7),
stringvalidator.RegexMatches(regexp.MustCompile(".jinja$"), "must end with '.jinja'"),
},
},
"text": resourceSchema.StringAttribute{
MarkdownDescription: "Configuration Jinja2 template text",
Expand All @@ -115,16 +108,14 @@ func (o *FreeformConfigTemplate) Request(ctx context.Context, diags *diag.Diagno
}

return &apstra.ConfigTemplateData{
Label: o.Name.ValueString(),
Text: o.Text.ValueString(),
Tags: tags,
TemplateId: apstra.ObjectId(o.TemplateId.ValueString()),
Label: o.Name.ValueString(),
Text: o.Text.ValueString(),
Tags: tags,
}
}

func (o *FreeformConfigTemplate) LoadApiData(ctx context.Context, in *apstra.ConfigTemplateData, diags *diag.Diagnostics) {
o.Name = types.StringValue(in.Label)
o.Text = types.StringValue(in.Text)
o.TemplateId = types.StringValue(string(in.TemplateId))
o.Tags = utils.SetValueOrNull(ctx, types.StringType, in.Tags, diags) // safe to ignore diagnostic here
}
2 changes: 2 additions & 0 deletions apstra/data_source_freeform_config_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"
)

var _ datasource.DataSourceWithConfigure = &dataSourceFreeformConfigTemplate{}
Expand Down Expand Up @@ -81,6 +82,7 @@ func (o *dataSourceFreeformConfigTemplate) Read(ctx context.Context, req datasou
return
}

config.Id = types.StringValue(api.Id.String())
config.LoadApiData(ctx, api.Data, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
Expand Down
1 change: 1 addition & 0 deletions apstra/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var (
ResourceAgentProfile = resourceAgentProfile{}
ResourceDatacenterGenericSystem = resourceDatacenterGenericSystem{}
ResourceDatacenterRoutingZone = resourceDatacenterRoutingZone{}
ResourceFreeformConfigTemplate = resourceFreeformConfigTemplate{}
ResourceIpv4Pool = resourceIpv4Pool{}
ResourceTemplatePodBased = resourceTemplatePodBased{}
ResourceTemplateCollapsed = resourceTemplateCollapsed{}
Expand Down
170 changes: 170 additions & 0 deletions apstra/resource_freeform_config_template_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//go:build integration

package tfapstra_test

import (
"context"
"fmt"
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"
"math/rand"
"strconv"
"testing"
)

const (
resourceFreeformConfigTemplateHcl = `
resource %q %q {
blueprint_id = %q
name = %q
text = %q
tags = %s
}
`
)

type resourceFreeformConfigTemplate struct {
blueprintId string
name string
text string
tags []string
}

func (o resourceFreeformConfigTemplate) render(rType, rName string) string {
return fmt.Sprintf(resourceFreeformConfigTemplateHcl,
rType, rName,
o.blueprintId,
o.name,
o.text,
stringSetOrNull(o.tags),
)
}

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

// required and computed attributes can always be checked
result.append(t, "TestCheckResourceAttrSet", "id")
result.append(t, "TestCheckResourceAttr", "blueprint_id", o.blueprintId)
result.append(t, "TestCheckResourceAttr", "name", o.name)
result.append(t, "TestCheckResourceAttr", "text", o.text)

if len(o.tags) > 0 {
result.append(t, "TestCheckResourceAttr", "tags.#", strconv.Itoa(len(o.tags)))
for _, tag := range o.tags {
result.append(t, "TestCheckTypeSetElemAttr", "tags.*", tag)
}
}

return result
}

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

// create a blueprint
bp := testutils.FfBlueprintA(t, ctx)

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

testCases := map[string]testCase{
"start_with_no_tags": {
steps: []testStep{
{
config: resourceFreeformConfigTemplate{
blueprintId: bp.Id().String(),
name: acctest.RandString(6) + ".jinja",
text: acctest.RandString(6),
},
},
{
config: resourceFreeformConfigTemplate{
blueprintId: bp.Id().String(),
name: acctest.RandString(6) + ".jinja",
text: acctest.RandString(6),
tags: randomStrings(rand.Intn(10)+2, 6),
},
},
{
config: resourceFreeformConfigTemplate{
blueprintId: bp.Id().String(),
name: acctest.RandString(6) + ".jinja",
text: acctest.RandString(6),
},
},
},
},
"start_with_tags": {
steps: []testStep{
{
config: resourceFreeformConfigTemplate{
blueprintId: bp.Id().String(),
name: acctest.RandString(6) + ".jinja",
text: acctest.RandString(6),
tags: randomStrings(rand.Intn(10)+2, 6),
},
},
{
config: resourceFreeformConfigTemplate{
blueprintId: bp.Id().String(),
name: acctest.RandString(6) + ".jinja",
text: acctest.RandString(6),
},
},
{
config: resourceFreeformConfigTemplate{
blueprintId: bp.Id().String(),
name: acctest.RandString(6) + ".jinja",
text: acctest.RandString(6),
tags: randomStrings(rand.Intn(10)+2, 6),
},
},
},
},
}

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

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,
})
})
}
}
8 changes: 8 additions & 0 deletions apstra/test_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,14 @@ func randomIPs(t testing.TB, n int, ipv4Cidr, ipv6Cidr string) []string {
return result
}

func randomStrings(strCount int, strLen int) []string {
result := make([]string, strCount)
for i := 0; i < strCount; i++ {
result[i] = acctest.RandString(strLen)
}
return result
}

type lineNumberer struct {
lines []string
base int
Expand Down
15 changes: 15 additions & 0 deletions apstra/test_utils/blueprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,18 @@ func BlueprintF(t testing.TB, ctx context.Context) *apstra.TwoStageL3ClosClient

return bpClient
}

func FfBlueprintA(t testing.TB, ctx context.Context) *apstra.FreeformClient {
t.Helper()

client := GetTestClient(t, ctx)

id, err := client.CreateFreeformBlueprint(ctx, acctest.RandString(6))
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, client.DeleteBlueprint(ctx, id)) })

bpClient, err := client.NewFreeformClient(ctx, id)
require.NoError(t, err)

return bpClient
}
7 changes: 4 additions & 3 deletions docs/data-sources/datacenter_configlet.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ At least one optional attribute is required.
## Example Usage

```terraform
# This example uses the `apstra_datacenter_configlets` data source to get a list
# of all imported configlets, and then uses the apstra_datacenter_configlet data source
# to inspect the results
data "apstra_freeform_config_template" "interfaces" {
blueprint_id = "043c5787-66e8-41c7-8925-c7e52fbe6e32"
name = "interfaces.jinja"

}

data "apstra_datacenter_blueprint" "b" {
name = "test"
Expand Down
17 changes: 4 additions & 13 deletions docs/data-sources/freeform_config_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,11 @@ At least one optional attribute is required.
## Example Usage

```terraform
resource "apstra_freeform_config_template" "test" {
blueprint_id = "freeform_blueprint-5ba09d07"
name = "test_conf_template.jinja"
tags = ["a", "b", "c"]
text = "this is a test for a config template."
# The following example retrieves a Config Template from a Freeform Blueprint
data "apstra_freeform_config_template" "interfaces" {
blueprint_id = "043c5787-66e8-41c7-8925-c7e52fbe6e32"
name = "interfaces.jinja"
}

data "apstra_freeform_config_template" "test" {
blueprint_id = "freeform_blueprint-5ba09d07"
id = apstra_freeform_config_template.test.id
}

output "test_out" {value = data.apstra_freeform_config_template.test}
```

<!-- schema generated by tfplugindocs -->
Expand All @@ -46,5 +38,4 @@ output "test_out" {value = data.apstra_freeform_config_template.test}

### Read-Only

- `template_id` (String) The Template ID of the configuration template in global catalog
- `text` (String) Configuration Jinja2 template text
Loading
Loading