Skip to content

Commit

Permalink
Merge pull request #760 from Juniper/754-freeform-allocation-groups
Browse files Browse the repository at this point in the history
754 freeform allocation groups
  • Loading branch information
bwJuniper authored Aug 1, 2024
2 parents cee3ab9 + bcdbea1 commit da36c24
Show file tree
Hide file tree
Showing 17 changed files with 1,076 additions and 6 deletions.
134 changes: 134 additions & 0 deletions apstra/blueprint/freeform_alloc_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package blueprint

import (
"context"
"fmt"
"regexp"
"strings"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
"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 FreeformAllocGroup struct {
BlueprintId types.String `tfsdk:"blueprint_id"`
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Type types.String `tfsdk:"type"`
PoolIds types.Set `tfsdk:"pool_ids"`
}

func (o FreeformAllocGroup) 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 Allocation Group lives.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Populate this field to look up the Allocation Group 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 the Allocation Group by Name. Required when `id` is omitted.",
Optional: true,
Computed: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"type": dataSourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("type of the Allocation Group, must be one of :\n - `" +
strings.Join(utils.AllResourcePoolTypes(), "`\n - `") + "`\n"),
Computed: true,
},
"pool_ids": dataSourceSchema.SetAttribute{
MarkdownDescription: "IDs of Resource Pools assigned to the allocation group",
Computed: true,
ElementType: types.StringType,
},
}
}

func (o FreeformAllocGroup) 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 Freeform Allocation Group.",
Computed: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
},
"name": resourceSchema.StringAttribute{
MarkdownDescription: "Freeform Allocation Group name as shown in the Web UI.",
Required: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
Validators: []validator.String{
stringvalidator.RegexMatches(regexp.MustCompile("^[a-zA-Z0-9.-_]+$"),
"name may consist only of the following characters : a-zA-Z0-9.-_"),
},
},
"type": resourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("type of the Allocation Group, must be one of :\n - `" +
strings.Join(utils.AllResourcePoolTypes(), "`\n - `") + "`\n"),
Required: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
Validators: []validator.String{stringvalidator.OneOf(utils.AllFFResourceTypes()...)},
},
"pool_ids": resourceSchema.SetAttribute{
MarkdownDescription: "IDs of Resource Pools assigned to the allocation group",
ElementType: types.StringType,
Required: true,
Validators: []validator.Set{setvalidator.SizeAtLeast(1)},
},
}
}

func (o *FreeformAllocGroup) Request(ctx context.Context, diags *diag.Diagnostics) *apstra.FreeformAllocGroupData {
// unpack
var allocGroupType apstra.ResourcePoolType
err := utils.ApiStringerFromFriendlyString(&allocGroupType, o.Type.ValueString())
if err != nil {
diags.AddError(fmt.Sprintf("error parsing type %q", o.Type.ValueString()), err.Error())
}

var poolIds []apstra.ObjectId
diags.Append(o.PoolIds.ElementsAs(ctx, &poolIds, false)...)
if diags.HasError() {
return nil
}

return &apstra.FreeformAllocGroupData{
Name: o.Name.ValueString(),
Type: allocGroupType,
PoolIds: poolIds,
}
}

