Skip to content

Commit

Permalink
Merge pull request #830 from Juniper/829-implement-freeform-group-gen…
Browse files Browse the repository at this point in the history
…erators

implement freeform group generators with tests
  • Loading branch information
bwJuniper authored Aug 30, 2024
2 parents 71c996c + 05a9a4b commit 5682a78
Show file tree
Hide file tree
Showing 12 changed files with 900 additions and 3 deletions.
101 changes: 101 additions & 0 deletions apstra/data_source_freeform_resource_group_generator.go
Original file line number Diff line number Diff line change
@@ -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_resource_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, &resp.Diagnostics)
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
}
1 change: 1 addition & 0 deletions apstra/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var (
ResourceFreeformBlueprint = resourceFreeformBlueprint{}
ResourceFreeformConfigTemplate = resourceFreeformConfigTemplate{}
ResourceFreeformDeviceProfile = resourceFreeformDeviceProfile{}
ResourceFreeformGroupGenerator = resourceFreeformGroupGenerator{}
ResourceFreeformLink = resourceFreeformLink{}
ResourceFreeformPropertySet = resourceFreeformPropertySet{}
ResourceFreeformResourceGenerator = resourceFreeformResourceGenerator{}
Expand Down
114 changes: 114 additions & 0 deletions apstra/freeform/resource_group_generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
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"
)

type GroupGenerator struct {
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{
"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"),
}...),
},
},
"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,
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{
"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()},
},
"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.",
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 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, _ *diag.Diagnostics) *apstra.FreeformGroupGeneratorData {
return &apstra.FreeformGroupGeneratorData{
ParentId: (*apstra.ObjectId)(o.GroupId.ValueStringPointer()),
Label: o.Name.ValueString(),
Scope: o.Scope.ValueString(),
}
}

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)
}
2 changes: 2 additions & 0 deletions apstra/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{} },
Expand Down Expand Up @@ -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{} },
Expand Down
2 changes: 1 addition & 1 deletion apstra/resource_freeform_resource_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit 5682a78

Please sign in to comment.