Skip to content

Commit

Permalink
fixed ra_group items, examples, make docs
Browse files Browse the repository at this point in the history
  • Loading branch information
bwJuniper committed Jul 22, 2024
1 parent 251dfc0 commit 30c49a0
Show file tree
Hide file tree
Showing 12 changed files with 879 additions and 3 deletions.
167 changes: 167 additions & 0 deletions apstra/blueprint/freeform_ra_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package blueprint

import (
"context"
"encoding/json"
"fmt"
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"regexp"

"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"
)

//{
// "parent_id": "string",
// "label": "string",
// "tags": [
// "string"
// ],
// "data": {}
//}

type FreeformRaGroup struct {
BlueprintId types.String `tfsdk:"blueprint_id"`
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
ParentId types.String `tfsdk:"parent_id"`
Tags types.Set `tfsdk:"tags"`
Data jsontypes.Normalized `tfsdk:"data"`
GeneratorId types.String `tfsdk:"generator_id"`
}

func (o FreeformRaGroup) 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 Resource Allocation Group lives.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Populate this field to look up the Freeform 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)},
},
"parent_id": dataSourceSchema.StringAttribute{
MarkdownDescription: "ID of the group node that is present as a parent of the current one in " +
"parent/children relationship." +
" If group is a top-level one, then 'parent_id' is equal to None/null.",
Computed: true,
},
"tags": dataSourceSchema.SetAttribute{
MarkdownDescription: "Set of Tag labels",
ElementType: types.StringType,
Computed: true,
},
"data": dataSourceSchema.StringAttribute{
MarkdownDescription: "Arbitrary key-value mapping that is useful in a context of this group. " +
"For example, you can store some VRF-related data there or add properties that are useful " +
"only in context of resource allocation, but not systems or interfaces.",
Computed: true,
CustomType: jsontypes.NormalizedType{},
},
"generator_id": dataSourceSchema.StringAttribute{
MarkdownDescription: "ID of the group generator that created the group.",
Computed: true,
},
}
}

func (o FreeformRaGroup) 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 Resource Allocation Group.",
Computed: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
},
"name": resourceSchema.StringAttribute{
MarkdownDescription: "Freeform Resource Allocation Group 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.-_")},
},
"parent_id": resourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("Type of the System. Must be one of `%s` or `%s`", apstra.SystemTypeInternal, apstra.SystemTypeExternal),
Optional: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"tags": resourceSchema.SetAttribute{
MarkdownDescription: "Set of Tag labels",
ElementType: types.StringType,
Optional: true,
Validators: []validator.Set{setvalidator.SizeAtLeast(1)},
},
"data": resourceSchema.StringAttribute{
MarkdownDescription: "Arbitrary key-value mapping that is useful in a context of this group. " +
"For example, you can store some VRF-related data there or add properties that are useful" +
" only in context of resource allocation, but not systems or interfaces. ",
Optional: true,
Computed: true,
Default: stringdefault.StaticString("{}"),
CustomType: jsontypes.NormalizedType{},
},
"generator_id": resourceSchema.StringAttribute{
MarkdownDescription: "ID of the Generator that created Resource Allocation Group, " +
"always `null` because groups created with this resource were not generated.",
Computed: true,
},
}
}

func (o *FreeformRaGroup) Request(ctx context.Context, diags *diag.Diagnostics) *apstra.FreeformRaGroupData {
var tags []string
diags.Append(o.Tags.ElementsAs(ctx, &tags, false)...)
if diags.HasError() {
return nil
}

return &apstra.FreeformRaGroupData{
ParentId: (*apstra.ObjectId)(o.ParentId.ValueStringPointer()),
Label: o.Name.ValueString(),
Tags: tags,
Data: json.RawMessage(o.Data.ValueString()),
GeneratorId: (*apstra.ObjectId)(o.GeneratorId.ValueStringPointer()),
}
}

func (o *FreeformRaGroup) LoadApiData(ctx context.Context, in *apstra.FreeformRaGroupData, diags *diag.Diagnostics) {
o.Name = types.StringValue(in.Label)
if in.ParentId != nil {
o.ParentId = types.StringValue(string(*in.ParentId))
}

o.Data = jsontypes.NewNormalizedValue(string(in.Data))
o.Tags = utils.SetValueOrNull(ctx, types.StringType, in.Tags, diags) // safe to ignore diagnostic here
o.GeneratorId = types.StringPointerValue((*string)(in.GeneratorId))
}
101 changes: 101 additions & 0 deletions apstra/datasource_freeform_ra_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 = &dataSourceFreeformRaGroup{}
_ datasourceWithSetFfBpClientFunc = &dataSourceFreeformRaGroup{}
)

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

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

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

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

func (o *dataSourceFreeformRaGroup) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config blueprint.FreeformRaGroup
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.FreeformRaGroup
switch {
case !config.Id.IsNull():
api, err = bp.GetRaGroup(ctx, apstra.ObjectId(config.Id.ValueString()))
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("id"),
"Freeform Resource Allocation Group not found",
fmt.Sprintf("Freeform Resource Allocation Group with ID %s not found", config.Id))
return
}
case !config.Name.IsNull():
api, err = bp.GetRaGroupByName(ctx, config.Name.ValueString())
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("name"),
"Freeform Resource Allocation Group not found",
fmt.Sprintf("Freeform resource allocation group with Name %s not found", config.Name))
return
}
}
if err != nil {
resp.Diagnostics.AddError("failed reading Freeform Resource Allocation Group", err.Error())
return
}
if api.Data == nil {
resp.Diagnostics.AddError("failed reading Freeform Resource 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 *dataSourceFreeformRaGroup) 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 @@ -14,6 +14,7 @@ var (
ResourceFreeformLink = resourceFreeformLink{}
ResourceFreeformSystem = resourceFreeformSystem{}
ResourceFreeformPropertySet = resourceFreeformPropertySet{}
ResourceFreeformRaGroup = resourceFreeformRaGroup{}
ResourceIpv4Pool = resourceIpv4Pool{}
ResourceTemplatePodBased = resourceTemplatePodBased{}
ResourceTemplateCollapsed = resourceTemplateCollapsed{}
Expand Down
2 changes: 2 additions & 0 deletions apstra/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
func() datasource.DataSource { return &dataSourceFreeformConfigTemplate{} },
func() datasource.DataSource { return &dataSourceFreeformLink{} },
func() datasource.DataSource { return &dataSourceFreeformPropertySet{} },
func() datasource.DataSource { return &dataSourceFreeformRaGroup{} },
func() datasource.DataSource { return &dataSourceFreeformSystem{} },
func() datasource.DataSource { return &dataSourceIntegerPool{} },
func() datasource.DataSource { return &dataSourceInterfacesByLinkTag{} },
Expand Down Expand Up @@ -609,6 +610,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
func() resource.Resource { return &resourceFreeformConfigTemplate{} },
func() resource.Resource { return &resourceFreeformLink{} },
func() resource.Resource { return &resourceFreeformPropertySet{} },
func() resource.Resource { return &resourceFreeformRaGroup{} },
func() resource.Resource { return &resourceFreeformSystem{} },
func() resource.Resource { return &resourceIntegerPool{} },
func() resource.Resource { return &resourceInterfaceMap{} },
Expand Down
Loading

0 comments on commit 30c49a0

Please sign in to comment.