-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #741 from Juniper/735-freeform-resource-datasource…
…-for-resource-groups 735 freeform resource datasource for resource groups
- Loading branch information
Showing
12 changed files
with
884 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package blueprint | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"regexp" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" | ||
|
||
"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 FreeformResourceGroup 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 FreeformResourceGroup) 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 Freeform 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 a " + | ||
"parent/child relationship. If this is a top-level (root) node, then `parent_id` will be `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, if any.", | ||
Computed: true, | ||
}, | ||
} | ||
} | ||
|
||
func (o FreeformResourceGroup) 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: "ID of the parent Freeform Resource Allocation Group, if this group is to be nested.", | ||
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 JSON-encoded 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 via resource declaration were not generated.", | ||
Computed: true, | ||
}, | ||
} | ||
} | ||
|
||
func (o *FreeformResourceGroup) Request(_ context.Context, _ *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 *FreeformResourceGroup) LoadApiData(_ context.Context, in *apstra.FreeformRaGroupData, _ *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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = &dataSourceFreeformResourceGroup{} | ||
_ datasourceWithSetFfBpClientFunc = &dataSourceFreeformResourceGroup{} | ||
) | ||
|
||
type dataSourceFreeformResourceGroup struct { | ||
getBpClientFunc func(context.Context, string) (*apstra.FreeformClient, error) | ||
} | ||
|
||
func (o *dataSourceFreeformResourceGroup) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { | ||
resp.TypeName = req.ProviderTypeName + "_freeform_resource_group" | ||
} | ||
|
||
func (o *dataSourceFreeformResourceGroup) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { | ||
configureDataSource(ctx, o, req, resp) | ||
} | ||
|
||
func (o *dataSourceFreeformResourceGroup) 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.FreeformResourceGroup{}.DataSourceAttributes(), | ||
} | ||
} | ||
|
||
func (o *dataSourceFreeformResourceGroup) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { | ||
var config blueprint.FreeformResourceGroup | ||
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 *dataSourceFreeformResourceGroup) setBpClientFunc(f func(context.Context, string) (*apstra.FreeformClient, error)) { | ||
o.getBpClientFunc = f | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.