diff --git a/apstra/freeform/freeform_system.go b/apstra/freeform/freeform_system.go index 64aae985..54bcf341 100644 --- a/apstra/freeform/freeform_system.go +++ b/apstra/freeform/freeform_system.go @@ -6,6 +6,7 @@ import ( "regexp" "github.com/Juniper/apstra-go-sdk/apstra" + apstravalidator "github.com/Juniper/terraform-provider-apstra/apstra/apstra_validator" "github.com/Juniper/terraform-provider-apstra/apstra/utils" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -103,7 +104,8 @@ func (o FreeformSystem) ResourceAttributes() map[string]resourceSchema.Attribute MarkdownDescription: "Freeform System name as shown in the Web UI.", Required: true, Validators: []validator.String{ - stringvalidator.RegexMatches(regexp.MustCompile("^[a-zA-Z0-9.-_]+$"), "name may consist only of the following characters : a-zA-Z0-9.-_")}, + 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.", @@ -118,15 +120,26 @@ func (o FreeformSystem) ResourceAttributes() map[string]resourceSchema.Attribute Validators: []validator.String{stringvalidator.OneOf(utils.AllNodeDeployModes()...)}, }, "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())}, + MarkdownDescription: fmt.Sprintf("Type of the System. Must be one of `%s` or `%s`", + utils.StringersToFriendlyString(apstra.SystemTypeInternal), + utils.StringersToFriendlyString(apstra.SystemTypeExternal), + ), + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + Validators: []validator.String{stringvalidator.OneOf( + utils.StringersToFriendlyString(apstra.SystemTypeInternal), + utils.StringersToFriendlyString(apstra.SystemTypeExternal), + )}, }, "device_profile_id": resourceSchema.StringAttribute{ - MarkdownDescription: "Device profile ID of the System", - Optional: true, - Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, + MarkdownDescription: fmt.Sprintf("Device profile ID of the System. Required when `type` is %q.", + utils.StringersToFriendlyString(apstra.SystemTypeInternal)), + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + apstravalidator.ForbiddenWhenValueIs(path.MatchRoot("type"), types.StringValue(utils.StringersToFriendlyString(apstra.SystemTypeExternal))), + apstravalidator.RequiredWhenValueIs(path.MatchRoot("type"), types.StringValue(utils.StringersToFriendlyString(apstra.SystemTypeInternal))), + }, }, "system_id": resourceSchema.StringAttribute{ MarkdownDescription: "ID (usually serial number) of the Managed Device to associate with this System", @@ -151,9 +164,9 @@ func (o *FreeformSystem) Request(ctx context.Context, diags *diag.Diagnostics) * var systemType apstra.SystemType switch o.Type.ValueString() { - case apstra.SystemTypeExternal.String(): + case utils.StringersToFriendlyString(apstra.SystemTypeExternal): systemType = apstra.SystemTypeExternal - case apstra.SystemTypeInternal.String(): + case utils.StringersToFriendlyString(apstra.SystemTypeInternal): systemType = apstra.SystemTypeInternal default: diags.AddError("unexpected system type", "got: "+o.Type.ValueString()) @@ -172,8 +185,9 @@ 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(in.Type.String()) + o.Type = types.StringValue(utils.StringersToFriendlyString(in.Type)) o.DeviceProfileId = types.StringValue(string(in.DeviceProfileId)) + o.DeviceProfileId = utils.StringValueOrNull(ctx, in.DeviceProfileId.String(), diags) o.SystemId = types.StringPointerValue((*string)(in.SystemId)) o.Tags = utils.SetValueOrNull(ctx, types.StringType, in.Tags, diags) // safe to ignore diagnostic here } diff --git a/apstra/resource_freeform_system_integration_test.go b/apstra/resource_freeform_system_integration_test.go index cd5e03ac..2f15e9a4 100644 --- a/apstra/resource_freeform_system_integration_test.go +++ b/apstra/resource_freeform_system_integration_test.go @@ -5,11 +5,11 @@ package tfapstra_test import ( "context" "fmt" - "github.com/Juniper/apstra-go-sdk/apstra" "math/rand" "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/go-version" @@ -22,7 +22,7 @@ const ( resource %q %q { blueprint_id = %q name = %q - device_profile_id = %q + device_profile_id = %s hostname = %q type = %q deploy_mode = %s @@ -46,7 +46,7 @@ func (o resourceFreeformSystem) render(rType, rName string) string { rType, rName, o.blueprintId, o.name, - o.deviceProfileId, + stringOrNull(o.deviceProfileId), o.hostname, o.systemType, stringOrNull(o.deployMode), @@ -62,7 +62,11 @@ func (o resourceFreeformSystem) testChecks(t testing.TB, rType, rName string) te 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) + if o.deviceProfileId != "" { + result.append(t, "TestCheckResourceAttr", "device_profile_id", o.deviceProfileId) + } else { + result.append(t, "TestCheckNoResourceAttr", "device_profile_id") + } result.append(t, "TestCheckResourceAttr", "hostname", o.hostname) if o.deployMode != "" { result.append(t, "TestCheckResourceAttr", "deploy_mode", o.deployMode) @@ -84,7 +88,8 @@ func TestResourceFreeformSystem(t *testing.T) { // create a blueprint bp := testutils.FfBlueprintA(t, ctx) - // get a device profile + + // import a device profile dpId, _ := bp.ImportDeviceProfile(ctx, "Juniper_vEX") type testStep struct { @@ -96,7 +101,69 @@ func TestResourceFreeformSystem(t *testing.T) { } testCases := map[string]testCase{ - "start_with_no_tags": { + "external_start_minimal": { + steps: []testStep{ + { + config: resourceFreeformSystem{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + hostname: acctest.RandString(6), + systemType: apstra.SystemTypeExternal.String(), + }, + }, + { + config: resourceFreeformSystem{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + hostname: acctest.RandString(6), + deployMode: apstra.DeployModeDeploy.String(), + systemType: apstra.SystemTypeExternal.String(), + tags: randomStrings(rand.Intn(10)+2, 6), + }, + }, + { + config: resourceFreeformSystem{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + hostname: acctest.RandString(6), + systemType: apstra.SystemTypeExternal.String(), + }, + }, + }, + }, + "external_start_maximal": { + steps: []testStep{ + { + config: resourceFreeformSystem{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + hostname: acctest.RandString(6), + deployMode: apstra.DeployModeDeploy.String(), + systemType: apstra.SystemTypeExternal.String(), + tags: randomStrings(rand.Intn(10)+2, 6), + }, + }, + { + config: resourceFreeformSystem{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + hostname: acctest.RandString(6), + systemType: apstra.SystemTypeExternal.String(), + }, + }, + { + config: resourceFreeformSystem{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + hostname: acctest.RandString(6), + deployMode: apstra.DeployModeDeploy.String(), + systemType: apstra.SystemTypeExternal.String(), + tags: randomStrings(rand.Intn(10)+2, 6), + }, + }, + }, + }, + "internal_start_minimal": { steps: []testStep{ { config: resourceFreeformSystem{ @@ -123,14 +190,13 @@ func TestResourceFreeformSystem(t *testing.T) { 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": { + "internal_start_maxmial": { steps: []testStep{ { config: resourceFreeformSystem{ @@ -148,8 +214,7 @@ func TestResourceFreeformSystem(t *testing.T) { blueprintId: bp.Id().String(), name: acctest.RandString(6), hostname: acctest.RandString(6), - deployMode: apstra.DeployModeUndeploy.String(), - systemType: apstra.SystemTypeExternal.String(), + systemType: apstra.SystemTypeInternal.String(), deviceProfileId: string(dpId), }, }, @@ -160,7 +225,7 @@ func TestResourceFreeformSystem(t *testing.T) { hostname: acctest.RandString(6), deviceProfileId: string(dpId), deployMode: apstra.DeployModeDeploy.String(), - systemType: apstra.SystemTypeExternal.String(), + systemType: apstra.SystemTypeInternal.String(), tags: randomStrings(rand.Intn(10)+2, 6), }, }, diff --git a/docs/resources/freeform_system.md b/docs/resources/freeform_system.md index 72e2e773..c841cc31 100644 --- a/docs/resources/freeform_system.md +++ b/docs/resources/freeform_system.md @@ -68,7 +68,7 @@ output "test_System_out" { value = data.apstra_freeform_system.test } ### Optional - `deploy_mode` (String) Deploy mode of the System -- `device_profile_id` (String) Device profile ID of the System +- `device_profile_id` (String) Device profile ID of the System. Required when `type` is "internal". - `system_id` (String) ID (usually serial number) of the Managed Device to associate with this System - `tags` (Set of String) Set of Tag labels diff --git a/go.mod b/go.mod index 87270cc3..67a5f90f 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-20240807201924-9a8d4a6b3ebf + github.com/Juniper/apstra-go-sdk v0.0.0-20240813145707-bda42a1f3e8d 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 f3d23e2e..ceabf0d1 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-20240807201924-9a8d4a6b3ebf h1:G5MohgVCO8fzW/Z40HTd8xQOyt6mcHXPpIsO/xqQVUA= -github.com/Juniper/apstra-go-sdk v0.0.0-20240807201924-9a8d4a6b3ebf/go.mod h1:cSUzaIIQzZysIVKgJnt2/jO2EKeAB60Xgbx8yBGwJ8Y= +github.com/Juniper/apstra-go-sdk v0.0.0-20240813145707-bda42a1f3e8d h1:CVKhYO0RdUE5zNRoXpKt5Pp1isu52A/u9e1ZarcO6GQ= +github.com/Juniper/apstra-go-sdk v0.0.0-20240813145707-bda42a1f3e8d/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=