From 5f393ebeae2ac252fa2592403dd25e9df6a2827f Mon Sep 17 00:00:00 2001 From: bwjuniper Date: Wed, 28 Aug 2024 10:02:44 -0700 Subject: [PATCH 1/4] implement freeform group generators with tests --- .../data_source_freeform_group_generator.go | 101 +++++++++ apstra/export_test.go | 1 + apstra/freeform/group_generator.go | 100 ++++++++ apstra/provider.go | 2 + apstra/resource_freeform_group_generator.go | 213 ++++++++++++++++++ ...eeform_group_generator_integration_test.go | 125 ++++++++++ docs/data-sources/freeform_group_generator.md | 73 ++++++ docs/resources/freeform_group_generator.md | 70 ++++++ .../example.tf | 38 ++++ .../example.tf | 38 ++++ 10 files changed, 761 insertions(+) create mode 100644 apstra/data_source_freeform_group_generator.go create mode 100644 apstra/freeform/group_generator.go create mode 100644 apstra/resource_freeform_group_generator.go create mode 100644 apstra/resource_freeform_group_generator_integration_test.go create mode 100644 docs/data-sources/freeform_group_generator.md create mode 100644 docs/resources/freeform_group_generator.md create mode 100644 examples/data-sources/apstra_freeform_group_generator/example.tf create mode 100644 examples/resources/apstra_freeform_group_generator/example.tf diff --git a/apstra/data_source_freeform_group_generator.go b/apstra/data_source_freeform_group_generator.go new file mode 100644 index 00000000..3d979ee2 --- /dev/null +++ b/apstra/data_source_freeform_group_generator.go @@ -0,0 +1,101 @@ +package tfapstra + +import ( + "context" + "fmt" + + "github.com/Juniper/apstra-go-sdk/apstra" + "github.com/Juniper/terraform-provider-apstra/apstra/freeform" + "github.com/Juniper/terraform-provider-apstra/apstra/utils" + "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 = &dataSourceFreeformGroupGenerator{} + _ datasourceWithSetFfBpClientFunc = &dataSourceFreeformGroupGenerator{} +) + +type dataSourceFreeformGroupGenerator struct { + getBpClientFunc func(context.Context, string) (*apstra.FreeformClient, error) +} + +func (o *dataSourceFreeformGroupGenerator) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_freeform_group_generator" +} + +func (o *dataSourceFreeformGroupGenerator) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + configureDataSource(ctx, o, req, resp) +} + +func (o *dataSourceFreeformGroupGenerator) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: docCategoryFreeform + "This data source provides details of a specific Freeform Group Generator.\n\n" + + "At least one optional attribute is required.", + Attributes: freeform.GroupGenerator{}.DataSourceAttributes(), + } +} + +func (o *dataSourceFreeformGroupGenerator) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config freeform.GroupGenerator + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + // get a client for the Freeform reference design + bp, err := o.getBpClientFunc(ctx, config.BlueprintId.ValueString()) + if err != nil { + if utils.IsApstra404(err) { + resp.Diagnostics.AddError(fmt.Sprintf("blueprint %s not found", config.BlueprintId), err.Error()) + return + } + resp.Diagnostics.AddError("failed to create blueprint client", err.Error()) + return + } + + var api *apstra.FreeformGroupGenerator + switch { + case !config.Id.IsNull(): + api, err = bp.GetGroupGenerator(ctx, apstra.ObjectId(config.Id.ValueString())) + if utils.IsApstra404(err) { + resp.Diagnostics.AddAttributeError( + path.Root("id"), + "Freeform Group Generator not found", + fmt.Sprintf("Freeform Group Generator with ID %s not found", config.Id)) + return + } + case !config.Name.IsNull(): + api, err = bp.GetGroupGeneratorByName(ctx, config.Name.ValueString()) + if utils.IsApstra404(err) { + resp.Diagnostics.AddAttributeError( + path.Root("name"), + "Freeform Group Generator not found", + fmt.Sprintf("Freeform Group Generator with Name %s not found", config.Name)) + return + } + } + if err != nil { + resp.Diagnostics.AddError("failed reading Freeform Group Generator", err.Error()) + return + } + if api.Data == nil { + resp.Diagnostics.AddError("failed reading Freeform Group Generator", "api response has no payload") + return + } + + config.Id = types.StringValue(api.Id.String()) + config.LoadApiData(ctx, api.Data) + if resp.Diagnostics.HasError() { + return + } + + // Set state + resp.Diagnostics.Append(resp.State.Set(ctx, &config)...) +} + +func (o *dataSourceFreeformGroupGenerator) setBpClientFunc(f func(context.Context, string) (*apstra.FreeformClient, error)) { + o.getBpClientFunc = f +} diff --git a/apstra/export_test.go b/apstra/export_test.go index e134e71c..6a83a741 100644 --- a/apstra/export_test.go +++ b/apstra/export_test.go @@ -21,6 +21,7 @@ var ( ResourceFreeformBlueprint = resourceFreeformBlueprint{} ResourceFreeformConfigTemplate = resourceFreeformConfigTemplate{} ResourceFreeformDeviceProfile = resourceFreeformDeviceProfile{} + ResourceFreeformGroupGenerator = resourceFreeformGroupGenerator{} ResourceFreeformLink = resourceFreeformLink{} ResourceFreeformPropertySet = resourceFreeformPropertySet{} ResourceFreeformResourceGenerator = resourceFreeformResourceGenerator{} diff --git a/apstra/freeform/group_generator.go b/apstra/freeform/group_generator.go new file mode 100644 index 00000000..c67231a0 --- /dev/null +++ b/apstra/freeform/group_generator.go @@ -0,0 +1,100 @@ +package freeform + +import ( + "context" + "github.com/Juniper/apstra-go-sdk/apstra" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + dataSourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "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 GroupGenerator struct { + BlueprintId types.String `tfsdk:"blueprint_id"` + Id types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Scope types.String `tfsdk:"scope"` +} + +func (o GroupGenerator) DataSourceAttributes() map[string]dataSourceSchema.Attribute { + return map[string]dataSourceSchema.Attribute{ + "blueprint_id": dataSourceSchema.StringAttribute{ + MarkdownDescription: "Apstra Blueprint ID. Used to identify " + + "the Blueprint where the Group lives.", + Required: true, + Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, + }, + "id": dataSourceSchema.StringAttribute{ + MarkdownDescription: "Populate this field to look up the Freeform Group Generator by ID. Required when `name` is omitted.", + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRelative(), + path.MatchRoot("name"), + }...), + }, + }, + "name": dataSourceSchema.StringAttribute{ + MarkdownDescription: "Populate this field to look up Group Generator by Name. Required when `id` is omitted.", + Optional: true, + Computed: true, + Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, + }, + "scope": dataSourceSchema.StringAttribute{ + MarkdownDescription: "Scope is a graph query which selects target nodes for which Groups should be generated.\n" + + "Example: `node('system', name='target', label=aeq('*prod*'))`", + Computed: true, + }, + } +} + +func (o GroupGenerator) ResourceAttributes() map[string]resourceSchema.Attribute { + return map[string]resourceSchema.Attribute{ + "blueprint_id": resourceSchema.StringAttribute{ + MarkdownDescription: "Apstra Blueprint ID.", + Required: true, + Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "id": resourceSchema.StringAttribute{ + MarkdownDescription: "ID of the Group Generator within the Freeform Blueprint.", + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "name": resourceSchema.StringAttribute{ + MarkdownDescription: "Freeform Group Generator 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.-_", + ), + }, + }, + "scope": resourceSchema.StringAttribute{ + MarkdownDescription: "Scope is a graph query which selects target nodes for which Groups should be generated.\n" + + "Example: `node('system', name='target', label=aeq('*prod*'))`", + Required: true, + Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, + }, + } +} + +func (o *GroupGenerator) Request(_ context.Context) *apstra.FreeformGroupGeneratorData { + return &apstra.FreeformGroupGeneratorData{ + Label: o.Name.ValueString(), + Scope: o.Scope.ValueString(), + } +} + +func (o *GroupGenerator) LoadApiData(_ context.Context, in *apstra.FreeformGroupGeneratorData) { + o.Name = types.StringValue(in.Label) + o.Scope = types.StringValue(in.Scope) +} diff --git a/apstra/provider.go b/apstra/provider.go index d91ecdee..570b3c9c 100644 --- a/apstra/provider.go +++ b/apstra/provider.go @@ -555,6 +555,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource func() datasource.DataSource { return &dataSourceFreeformAllocGroup{} }, func() datasource.DataSource { return &dataSourceFreeformBlueprint{} }, func() datasource.DataSource { return &dataSourceFreeformConfigTemplate{} }, + func() datasource.DataSource { return &dataSourceFreeformGroupGenerator{} }, func() datasource.DataSource { return &dataSourceFreeformLink{} }, func() datasource.DataSource { return &dataSourceFreeformPropertySet{} }, func() datasource.DataSource { return &dataSourceFreeformResourceGenerator{} }, @@ -623,6 +624,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource { func() resource.Resource { return &resourceFreeformBlueprint{} }, func() resource.Resource { return &resourceFreeformConfigTemplate{} }, func() resource.Resource { return &resourceFreeformDeviceProfile{} }, + func() resource.Resource { return &resourceFreeformGroupGenerator{} }, func() resource.Resource { return &resourceFreeformLink{} }, func() resource.Resource { return &resourceFreeformPropertySet{} }, func() resource.Resource { return &resourceFreeformResourceGenerator{} }, diff --git a/apstra/resource_freeform_group_generator.go b/apstra/resource_freeform_group_generator.go new file mode 100644 index 00000000..e4eb2792 --- /dev/null +++ b/apstra/resource_freeform_group_generator.go @@ -0,0 +1,213 @@ +package tfapstra + +import ( + "context" + "fmt" + + "github.com/Juniper/apstra-go-sdk/apstra" + "github.com/Juniper/terraform-provider-apstra/apstra/freeform" + "github.com/Juniper/terraform-provider-apstra/apstra/utils" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var ( + _ resource.ResourceWithConfigure = &resourceFreeformGroupGenerator{} + _ resourceWithSetFfBpClientFunc = &resourceFreeformGroupGenerator{} + _ resourceWithSetBpLockFunc = &resourceFreeformGroupGenerator{} +) + +type resourceFreeformGroupGenerator struct { + getBpClientFunc func(context.Context, string) (*apstra.FreeformClient, error) + lockFunc func(context.Context, string) error +} + +func (o *resourceFreeformGroupGenerator) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_freeform_group_generator" +} + +func (o *resourceFreeformGroupGenerator) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + configureResource(ctx, o, req, resp) +} + +func (o *resourceFreeformGroupGenerator) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: docCategoryFreeform + "This resource creates a Group Generator in a Freeform Blueprint.", + Attributes: freeform.GroupGenerator{}.ResourceAttributes(), + } +} + +func (o *resourceFreeformGroupGenerator) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // Retrieve values from plan + var plan freeform.GroupGenerator + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + // get a client for the Freeform reference design + bp, err := o.getBpClientFunc(ctx, plan.BlueprintId.ValueString()) + if err != nil { + if utils.IsApstra404(err) { + resp.Diagnostics.AddError(fmt.Sprintf("blueprint %s not found", plan.BlueprintId), err.Error()) + return + } + resp.Diagnostics.AddError("failed to create blueprint client", err.Error()) + return + } + + // Lock the blueprint mutex. + err = o.lockFunc(ctx, plan.BlueprintId.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("error locking blueprint %q mutex", plan.BlueprintId.ValueString()), + err.Error()) + return + } + + // Convert the plan into an API Request + request := plan.Request(ctx) + if resp.Diagnostics.HasError() { + return + } + + // Create the resource + id, err := bp.CreateGroupGenerator(ctx, request) + if err != nil { + resp.Diagnostics.AddError("error creating new Group Generator", err.Error()) + return + } + + plan.Id = types.StringValue(id.String()) + + // set state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (o *resourceFreeformGroupGenerator) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state freeform.GroupGenerator + 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, state.BlueprintId.ValueString()) + if err != nil { + if utils.IsApstra404(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError("failed to create blueprint client", err.Error()) + return + } + + api, err := bp.GetGroupGenerator(ctx, apstra.ObjectId(state.Id.ValueString())) + if err != nil { + if utils.IsApstra404(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError("Error retrieving Freeform Group Generator", err.Error()) + return + } + + state.LoadApiData(ctx, api.Data) + if resp.Diagnostics.HasError() { + return + } + + // Set state + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (o *resourceFreeformGroupGenerator) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // Get plan values + var plan freeform.GroupGenerator + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + // get a client for the Freeform reference design + bp, err := o.getBpClientFunc(ctx, plan.BlueprintId.ValueString()) + if err != nil { + if utils.IsApstra404(err) { + resp.Diagnostics.AddError(fmt.Sprintf("blueprint %s not found", plan.BlueprintId), err.Error()) + return + } + resp.Diagnostics.AddError("failed to create blueprint client", err.Error()) + return + } + + // Lock the blueprint mutex. + err = o.lockFunc(ctx, plan.BlueprintId.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("error locking blueprint %q mutex", plan.BlueprintId.ValueString()), + err.Error()) + return + } + + // Convert the plan into an API Request + request := plan.Request(ctx) + if resp.Diagnostics.HasError() { + return + } + + // Update the Group + err = bp.UpdateGroupGenerator(ctx, apstra.ObjectId(plan.Id.ValueString()), request) + if err != nil { + resp.Diagnostics.AddError("error updating Freeform Group Generator", err.Error()) + return + } + + // set state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (o *resourceFreeformGroupGenerator) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state freeform.GroupGenerator + 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, state.BlueprintId.ValueString()) + if err != nil { + if utils.IsApstra404(err) { + return // 404 is okay + } + resp.Diagnostics.AddError("failed to create blueprint client", err.Error()) + return + } + + // Lock the blueprint mutex. + err = o.lockFunc(ctx, state.BlueprintId.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("error locking blueprint %q mutex", state.BlueprintId.ValueString()), + err.Error()) + return + } + + // Delete Config Template by calling API + err = bp.DeleteGroupGenerator(ctx, apstra.ObjectId(state.Id.ValueString())) + if err != nil { + if utils.IsApstra404(err) { + return // 404 is okay + } + resp.Diagnostics.AddError("error deleting Freeform Group", err.Error()) + return + } +} + +func (o *resourceFreeformGroupGenerator) setBpClientFunc(f func(context.Context, string) (*apstra.FreeformClient, error)) { + o.getBpClientFunc = f +} + +func (o *resourceFreeformGroupGenerator) setBpLockFunc(f func(context.Context, string) error) { + o.lockFunc = f +} diff --git a/apstra/resource_freeform_group_generator_integration_test.go b/apstra/resource_freeform_group_generator_integration_test.go new file mode 100644 index 00000000..59fc50bb --- /dev/null +++ b/apstra/resource_freeform_group_generator_integration_test.go @@ -0,0 +1,125 @@ +//go:build integration + +package tfapstra_test + +import ( + "context" + "fmt" + "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 ( + resourceFreeformGroupGeneratorHcl = ` +resource %q %q { + blueprint_id = %q + name = %q + scope = %q + } +` +) + +type resourceFreeformGroupGenerator struct { + blueprintId string + name string + scope string +} + +func (o resourceFreeformGroupGenerator) render(rType, rName string) string { + return fmt.Sprintf(resourceFreeformGroupGeneratorHcl, + rType, rName, + o.blueprintId, + o.name, + o.scope, + ) +} + +func (o resourceFreeformGroupGenerator) 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", "scope", o.scope) + + return result +} + +func TestResourceFreeformGroupGenerator(t *testing.T) { + ctx := context.Background() + client := testutils.GetTestClient(t, ctx) + apiVersion := version.Must(version.NewVersion(client.ApiVersion())) + + // create a blueprint and a group... + bp, _ := testutils.FfBlueprintC(t, ctx) + + type testStep struct { + config resourceFreeformGroupGenerator + } + + type testCase struct { + apiVersionConstraints version.Constraints + steps []testStep + } + + testCases := map[string]testCase{ + "start_asn_resource_generator": { + steps: []testStep{ + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: "node('system', name='target')", + }, + }, + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: "node('system', deploy_mode='deploy', name='target')", + }, + }, + }, + }, + } + + resourceType := tfapstra.ResourceName(ctx, &tfapstra.ResourceFreeformGroupGenerator) + + 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 ------%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, + }) + }) + } +} diff --git a/docs/data-sources/freeform_group_generator.md b/docs/data-sources/freeform_group_generator.md new file mode 100644 index 00000000..66092a82 --- /dev/null +++ b/docs/data-sources/freeform_group_generator.md @@ -0,0 +1,73 @@ +--- +page_title: "apstra_freeform_group_generator Data Source - terraform-provider-apstra" +subcategory: "Reference Design: Freeform" +description: |- + This data source provides details of a specific Freeform Group Generator. + At least one optional attribute is required. +--- + +# apstra_freeform_group_generator (Data Source) + +This data source provides details of a specific Freeform Group Generator. + +At least one optional attribute is required. + + +## Example Usage + +```terraform +# This example creates an group Generator within a +# preexisting resource group in a Freeform Blueprint. +# +# After creating the Group Generator, the data source is invoked to look up +# the details. + +# Create a resource group in a preexisting blueprint. +resource "apstra_freeform_resource_group" "fizz_grp" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "fizz_grp" +} + +# Create a resource generator scoped to target all systems in the blueprint. +resource "apstra_freeform_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "test_res_gen" + scope = "node('system', name='target')" +} + +# Invoke the resource generator data source +data "apstra_freeform_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + id = apstra_freeform_group_generator.test_group_gen.id +} + +# Output the data source so that it prints on screen +output "test_group_generator_out" { + value = data.apstra_freeform_group_generator.test_group_gen +} + +# The output looks like this: +#test_group_generator_out = { +# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" +# "id" = "wOQS9qZezzRJCgyvxwY" +# "name" = "test_res_gen" +# "scope" = "node('system', name='target')" +#} +``` + + +## Schema + +### Required + +- `blueprint_id` (String) Apstra Blueprint ID. Used to identify the Blueprint where the Group lives. + +### Optional + +- `id` (String) Populate this field to look up the Freeform Group Generator by ID. Required when `name` is omitted. +- `name` (String) Populate this field to look up Group Generator by Name. Required when `id` is omitted. + +### Read-Only + +- `scope` (String) Scope is a graph query which selects target nodes for which Groups should be generated. +Example: `node('system', name='target', label=aeq('*prod*'))` diff --git a/docs/resources/freeform_group_generator.md b/docs/resources/freeform_group_generator.md new file mode 100644 index 00000000..653b8960 --- /dev/null +++ b/docs/resources/freeform_group_generator.md @@ -0,0 +1,70 @@ +--- +page_title: "apstra_freeform_group_generator Resource - terraform-provider-apstra" +subcategory: "Reference Design: Freeform" +description: |- + This resource creates a Group Generator in a Freeform Blueprint. +--- + +# apstra_freeform_group_generator (Resource) + +This resource creates a Group Generator in a Freeform Blueprint. + + +## Example Usage + +```terraform +# This example creates an group Generator within a +# preexisting resource group in a Freeform Blueprint. +# +# After creating the Group Generator, the data source is invoked to look up +# the details. + +# Create a resource group in a preexisting blueprint. +resource "apstra_freeform_resource_group" "fizz_grp" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "fizz_grp" +} + +# Create a resource generator scoped to target all systems in the blueprint. +resource "apstra_freeform_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "test_res_gen" + scope = "node('system', name='target')" +} + +# Invoke the resource generator data source +data "apstra_freeform_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + id = apstra_freeform_group_generator.test_group_gen.id +} + +# Output the data source so that it prints on screen +output "test_group_generator_out" { + value = data.apstra_freeform_group_generator.test_group_gen +} + +# The output looks like this: +#test_group_generator_out = { +# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" +# "id" = "wOQS9qZezzRJCgyvxwY" +# "name" = "test_res_gen" +# "scope" = "node('system', name='target')" +#} +``` + + +## Schema + +### Required + +- `blueprint_id` (String) Apstra Blueprint ID. +- `name` (String) Freeform Group Generator name as shown in the Web UI. +- `scope` (String) Scope is a graph query which selects target nodes for which Groups should be generated. +Example: `node('system', name='target', label=aeq('*prod*'))` + +### Read-Only + +- `id` (String) ID of the Group Generator within the Freeform Blueprint. + + + diff --git a/examples/data-sources/apstra_freeform_group_generator/example.tf b/examples/data-sources/apstra_freeform_group_generator/example.tf new file mode 100644 index 00000000..9a812d61 --- /dev/null +++ b/examples/data-sources/apstra_freeform_group_generator/example.tf @@ -0,0 +1,38 @@ +# This example creates an group Generator within a +# preexisting resource group in a Freeform Blueprint. +# +# After creating the Group Generator, the data source is invoked to look up +# the details. + +# Create a resource group in a preexisting blueprint. +resource "apstra_freeform_resource_group" "fizz_grp" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "fizz_grp" +} + +# Create a resource generator scoped to target all systems in the blueprint. +resource "apstra_freeform_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "test_res_gen" + scope = "node('system', name='target')" +} + +# Invoke the resource generator data source +data "apstra_freeform_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + id = apstra_freeform_group_generator.test_group_gen.id +} + +# Output the data source so that it prints on screen +output "test_group_generator_out" { + value = data.apstra_freeform_group_generator.test_group_gen +} + +# The output looks like this: +#test_group_generator_out = { +# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" +# "id" = "wOQS9qZezzRJCgyvxwY" +# "name" = "test_res_gen" +# "scope" = "node('system', name='target')" +#} + diff --git a/examples/resources/apstra_freeform_group_generator/example.tf b/examples/resources/apstra_freeform_group_generator/example.tf new file mode 100644 index 00000000..9a812d61 --- /dev/null +++ b/examples/resources/apstra_freeform_group_generator/example.tf @@ -0,0 +1,38 @@ +# This example creates an group Generator within a +# preexisting resource group in a Freeform Blueprint. +# +# After creating the Group Generator, the data source is invoked to look up +# the details. + +# Create a resource group in a preexisting blueprint. +resource "apstra_freeform_resource_group" "fizz_grp" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "fizz_grp" +} + +# Create a resource generator scoped to target all systems in the blueprint. +resource "apstra_freeform_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "test_res_gen" + scope = "node('system', name='target')" +} + +# Invoke the resource generator data source +data "apstra_freeform_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + id = apstra_freeform_group_generator.test_group_gen.id +} + +# Output the data source so that it prints on screen +output "test_group_generator_out" { + value = data.apstra_freeform_group_generator.test_group_gen +} + +# The output looks like this: +#test_group_generator_out = { +# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" +# "id" = "wOQS9qZezzRJCgyvxwY" +# "name" = "test_res_gen" +# "scope" = "node('system', name='target')" +#} + From 219d4b8e6e077c243f697fb2a388913008463437 Mon Sep 17 00:00:00 2001 From: bwjuniper Date: Wed, 28 Aug 2024 10:07:02 -0700 Subject: [PATCH 2/4] go mod tidy --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d73ad39e..023b666a 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-20240828150246-aaece793e672 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..be62c6f9 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-20240828150246-aaece793e672 h1:6xwcQiG477z0MBqFUaMAIHi8ZctvIhpbeiDXf7UCHPk= +github.com/Juniper/apstra-go-sdk v0.0.0-20240828150246-aaece793e672/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 6e8c2268f02ba572916095b811e05f1e4df22677 Mon Sep 17 00:00:00 2001 From: bwjuniper Date: Wed, 28 Aug 2024 12:52:44 -0700 Subject: [PATCH 3/4] go mod tidy --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 023b666a..ad04de40 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-20240828150246-aaece793e672 + github.com/Juniper/apstra-go-sdk v0.0.0-20240828195058-e7a28edd78dd 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 be62c6f9..6e1e8397 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-20240828150246-aaece793e672 h1:6xwcQiG477z0MBqFUaMAIHi8ZctvIhpbeiDXf7UCHPk= -github.com/Juniper/apstra-go-sdk v0.0.0-20240828150246-aaece793e672/go.mod h1:cSUzaIIQzZysIVKgJnt2/jO2EKeAB60Xgbx8yBGwJ8Y= +github.com/Juniper/apstra-go-sdk v0.0.0-20240828195058-e7a28edd78dd h1:aHNvAvImPwBDCFTTC6CaMJt5VvPSQGpfJaRrsScG9zs= +github.com/Juniper/apstra-go-sdk v0.0.0-20240828195058-e7a28edd78dd/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 7ba6971d92fe4c7bcdec56af5c67ce041998a9b0 Mon Sep 17 00:00:00 2001 From: Chris Marget Date: Thu, 29 Aug 2024 22:04:07 -0400 Subject: [PATCH 4/4] code review, add `group_id` attribute --- ...urce_freeform_resource_group_generator.go} | 4 +- apstra/freeform/link.go | 1 - ...nerator.go => resource_group_generator.go} | 50 ++-- ...eeform_group_generator_integration_test.go | 125 --------- ...resource_freeform_link_integration_test.go | 1 - .../resource_freeform_resource_generator.go | 2 +- ...urce_freeform_resource_group_generator.go} | 13 +- ...source_group_generator_integration_test.go | 239 ++++++++++++++++++ apstra/test_helpers_test.go | 4 +- ...d => freeform_resource_group_generator.md} | 42 +-- docs/resources/freeform_group_generator.md | 70 ----- .../freeform_resource_group_generator.md | 75 ++++++ .../example.tf | 38 --- .../example.tf | 39 +++ .../example.tf | 38 --- .../example.tf | 39 +++ go.mod | 2 +- go.sum | 4 +- 18 files changed, 460 insertions(+), 326 deletions(-) rename apstra/{data_source_freeform_group_generator.go => data_source_freeform_resource_group_generator.go} (96%) rename apstra/freeform/{group_generator.go => resource_group_generator.go} (78%) delete mode 100644 apstra/resource_freeform_group_generator_integration_test.go rename apstra/{resource_freeform_group_generator.go => resource_freeform_resource_group_generator.go} (95%) create mode 100644 apstra/resource_freeform_resource_group_generator_integration_test.go rename docs/data-sources/{freeform_group_generator.md => freeform_resource_group_generator.md} (54%) delete mode 100644 docs/resources/freeform_group_generator.md create mode 100644 docs/resources/freeform_resource_group_generator.md delete mode 100644 examples/data-sources/apstra_freeform_group_generator/example.tf create mode 100644 examples/data-sources/apstra_freeform_resource_group_generator/example.tf delete mode 100644 examples/resources/apstra_freeform_group_generator/example.tf create mode 100644 examples/resources/apstra_freeform_resource_group_generator/example.tf diff --git a/apstra/data_source_freeform_group_generator.go b/apstra/data_source_freeform_resource_group_generator.go similarity index 96% rename from apstra/data_source_freeform_group_generator.go rename to apstra/data_source_freeform_resource_group_generator.go index 3d979ee2..a881b8e8 100644 --- a/apstra/data_source_freeform_group_generator.go +++ b/apstra/data_source_freeform_resource_group_generator.go @@ -23,7 +23,7 @@ type dataSourceFreeformGroupGenerator struct { } func (o *dataSourceFreeformGroupGenerator) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_freeform_group_generator" + resp.TypeName = req.ProviderTypeName + "_freeform_resource_group_generator" } func (o *dataSourceFreeformGroupGenerator) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { @@ -87,7 +87,7 @@ func (o *dataSourceFreeformGroupGenerator) Read(ctx context.Context, req datasou } config.Id = types.StringValue(api.Id.String()) - config.LoadApiData(ctx, api.Data) + config.LoadApiData(ctx, api.Data, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } diff --git a/apstra/freeform/link.go b/apstra/freeform/link.go index ee807e7e..3be0c3b0 100644 --- a/apstra/freeform/link.go +++ b/apstra/freeform/link.go @@ -188,7 +188,6 @@ func (o *Link) LoadApiData(ctx context.Context, in *apstra.FreeformLinkData, dia } o.Speed = types.StringValue(string(in.Speed)) - o.Type = types.StringValue(in.Type.String()) o.Name = types.StringValue(in.Label) o.Endpoints = newFreeformEndpointMap(ctx, in.Endpoints, diags) // safe to ignore diagnostic here o.AggregateLinkId = types.StringPointerValue((*string)(in.AggregateLinkId)) diff --git a/apstra/freeform/group_generator.go b/apstra/freeform/resource_group_generator.go similarity index 78% rename from apstra/freeform/group_generator.go rename to apstra/freeform/resource_group_generator.go index c67231a0..25753639 100644 --- a/apstra/freeform/group_generator.go +++ b/apstra/freeform/resource_group_generator.go @@ -2,33 +2,30 @@ package freeform import ( "context" + "regexp" + "github.com/Juniper/apstra-go-sdk/apstra" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" dataSourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "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 GroupGenerator struct { - BlueprintId types.String `tfsdk:"blueprint_id"` Id types.String `tfsdk:"id"` + BlueprintId types.String `tfsdk:"blueprint_id"` + GroupId types.String `tfsdk:"group_id"` Name types.String `tfsdk:"name"` Scope types.String `tfsdk:"scope"` } func (o GroupGenerator) DataSourceAttributes() map[string]dataSourceSchema.Attribute { return map[string]dataSourceSchema.Attribute{ - "blueprint_id": dataSourceSchema.StringAttribute{ - MarkdownDescription: "Apstra Blueprint ID. Used to identify " + - "the Blueprint where the Group lives.", - Required: true, - Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, - }, "id": dataSourceSchema.StringAttribute{ MarkdownDescription: "Populate this field to look up the Freeform Group Generator by ID. Required when `name` is omitted.", Optional: true, @@ -41,6 +38,16 @@ func (o GroupGenerator) DataSourceAttributes() map[string]dataSourceSchema.Attri }...), }, }, + "blueprint_id": dataSourceSchema.StringAttribute{ + MarkdownDescription: "Apstra Blueprint ID. Used to identify " + + "the Blueprint where the Group lives.", + Required: true, + Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, + }, + "group_id": dataSourceSchema.StringAttribute{ + MarkdownDescription: "Resource Group the Group Generator belongs to.", + Computed: true, + }, "name": dataSourceSchema.StringAttribute{ MarkdownDescription: "Populate this field to look up Group Generator by Name. Required when `id` is omitted.", Optional: true, @@ -57,16 +64,21 @@ func (o GroupGenerator) DataSourceAttributes() map[string]dataSourceSchema.Attri func (o GroupGenerator) ResourceAttributes() map[string]resourceSchema.Attribute { return map[string]resourceSchema.Attribute{ + "id": resourceSchema.StringAttribute{ + MarkdownDescription: "ID of the Group Generator within the Freeform Blueprint.", + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, "blueprint_id": resourceSchema.StringAttribute{ MarkdownDescription: "Apstra Blueprint ID.", Required: true, Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, }, - "id": resourceSchema.StringAttribute{ - MarkdownDescription: "ID of the Group Generator within the Freeform Blueprint.", - Computed: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + "group_id": resourceSchema.StringAttribute{ + MarkdownDescription: "Resource Group the Group Generator belongs to. Omit to create at the `root` level.", + Optional: true, + Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, }, "name": resourceSchema.StringAttribute{ MarkdownDescription: "Freeform Group Generator name as shown in the Web UI.", @@ -79,22 +91,24 @@ func (o GroupGenerator) ResourceAttributes() map[string]resourceSchema.Attribute }, }, "scope": resourceSchema.StringAttribute{ - MarkdownDescription: "Scope is a graph query which selects target nodes for which Groups should be generated.\n" + - "Example: `node('system', name='target', label=aeq('*prod*'))`", + MarkdownDescription: "Scope is a graph query which selects target nodes for which Group Generators should " + + "be generated.\nExample: `node('system', name='target', label=aeq('*prod*'))`", Required: true, Validators: []validator.String{stringvalidator.LengthAtLeast(1)}, }, } } -func (o *GroupGenerator) Request(_ context.Context) *apstra.FreeformGroupGeneratorData { +func (o *GroupGenerator) Request(_ context.Context, _ *diag.Diagnostics) *apstra.FreeformGroupGeneratorData { return &apstra.FreeformGroupGeneratorData{ - Label: o.Name.ValueString(), - Scope: o.Scope.ValueString(), + ParentId: (*apstra.ObjectId)(o.GroupId.ValueStringPointer()), + Label: o.Name.ValueString(), + Scope: o.Scope.ValueString(), } } -func (o *GroupGenerator) LoadApiData(_ context.Context, in *apstra.FreeformGroupGeneratorData) { +func (o *GroupGenerator) LoadApiData(_ context.Context, in *apstra.FreeformGroupGeneratorData, _ *diag.Diagnostics) { + o.GroupId = types.StringPointerValue((*string)(in.ParentId)) o.Name = types.StringValue(in.Label) o.Scope = types.StringValue(in.Scope) } diff --git a/apstra/resource_freeform_group_generator_integration_test.go b/apstra/resource_freeform_group_generator_integration_test.go deleted file mode 100644 index 59fc50bb..00000000 --- a/apstra/resource_freeform_group_generator_integration_test.go +++ /dev/null @@ -1,125 +0,0 @@ -//go:build integration - -package tfapstra_test - -import ( - "context" - "fmt" - "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 ( - resourceFreeformGroupGeneratorHcl = ` -resource %q %q { - blueprint_id = %q - name = %q - scope = %q - } -` -) - -type resourceFreeformGroupGenerator struct { - blueprintId string - name string - scope string -} - -func (o resourceFreeformGroupGenerator) render(rType, rName string) string { - return fmt.Sprintf(resourceFreeformGroupGeneratorHcl, - rType, rName, - o.blueprintId, - o.name, - o.scope, - ) -} - -func (o resourceFreeformGroupGenerator) 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", "scope", o.scope) - - return result -} - -func TestResourceFreeformGroupGenerator(t *testing.T) { - ctx := context.Background() - client := testutils.GetTestClient(t, ctx) - apiVersion := version.Must(version.NewVersion(client.ApiVersion())) - - // create a blueprint and a group... - bp, _ := testutils.FfBlueprintC(t, ctx) - - type testStep struct { - config resourceFreeformGroupGenerator - } - - type testCase struct { - apiVersionConstraints version.Constraints - steps []testStep - } - - testCases := map[string]testCase{ - "start_asn_resource_generator": { - steps: []testStep{ - { - config: resourceFreeformGroupGenerator{ - blueprintId: bp.Id().String(), - name: acctest.RandString(6), - scope: "node('system', name='target')", - }, - }, - { - config: resourceFreeformGroupGenerator{ - blueprintId: bp.Id().String(), - name: acctest.RandString(6), - scope: "node('system', deploy_mode='deploy', name='target')", - }, - }, - }, - }, - } - - resourceType := tfapstra.ResourceName(ctx, &tfapstra.ResourceFreeformGroupGenerator) - - 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 ------%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, - }) - }) - } -} diff --git a/apstra/resource_freeform_link_integration_test.go b/apstra/resource_freeform_link_integration_test.go index eb7f5ba5..ded3fbed 100644 --- a/apstra/resource_freeform_link_integration_test.go +++ b/apstra/resource_freeform_link_integration_test.go @@ -76,7 +76,6 @@ func (o resourceFreeformLink) testChecks(t testing.TB, rType, rName string, ipAl // required and computed attributes can always be checked result.append(t, "TestCheckResourceAttrSet", "id") - result.append(t, "TestCheckResourceAttr", "type", apstra.FFLinkTypeEthernet.String()) result.append(t, "TestCheckNoResourceAttr", "aggregate_link_id") result.append(t, "TestCheckResourceAttr", "blueprint_id", o.blueprintId) result.append(t, "TestCheckResourceAttr", "name", o.name) diff --git a/apstra/resource_freeform_resource_generator.go b/apstra/resource_freeform_resource_generator.go index 8e761d97..aa00cb30 100644 --- a/apstra/resource_freeform_resource_generator.go +++ b/apstra/resource_freeform_resource_generator.go @@ -223,7 +223,7 @@ func (o *resourceFreeformResourceGenerator) Delete(ctx context.Context, req reso return } - // Delete Config Template by calling API + // Delete Resource Generator by calling API err = bp.DeleteResourceGenerator(ctx, apstra.ObjectId(state.Id.ValueString())) if err != nil { if utils.IsApstra404(err) { diff --git a/apstra/resource_freeform_group_generator.go b/apstra/resource_freeform_resource_group_generator.go similarity index 95% rename from apstra/resource_freeform_group_generator.go rename to apstra/resource_freeform_resource_group_generator.go index e4eb2792..f51eaa3e 100644 --- a/apstra/resource_freeform_group_generator.go +++ b/apstra/resource_freeform_resource_group_generator.go @@ -24,7 +24,7 @@ type resourceFreeformGroupGenerator struct { } func (o *resourceFreeformGroupGenerator) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_freeform_group_generator" + resp.TypeName = req.ProviderTypeName + "_freeform_resource_group_generator" } func (o *resourceFreeformGroupGenerator) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { @@ -67,7 +67,7 @@ func (o *resourceFreeformGroupGenerator) Create(ctx context.Context, req resourc } // Convert the plan into an API Request - request := plan.Request(ctx) + request := plan.Request(ctx, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } @@ -79,9 +79,8 @@ func (o *resourceFreeformGroupGenerator) Create(ctx context.Context, req resourc return } - plan.Id = types.StringValue(id.String()) - // set state + plan.Id = types.StringValue(id.String()) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } @@ -113,7 +112,7 @@ func (o *resourceFreeformGroupGenerator) Read(ctx context.Context, req resource. return } - state.LoadApiData(ctx, api.Data) + state.LoadApiData(ctx, api.Data, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } @@ -151,7 +150,7 @@ func (o *resourceFreeformGroupGenerator) Update(ctx context.Context, req resourc } // Convert the plan into an API Request - request := plan.Request(ctx) + request := plan.Request(ctx, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } @@ -193,7 +192,7 @@ func (o *resourceFreeformGroupGenerator) Delete(ctx context.Context, req resourc return } - // Delete Config Template by calling API + // Delete Resource Generator by calling API err = bp.DeleteGroupGenerator(ctx, apstra.ObjectId(state.Id.ValueString())) if err != nil { if utils.IsApstra404(err) { diff --git a/apstra/resource_freeform_resource_group_generator_integration_test.go b/apstra/resource_freeform_resource_group_generator_integration_test.go new file mode 100644 index 00000000..b451bca8 --- /dev/null +++ b/apstra/resource_freeform_resource_group_generator_integration_test.go @@ -0,0 +1,239 @@ +//go:build integration + +package tfapstra_test + +import ( + "context" + "fmt" + "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" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/stretchr/testify/require" +) + +const ( + resourceFreeformGroupGeneratorHcl = ` +resource %q %q { + blueprint_id = %q + group_id = %s + name = %q + scope = %q + } +` +) + +type resourceFreeformGroupGenerator struct { + blueprintId string + groupId *apstra.ObjectId + name string + scope string +} + +func (o resourceFreeformGroupGenerator) render(rType, rName string) string { + return fmt.Sprintf(resourceFreeformGroupGeneratorHcl, + rType, rName, + o.blueprintId, + stringPtrOrNull(o.groupId), + o.name, + o.scope, + ) +} + +func (o resourceFreeformGroupGenerator) 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", "scope", o.scope) + + if o.groupId == nil { + result.append(t, "TestCheckNoResourceAttr", "group_id") + } else { + result.append(t, "TestCheckResourceAttr", "group_id", o.groupId.String()) + } + + return result +} + +func TestResourceFreeformGroupGenerator(t *testing.T) { + ctx := context.Background() + client := testutils.GetTestClient(t, ctx) + apiVersion := version.Must(version.NewVersion(client.ApiVersion())) + + resourceGroupCount := 2 + + // create a blueprint + bp := testutils.FfBlueprintA(t, ctx) + + var err error + + resourceGroupIds := make([]apstra.ObjectId, resourceGroupCount) + for i := range resourceGroupCount { + resourceGroupIds[i], err = bp.CreateRaGroup(ctx, &apstra.FreeformRaGroupData{Label: acctest.RandString(6)}) + require.NoError(t, err) + } + + type testStep struct { + config resourceFreeformGroupGenerator + } + + type testCase struct { + apiVersionConstraints version.Constraints + steps []testStep + } + + testCases := map[string]testCase{ + "root": { + steps: []testStep{ + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + }, + }, + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + }, + }, + }, + }, + "group": { + steps: []testStep{ + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + groupId: &resourceGroupIds[0], + }, + }, + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + groupId: &resourceGroupIds[0], + }, + }, + }, + }, + "change_group": { + steps: []testStep{ + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + groupId: &resourceGroupIds[0], + }, + }, + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + groupId: &resourceGroupIds[1], + }, + }, + }, + }, + "root_then_group": { + steps: []testStep{ + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + }, + }, + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + groupId: &resourceGroupIds[1], + }, + }, + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + }, + }, + }, + }, + "group_then_root": { + steps: []testStep{ + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + groupId: &resourceGroupIds[1], + }, + }, + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + }, + }, + { + config: resourceFreeformGroupGenerator{ + blueprintId: bp.Id().String(), + name: acctest.RandString(6), + scope: fmt.Sprintf(`node('system', label='%s', name='target')`, acctest.RandString(6)), + groupId: &resourceGroupIds[1], + }, + }, + }, + }, + } + + resourceType := tfapstra.ResourceName(ctx, &tfapstra.ResourceFreeformGroupGenerator) + + 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 ------%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, + }) + }) + } +} diff --git a/apstra/test_helpers_test.go b/apstra/test_helpers_test.go index dd3ea2de..85f63853 100644 --- a/apstra/test_helpers_test.go +++ b/apstra/test_helpers_test.go @@ -56,11 +56,11 @@ func systemIds(ctx context.Context, t *testing.T, bp *apstra.TwoStageL3ClosClien return ids } -func stringPtrOrNull(in *string) string { +func stringPtrOrNull[S ~string](in *S) string { if in == nil { return "null" } - return `"` + *in + `"` + return fmt.Sprintf(`%q`, *in) } func stringOrNull(in string) string { diff --git a/docs/data-sources/freeform_group_generator.md b/docs/data-sources/freeform_resource_group_generator.md similarity index 54% rename from docs/data-sources/freeform_group_generator.md rename to docs/data-sources/freeform_resource_group_generator.md index 66092a82..e00a5790 100644 --- a/docs/data-sources/freeform_group_generator.md +++ b/docs/data-sources/freeform_resource_group_generator.md @@ -1,12 +1,12 @@ --- -page_title: "apstra_freeform_group_generator Data Source - terraform-provider-apstra" +page_title: "apstra_freeform_resource_group_generator Data Source - terraform-provider-apstra" subcategory: "Reference Design: Freeform" description: |- This data source provides details of a specific Freeform Group Generator. At least one optional attribute is required. --- -# apstra_freeform_group_generator (Data Source) +# apstra_freeform_resource_group_generator (Data Source) This data source provides details of a specific Freeform Group Generator. @@ -16,8 +16,8 @@ At least one optional attribute is required. ## Example Usage ```terraform -# This example creates an group Generator within a -# preexisting resource group in a Freeform Blueprint. +# This example creates a Resource Group Generator under a +# Resource Group in a Freeform Blueprint. # # After creating the Group Generator, the data source is invoked to look up # the details. @@ -29,30 +29,31 @@ resource "apstra_freeform_resource_group" "fizz_grp" { } # Create a resource generator scoped to target all systems in the blueprint. -resource "apstra_freeform_group_generator" "test_group_gen" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - name = "test_res_gen" - scope = "node('system', name='target')" +resource "apstra_freeform_resource_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + group_id = apstra_freeform_resource_group.fizz_grp.id + name = "test_res_gen" + scope = "node('system', name='target')" } -# Invoke the resource generator data source -data "apstra_freeform_group_generator" "test_group_gen" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - id = apstra_freeform_group_generator.test_group_gen.id +# Invoke the resource group generator data source +data "apstra_freeform_resource_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + id = apstra_freeform_resource_group_generator.test_group_gen.id } # Output the data source so that it prints on screen -output "test_group_generator_out" { - value = data.apstra_freeform_group_generator.test_group_gen +output "test_resource_group_generator_out" { + value = data.apstra_freeform_resource_group_generator.test_group_gen } # The output looks like this: -#test_group_generator_out = { -# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" -# "id" = "wOQS9qZezzRJCgyvxwY" -# "name" = "test_res_gen" -# "scope" = "node('system', name='target')" -#} +# test_resource_group_generator_out = { +# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" +# "id" = "wOQS9qZezzRJCgyvxwY" +# "name" = "test_res_gen" +# "scope" = "node('system', name='target')" +# } ``` @@ -69,5 +70,6 @@ output "test_group_generator_out" { ### Read-Only +- `group_id` (String) Resource Group the Group Generator belongs to. - `scope` (String) Scope is a graph query which selects target nodes for which Groups should be generated. Example: `node('system', name='target', label=aeq('*prod*'))` diff --git a/docs/resources/freeform_group_generator.md b/docs/resources/freeform_group_generator.md deleted file mode 100644 index 653b8960..00000000 --- a/docs/resources/freeform_group_generator.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -page_title: "apstra_freeform_group_generator Resource - terraform-provider-apstra" -subcategory: "Reference Design: Freeform" -description: |- - This resource creates a Group Generator in a Freeform Blueprint. ---- - -# apstra_freeform_group_generator (Resource) - -This resource creates a Group Generator in a Freeform Blueprint. - - -## Example Usage - -```terraform -# This example creates an group Generator within a -# preexisting resource group in a Freeform Blueprint. -# -# After creating the Group Generator, the data source is invoked to look up -# the details. - -# Create a resource group in a preexisting blueprint. -resource "apstra_freeform_resource_group" "fizz_grp" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - name = "fizz_grp" -} - -# Create a resource generator scoped to target all systems in the blueprint. -resource "apstra_freeform_group_generator" "test_group_gen" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - name = "test_res_gen" - scope = "node('system', name='target')" -} - -# Invoke the resource generator data source -data "apstra_freeform_group_generator" "test_group_gen" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - id = apstra_freeform_group_generator.test_group_gen.id -} - -# Output the data source so that it prints on screen -output "test_group_generator_out" { - value = data.apstra_freeform_group_generator.test_group_gen -} - -# The output looks like this: -#test_group_generator_out = { -# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" -# "id" = "wOQS9qZezzRJCgyvxwY" -# "name" = "test_res_gen" -# "scope" = "node('system', name='target')" -#} -``` - - -## Schema - -### Required - -- `blueprint_id` (String) Apstra Blueprint ID. -- `name` (String) Freeform Group Generator name as shown in the Web UI. -- `scope` (String) Scope is a graph query which selects target nodes for which Groups should be generated. -Example: `node('system', name='target', label=aeq('*prod*'))` - -### Read-Only - -- `id` (String) ID of the Group Generator within the Freeform Blueprint. - - - diff --git a/docs/resources/freeform_resource_group_generator.md b/docs/resources/freeform_resource_group_generator.md new file mode 100644 index 00000000..10effc06 --- /dev/null +++ b/docs/resources/freeform_resource_group_generator.md @@ -0,0 +1,75 @@ +--- +page_title: "apstra_freeform_resource_group_generator Resource - terraform-provider-apstra" +subcategory: "Reference Design: Freeform" +description: |- + This resource creates a Group Generator in a Freeform Blueprint. +--- + +# apstra_freeform_resource_group_generator (Resource) + +This resource creates a Group Generator in a Freeform Blueprint. + + +## Example Usage + +```terraform +# This example creates a Resource Group Generator under a +# Resource Group in a Freeform Blueprint. +# +# After creating the Group Generator, the data source is invoked to look up +# the details. + +# Create a resource group in a preexisting blueprint. +resource "apstra_freeform_resource_group" "fizz_grp" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "fizz_grp" +} + +# Create a resource generator scoped to target all systems in the blueprint. +resource "apstra_freeform_resource_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + group_id = apstra_freeform_resource_group.fizz_grp.id + name = "test_res_gen" + scope = "node('system', name='target')" +} + +# Invoke the resource group generator data source +data "apstra_freeform_resource_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + id = apstra_freeform_resource_group_generator.test_group_gen.id +} + +# Output the data source so that it prints on screen +output "test_resource_group_generator_out" { + value = data.apstra_freeform_resource_group_generator.test_group_gen +} + +# The output looks like this: +# test_resource_group_generator_out = { +# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" +# "id" = "wOQS9qZezzRJCgyvxwY" +# "name" = "test_res_gen" +# "scope" = "node('system', name='target')" +# } +``` + + +## Schema + +### Required + +- `blueprint_id` (String) Apstra Blueprint ID. +- `name` (String) Freeform Group Generator name as shown in the Web UI. +- `scope` (String) Scope is a graph query which selects target nodes for which Group Generators should be generated. +Example: `node('system', name='target', label=aeq('*prod*'))` + +### Optional + +- `group_id` (String) Resource Group the Group Generator belongs to. Omit to create at the `root` level. + +### Read-Only + +- `id` (String) ID of the Group Generator within the Freeform Blueprint. + + + diff --git a/examples/data-sources/apstra_freeform_group_generator/example.tf b/examples/data-sources/apstra_freeform_group_generator/example.tf deleted file mode 100644 index 9a812d61..00000000 --- a/examples/data-sources/apstra_freeform_group_generator/example.tf +++ /dev/null @@ -1,38 +0,0 @@ -# This example creates an group Generator within a -# preexisting resource group in a Freeform Blueprint. -# -# After creating the Group Generator, the data source is invoked to look up -# the details. - -# Create a resource group in a preexisting blueprint. -resource "apstra_freeform_resource_group" "fizz_grp" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - name = "fizz_grp" -} - -# Create a resource generator scoped to target all systems in the blueprint. -resource "apstra_freeform_group_generator" "test_group_gen" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - name = "test_res_gen" - scope = "node('system', name='target')" -} - -# Invoke the resource generator data source -data "apstra_freeform_group_generator" "test_group_gen" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - id = apstra_freeform_group_generator.test_group_gen.id -} - -# Output the data source so that it prints on screen -output "test_group_generator_out" { - value = data.apstra_freeform_group_generator.test_group_gen -} - -# The output looks like this: -#test_group_generator_out = { -# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" -# "id" = "wOQS9qZezzRJCgyvxwY" -# "name" = "test_res_gen" -# "scope" = "node('system', name='target')" -#} - diff --git a/examples/data-sources/apstra_freeform_resource_group_generator/example.tf b/examples/data-sources/apstra_freeform_resource_group_generator/example.tf new file mode 100644 index 00000000..aac9d850 --- /dev/null +++ b/examples/data-sources/apstra_freeform_resource_group_generator/example.tf @@ -0,0 +1,39 @@ +# This example creates a Resource Group Generator under a +# Resource Group in a Freeform Blueprint. +# +# After creating the Group Generator, the data source is invoked to look up +# the details. + +# Create a resource group in a preexisting blueprint. +resource "apstra_freeform_resource_group" "fizz_grp" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "fizz_grp" +} + +# Create a resource generator scoped to target all systems in the blueprint. +resource "apstra_freeform_resource_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + group_id = apstra_freeform_resource_group.fizz_grp.id + name = "test_res_gen" + scope = "node('system', name='target')" +} + +# Invoke the resource group generator data source +data "apstra_freeform_resource_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + id = apstra_freeform_resource_group_generator.test_group_gen.id +} + +# Output the data source so that it prints on screen +output "test_resource_group_generator_out" { + value = data.apstra_freeform_resource_group_generator.test_group_gen +} + +# The output looks like this: +# test_resource_group_generator_out = { +# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" +# "id" = "wOQS9qZezzRJCgyvxwY" +# "name" = "test_res_gen" +# "scope" = "node('system', name='target')" +# } + diff --git a/examples/resources/apstra_freeform_group_generator/example.tf b/examples/resources/apstra_freeform_group_generator/example.tf deleted file mode 100644 index 9a812d61..00000000 --- a/examples/resources/apstra_freeform_group_generator/example.tf +++ /dev/null @@ -1,38 +0,0 @@ -# This example creates an group Generator within a -# preexisting resource group in a Freeform Blueprint. -# -# After creating the Group Generator, the data source is invoked to look up -# the details. - -# Create a resource group in a preexisting blueprint. -resource "apstra_freeform_resource_group" "fizz_grp" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - name = "fizz_grp" -} - -# Create a resource generator scoped to target all systems in the blueprint. -resource "apstra_freeform_group_generator" "test_group_gen" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - name = "test_res_gen" - scope = "node('system', name='target')" -} - -# Invoke the resource generator data source -data "apstra_freeform_group_generator" "test_group_gen" { - blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" - id = apstra_freeform_group_generator.test_group_gen.id -} - -# Output the data source so that it prints on screen -output "test_group_generator_out" { - value = data.apstra_freeform_group_generator.test_group_gen -} - -# The output looks like this: -#test_group_generator_out = { -# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" -# "id" = "wOQS9qZezzRJCgyvxwY" -# "name" = "test_res_gen" -# "scope" = "node('system', name='target')" -#} - diff --git a/examples/resources/apstra_freeform_resource_group_generator/example.tf b/examples/resources/apstra_freeform_resource_group_generator/example.tf new file mode 100644 index 00000000..aac9d850 --- /dev/null +++ b/examples/resources/apstra_freeform_resource_group_generator/example.tf @@ -0,0 +1,39 @@ +# This example creates a Resource Group Generator under a +# Resource Group in a Freeform Blueprint. +# +# After creating the Group Generator, the data source is invoked to look up +# the details. + +# Create a resource group in a preexisting blueprint. +resource "apstra_freeform_resource_group" "fizz_grp" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + name = "fizz_grp" +} + +# Create a resource generator scoped to target all systems in the blueprint. +resource "apstra_freeform_resource_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + group_id = apstra_freeform_resource_group.fizz_grp.id + name = "test_res_gen" + scope = "node('system', name='target')" +} + +# Invoke the resource group generator data source +data "apstra_freeform_resource_group_generator" "test_group_gen" { + blueprint_id = "f1b86583-9139-49ed-8a3c-0490253e006e" + id = apstra_freeform_resource_group_generator.test_group_gen.id +} + +# Output the data source so that it prints on screen +output "test_resource_group_generator_out" { + value = data.apstra_freeform_resource_group_generator.test_group_gen +} + +# The output looks like this: +# test_resource_group_generator_out = { +# "blueprint_id" = "f1b86583-9139-49ed-8a3c-0490253e006e" +# "id" = "wOQS9qZezzRJCgyvxwY" +# "name" = "test_res_gen" +# "scope" = "node('system', name='target')" +# } + diff --git a/go.mod b/go.mod index ad04de40..34a0207c 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-20240828195058-e7a28edd78dd + github.com/Juniper/apstra-go-sdk v0.0.0-20240830010414-20bd6839d500 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 6e1e8397..3db95f38 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-20240828195058-e7a28edd78dd h1:aHNvAvImPwBDCFTTC6CaMJt5VvPSQGpfJaRrsScG9zs= -github.com/Juniper/apstra-go-sdk v0.0.0-20240828195058-e7a28edd78dd/go.mod h1:cSUzaIIQzZysIVKgJnt2/jO2EKeAB60Xgbx8yBGwJ8Y= +github.com/Juniper/apstra-go-sdk v0.0.0-20240830010414-20bd6839d500 h1:vvvqUWUrgB9U8wbWmsrOjbJLQm2iMLpEikZfZJtfy4Q= +github.com/Juniper/apstra-go-sdk v0.0.0-20240830010414-20bd6839d500/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=