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

fix tests and examples #699

Merged
merged 3 commits into from
Jul 8, 2024
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
22 changes: 14 additions & 8 deletions apstra/blueprint/freeform_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package blueprint
import (
"context"
"fmt"
"regexp"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
Expand Down Expand Up @@ -85,6 +86,7 @@ func (o FreeformSystem) DataSourceAttributes() map[string]dataSourceSchema.Attri
}

func (o FreeformSystem) ResourceAttributes() map[string]resourceSchema.Attribute {
hostnameRegexp := "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"
return map[string]resourceSchema.Attribute{
"blueprint_id": resourceSchema.StringAttribute{
MarkdownDescription: "Apstra Blueprint ID.",
Expand All @@ -100,29 +102,33 @@ func (o FreeformSystem) ResourceAttributes() map[string]resourceSchema.Attribute
"name": resourceSchema.StringAttribute{
MarkdownDescription: "Freeform System name as shown in the Web UI.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
Validators: []validator.String{
stringvalidator.RegexMatches(regexp.MustCompile("^[a-zA-Z0-9.-_]+$"), "name may consist only of the following characters : a-zA-Z0-9.-_")},
},
"hostname": resourceSchema.StringAttribute{
MarkdownDescription: "Hostname of the Freeform System.",
Optional: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
Required: true,
Validators: []validator.String{
stringvalidator.RegexMatches(regexp.MustCompile(hostnameRegexp), "must match regex "+hostnameRegexp),
},
},
"deploy_mode": dataSourceSchema.StringAttribute{
"deploy_mode": resourceSchema.StringAttribute{
MarkdownDescription: "Deploy mode of the System",
Optional: true,
Validators: []validator.String{stringvalidator.OneOf(utils.AllNodeDeployModes()...)},
},
"type": dataSourceSchema.StringAttribute{
"type": resourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("Type of the System. Must be one of `%s` or `%s`", apstra.SystemTypeInternal, apstra.SystemTypeExternal),
Required: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
Validators: []validator.String{stringvalidator.OneOf(apstra.SystemTypeInternal.String(), apstra.SystemTypeExternal.String())},
},
"device_profile_id": dataSourceSchema.StringAttribute{
"device_profile_id": resourceSchema.StringAttribute{
MarkdownDescription: "Device profile ID of the System",
Optional: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"system_id": dataSourceSchema.StringAttribute{
"system_id": resourceSchema.StringAttribute{
MarkdownDescription: "ID (usually serial number) of the Managed Device to associate with this System",
Optional: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
Expand Down Expand Up @@ -166,7 +172,7 @@ func (o *FreeformSystem) Request(ctx context.Context, diags *diag.Diagnostics) *
func (o *FreeformSystem) LoadApiData(ctx context.Context, in *apstra.FreeformSystemData, diags *diag.Diagnostics) {
o.Name = types.StringValue(in.Label)
o.Hostname = types.StringValue(in.Hostname)
o.Type = types.StringValue(string(rune(in.Type)))
o.Type = types.StringValue(in.Type.String())
o.DeviceProfileId = types.StringValue(string(in.DeviceProfileId))
o.SystemId = types.StringPointerValue((*string)(in.SystemId))
o.Tags = utils.SetValueOrNull(ctx, types.StringType, in.Tags, diags) // safe to ignore diagnostic here
Expand Down
1 change: 1 addition & 0 deletions apstra/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var (
ResourceDatacenterGenericSystem = resourceDatacenterGenericSystem{}
ResourceDatacenterRoutingZone = resourceDatacenterRoutingZone{}
ResourceFreeformConfigTemplate = resourceFreeformConfigTemplate{}
ResourceFreeformSystem = resourceFreeformSystem{}
ResourceFreeformPropertySet = resourceFreeformPropertySet{}
ResourceIpv4Pool = resourceIpv4Pool{}
ResourceTemplatePodBased = resourceTemplatePodBased{}
Expand Down
1 change: 0 additions & 1 deletion apstra/resource_freeform_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package tfapstra
import (
"context"
"fmt"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/blueprint"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
Expand Down
204 changes: 204 additions & 0 deletions apstra/resource_freeform_system_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
//go:build integration

package tfapstra_test

import (
"context"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
"math/rand"
"strconv"
"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 (
resourceFreeformSystemHcl = `
resource %q %q {
blueprint_id = %q
name = %q
device_profile_id = %q
hostname = %q
type = %q
deploy_mode = %s
tags = %s
}
`
)

type resourceFreeformSystem struct {
blueprintId string
name string
deviceProfileId string
hostname string
systemType string
deployMode string
tags []string
}

func (o resourceFreeformSystem) render(rType, rName string) string {
return fmt.Sprintf(resourceFreeformSystemHcl,
rType, rName,
o.blueprintId,
o.name,
o.deviceProfileId,
o.hostname,
o.systemType,
stringOrNull(o.deployMode),
stringSetOrNull(o.tags),
)
}

func (o resourceFreeformSystem) 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", "type", o.systemType)
result.append(t, "TestCheckResourceAttr", "device_profile_id", o.deviceProfileId)
result.append(t, "TestCheckResourceAttr", "hostname", o.hostname)
if o.deployMode != "" {
result.append(t, "TestCheckResourceAttr", "deploy_mode", o.deployMode)
}
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 TestResourceFreeformSystem(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)
// get a device profile
dpId, _ := bp.ImportDeviceProfile(ctx, "Juniper_vEX")

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

testCases := map[string]testCase{
"start_with_no_tags": {
steps: []testStep{
{
config: resourceFreeformSystem{
blueprintId: bp.Id().String(),
name: acctest.RandString(6),
hostname: acctest.RandString(6),
deviceProfileId: string(dpId),
systemType: apstra.SystemTypeInternal.String(),
},
},
{
config: resourceFreeformSystem{
blueprintId: bp.Id().String(),
name: acctest.RandString(6),
hostname: acctest.RandString(6),
deviceProfileId: string(dpId),
deployMode: apstra.DeployModeDeploy.String(),
systemType: apstra.SystemTypeInternal.String(),
tags: randomStrings(rand.Intn(10)+2, 6),
},
},
{
config: resourceFreeformSystem{
blueprintId: bp.Id().String(),
name: acctest.RandString(6),
hostname: acctest.RandString(6),
deployMode: apstra.DeployModeUndeploy.String(),
systemType: apstra.SystemTypeInternal.String(),
deviceProfileId: string(dpId),
},
},
},
},
"start_with_tags": {
steps: []testStep{
{
config: resourceFreeformSystem{
blueprintId: bp.Id().String(),
name: acctest.RandString(6),
hostname: acctest.RandString(6),
deviceProfileId: string(dpId),
deployMode: apstra.DeployModeDeploy.String(),
systemType: apstra.SystemTypeInternal.String(),
tags: randomStrings(rand.Intn(10)+2, 6),
},
},
{
config: resourceFreeformSystem{
blueprintId: bp.Id().String(),
name: acctest.RandString(6),
hostname: acctest.RandString(6),
deployMode: apstra.DeployModeUndeploy.String(),
systemType: apstra.SystemTypeExternal.String(),
deviceProfileId: string(dpId),
},
},
{
config: resourceFreeformSystem{
blueprintId: bp.Id().String(),
name: acctest.RandString(6),
hostname: acctest.RandString(6),
deviceProfileId: string(dpId),
deployMode: apstra.DeployModeDeploy.String(),
systemType: apstra.SystemTypeExternal.String(),
tags: randomStrings(rand.Intn(10)+2, 6),
},
},
},
},
}

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

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,
})
})
}
}
28 changes: 14 additions & 14 deletions docs/data-sources/freeform_property_set.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ At least one optional attribute is required.

# first we create a property set so we can use a data source to retrieve it.
resource "apstra_freeform_property_set" "prop_set_foo" {
blueprint_id = "freeform_blueprint-5ba09d07"
blueprint_id = "043c5787-66e8-41c7-8925-c7e52fbe6e32"
name = "prop_set_foo"
values = jsonencode({
values = jsonencode({
foo = "bar"
clown = 2
})
Expand All @@ -31,21 +31,21 @@ resource "apstra_freeform_property_set" "prop_set_foo" {
# here we retrieve the property_set.

data "apstra_freeform_property_set" "foo" {
blueprint_id = "freeform_blueprint-5ba09d07"
blueprint_id = "043c5787-66e8-41c7-8925-c7e52fbe6e32"
name = apstra_freeform_property_set.prop_set_foo.name
}

#here we build an output block to display it.
output "foo" {value = data.apstra_freeform_property_set.foo}

#Output looks like this
#foo = {
# "blueprint_id" = "freeform_blueprint-5ba09d07"
# "id" = tostring(null)
# "name" = "prop_set_foo"
# "system_id" = tostring(null)
# "values" = "{\"clown\": 2, \"foo\": \"bar\"}"
#}
# here we build an output block to display it.
output "foo" { value = data.apstra_freeform_property_set.foo }

# Output looks like this
# foo = {
# "blueprint_id" = "043c5787-66e8-41c7-8925-c7e52fbe6e32"
# "id" = tostring(null)
# "name" = "prop_set_foo"
# "system_id" = tostring(null)
# "values" = "{\"clown\": 2, \"foo\": \"bar\"}"
# }
```

<!-- schema generated by tfplugindocs -->
Expand Down
40 changes: 39 additions & 1 deletion docs/data-sources/freeform_system.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,45 @@ At least one optional attribute is required.
## Example Usage

```terraform
#
# This example defines a freeform system in a blueprint

resource "apstra_freeform_system" "test" {
blueprint_id = "043c5787-66e8-41c7-8925-c7e52fbe6e32"
name = "test_system"
tags = ["a", "b", "c"]
type = "internal"
hostname = "testsystem"
deploy_mode = "deploy"
device_profile_id = "PtrWb4-VSwKiYRbCodk"
}

# here we retrieve the freeform system

data "apstra_freeform_system" "test" {
blueprint_id = "043c5787-66e8-41c7-8925-c7e52fbe6e32"
id = apstra_freeform_system.test.id
}

# here we build an output bock to display it

output "test_System_out" {value = data.apstra_freeform_system.test}

# Output looks like this
# test_System_out = {
# "blueprint_id" = "043c5787-66e8-41c7-8925-c7e52fbe6e32"
# "deploy_mode" = tostring(null)
# "device_profile_id" = "PtrWb4-VSwKiYRbCodk"
# "hostname" = "systemfoo"
# "id" = "-63CYLAiWuAq0ljzX0Q"
# "name" = "test_system_foo"
# "system_id" = tostring(null)
# "tags" = toset([
# "a",
# "b",
# "c",
# ])
# "type" = "internal"
# }
```

<!-- schema generated by tfplugindocs -->
Expand Down
Loading
Loading