func (o *FreeformAllocGroup) LoadApiData(ctx context.Context, in *apstra.FreeformAllocGroupData, diags *diag.Diagnostics) {
// pack
o.Name = types.StringValue(in.Name)
o.Type = types.StringValue(utils.StringersToFriendlyString(in.Type))
o.PoolIds = utils.SetValueOrNull(ctx, types.StringType, in.PoolIds, diags)
}
4 changes: 2 additions & 2 deletions apstra/blueprint/freeform_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ func (o FreeformResource) ResourceAttributes() map[string]resourceSchema.Attribu
},
"type": resourceSchema.StringAttribute{
MarkdownDescription: "type of the Resource, must be one of :\n - `" +
strings.Join(utils.AllResourceTypes(), "`\n - `") + "`\n",
strings.Join(utils.AllFFResourceTypes(), "`\n - `") + "`\n",
Optional: true,
Validators: []validator.String{stringvalidator.OneOf(utils.AllResourceTypes()...)},
Validators: []validator.String{stringvalidator.OneOf(utils.AllFFResourceTypes()...)},
},
"integer_value": resourceSchema.Int64Attribute{
MarkdownDescription: fmt.Sprintf("Value used by integer type resources (`%s`, `%s`, `%s`, `%s`). "+
Expand Down
101 changes: 101 additions & 0 deletions apstra/data_source_freeform_alloc_group.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/blueprint"
"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 = &dataSourceFreeformAllocGroup{}
_ datasourceWithSetFfBpClientFunc = &dataSourceFreeformAllocGroup{}
)

type dataSourceFreeformAllocGroup struct {
getBpClientFunc func(context.Context, string) (*apstra.FreeformClient, error)
}

func (o *dataSourceFreeformAllocGroup) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_freeform_alloc_group"
}

func (o *dataSourceFreeformAllocGroup) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
configureDataSource(ctx, o, req, resp)
}

func (o *dataSourceFreeformAllocGroup) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: docCategoryFreeform + "This data source provides details of a Freeform Allocation Group.\n\n" +
"At least one optional attribute is required.",
Attributes: blueprint.FreeformAllocGroup{}.DataSourceAttributes(),
}
}

func (o *dataSourceFreeformAllocGroup) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config blueprint.FreeformAllocGroup
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.FreeformAllocGroup
switch {
case !config.Id.IsNull():
api, err = bp.GetAllocGroup(ctx, apstra.ObjectId(config.Id.ValueString()))
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("id"),
"Freeform Allocation Group not found",
fmt.Sprintf("Freeform Allocation Group with ID %s not found", config.Id))
return
}
case !config.Name.IsNull():
api, err = bp.GetAllocGroupByName(ctx, config.Name.ValueString())
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("name"),
"Freeform Allocation Group not found",
fmt.Sprintf("Freeform Allocation Group with Name %s not found", config.Name))
return
}
}
if err != nil {
resp.Diagnostics.AddError("failed reading Freeform Allocation Group", err.Error())
return
}
if api.Data == nil {
resp.Diagnostics.AddError("failed reading Freeform Allocation Group", "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 *dataSourceFreeformAllocGroup) 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 @@ -19,6 +19,7 @@ var (
ResourceFreeformPropertySet = resourceFreeformPropertySet{}
ResourceFreeformResourceGroup = resourceFreeformResourceGroup{}
ResourceFreeformResource = resourceFreeformResource{}
ResourceFreeformAllocGroup = resourceFreeformAllocGroup{}
ResourceFreeformSystem = resourceFreeformSystem{}
ResourceIntegerPool = resourceIntegerPool{}
ResourceIpv4Pool = resourceIpv4Pool{}
Expand Down
2 changes: 2 additions & 0 deletions apstra/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
func() datasource.DataSource { return &dataSourceDatacenterVirtualNetwork{} },
func() datasource.DataSource { return &dataSourceDatacenterVirtualNetworks{} },
func() datasource.DataSource { return &dataSourceDeviceConfig{} },
func() datasource.DataSource { return &dataSourceFreeformAllocGroup{} },
func() datasource.DataSource { return &dataSourceFreeformBlueprint{} },
func() datasource.DataSource { return &dataSourceFreeformConfigTemplate{} },
func() datasource.DataSource { return &dataSourceFreeformLink{} },
Expand Down Expand Up @@ -611,6 +612,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
func() resource.Resource { return &resourceDatacenterIpLinkAddressing{} },
func() resource.Resource { return &resourceDatacenterVirtualNetwork{} },
func() resource.Resource { return &resourceDeviceAllocation{} },
func() resource.Resource { return &resourceFreeformAllocGroup{} },
func() resource.Resource { return &resourceFreeformBlueprint{} },
func() resource.Resource { return &resourceFreeformConfigTemplate{} },
func() resource.Resource { return &resourceFreeformLink{} },
Expand Down
Loading

0 comments on commit da36c24

Please sign in to comment.