Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement freeform group generators with tests #830

Merged
merged 7 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading