Skip to content

Commit

Permalink
add routing zone constraint data sources
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismarget-j committed Dec 20, 2024
1 parent 23b9919 commit 80365a3
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 10 deletions.
96 changes: 93 additions & 3 deletions apstra/blueprint/routing_zone_constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package blueprint
import (
"context"
"fmt"
"strings"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/apstra-go-sdk/apstra/enum"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
apstravalidator "github.com/Juniper/terraform-provider-apstra/apstra/validator"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"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"
Expand All @@ -17,7 +20,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"strings"
)

type DatacenterRoutingZoneConstraint struct {
Expand All @@ -29,7 +31,7 @@ type DatacenterRoutingZoneConstraint struct {
Constraints types.Set `tfsdk:"constraints"`
}

func (o DatacenterRoutingZoneConstraint) DatasourceAttributes() map[string]dataSourceSchema.Attribute {
func (o DatacenterRoutingZoneConstraint) DataSourceAttributes() map[string]dataSourceSchema.Attribute {
return map[string]dataSourceSchema.Attribute{
"id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Apstra graph node ID. Required when `name` is omitted.",
Expand Down Expand Up @@ -81,6 +83,51 @@ func (o DatacenterRoutingZoneConstraint) DatasourceAttributes() map[string]dataS
}
}

func (o DatacenterRoutingZoneConstraint) DataSourceFilterAttributes() map[string]dataSourceSchema.Attribute {
return map[string]dataSourceSchema.Attribute{
"id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Not applicable in filter context. Ignore.",
Computed: true,
},
"blueprint_id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Not applicable in filter context. Ignore.",
Computed: true,
},
"name": dataSourceSchema.StringAttribute{
MarkdownDescription: "Name displayed in the Apstra web UI.",
Optional: true,
},
"max_count_constraint": dataSourceSchema.Int64Attribute{
MarkdownDescription: "The maximum number of Routing Zones that the Application Point can be part of.",
Optional: true,
},
"routing_zones_list_constraint": dataSourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf(
"Routing Zone constraint mode. One of: %s.", strings.Join(
[]string{
"`" + utils.StringersToFriendlyString(enum.RoutingZoneConstraintModeAllow) + "`",
"`" + utils.StringersToFriendlyString(enum.RoutingZoneConstraintModeDeny) + "`",
"`" + utils.StringersToFriendlyString(enum.RoutingZoneConstraintModeNone) + "`",
}, ", "),
),
Optional: true,
Validators: []validator.String{stringvalidator.OneOf( // validated b/c this runs through rosetta
utils.StringersToFriendlyString(enum.RoutingZoneConstraintModeAllow),
utils.StringersToFriendlyString(enum.RoutingZoneConstraintModeDeny),
utils.StringersToFriendlyString(enum.RoutingZoneConstraintModeNone),
)},
},
"constraints": dataSourceSchema.SetAttribute{
MarkdownDescription: "Set of Routing Zone IDs. All Routing Zones supplied here are used to match the " +
"Routing Zone Constraint, but a matching Routing Zone Constraintmay have additional Security Zones " +
"not enumerated in this set.",
Optional: true,
ElementType: types.StringType,
Validators: []validator.Set{setvalidator.ValueStringsAre(stringvalidator.LengthAtLeast(1))},
},
}
}

func (o DatacenterRoutingZoneConstraint) ResourceAttributes() map[string]resourceSchema.Attribute {
return map[string]resourceSchema.Attribute{
"id": resourceSchema.StringAttribute{
Expand Down Expand Up @@ -163,7 +210,7 @@ func (o DatacenterRoutingZoneConstraint) Request(ctx context.Context, diags *dia
return &result
}

func (o *DatacenterRoutingZoneConstraint) LoadApiData(ctx context.Context, in *apstra.RoutingZoneConstraintData, diags *diag.Diagnostics) {
func (o *DatacenterRoutingZoneConstraint) LoadApiData(ctx context.Context, in apstra.RoutingZoneConstraintData, diags *diag.Diagnostics) {
o.Name = types.StringValue(in.Label)
if in.MaxRoutingZones == nil {
o.MaxCountConstraint = types.Int64Null()
Expand All @@ -173,3 +220,46 @@ func (o *DatacenterRoutingZoneConstraint) LoadApiData(ctx context.Context, in *a
o.RoutingZonesListConstraint = types.StringValue(in.Mode.String())
o.Constraints = utils.SetValueOrNull(ctx, types.StringType, in.RoutingZoneIds, diags)
}

func (o DatacenterRoutingZoneConstraint) Query(ctx context.Context, rzcResultName string, diags *diag.Diagnostics) *apstra.MatchQuery {
rzcNameAttr := apstra.QEEAttribute{Key: "name", Value: apstra.QEStringVal(rzcResultName)}
nodeAttributes := []apstra.QEEAttribute{rzcNameAttr, apstra.NodeTypeRoutingZoneConstraint.QEEAttribute()}

// add the name to the match, if any
if !o.Name.IsNull() {
nodeAttributes = append(nodeAttributes, apstra.QEEAttribute{Key: "label", Value: apstra.QEStringVal(o.Name.ValueString())})
}

// add the max to the match, if any
if !o.MaxCountConstraint.IsNull() {
nodeAttributes = append(nodeAttributes, apstra.QEEAttribute{Key: "max_count_constraint", Value: apstra.QEIntVal(o.MaxCountConstraint.ValueInt64())})
}

// add the mode to the match, if any
if !o.RoutingZonesListConstraint.IsNull() {
var rzcm enum.RoutingZoneConstraintMode
err := utils.ApiStringerFromFriendlyString(&rzcm, o.RoutingZonesListConstraint.ValueString())
if err != nil {
diags.AddError(fmt.Sprintf("failed converting %s to API type", o.RoutingZonesListConstraint), err.Error())
return nil
}
nodeAttributes = append(nodeAttributes, apstra.QEEAttribute{Key: "routing_zones_list_constraint", Value: apstra.QEStringVal(rzcm.String())})
}

query := new(apstra.MatchQuery).Match(new(apstra.PathQuery).Node(nodeAttributes))

var rzIds []string
diags.Append(o.Constraints.ElementsAs(ctx, &rzIds, false)...)
if diags.HasError() {
return nil
}

for _, rzId := range rzIds {
query.Match(new(apstra.PathQuery).
Node([]apstra.QEEAttribute{rzcNameAttr}).
Out([]apstra.QEEAttribute{apstra.RelationshipTypeConstraint.QEEAttribute()}).
Node([]apstra.QEEAttribute{apstra.NodeTypeSecurityZone.QEEAttribute(), {Key: "id", Value: apstra.QEStringVal(rzId)}}))
}

return query
}
102 changes: 102 additions & 0 deletions apstra/data_source_datacenter_routing_zone_constraint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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 = &dataSourceDatacenterRoutingZoneConstraint{}
_ datasourceWithSetDcBpClientFunc = &dataSourceDatacenterRoutingZoneConstraint{}
)

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

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

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

func (o *dataSourceDatacenterRoutingZoneConstraint) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: docCategoryDatacenter + "This resource returns details of a Routing Zone Constraint within a Datacenter Blueprint.\n\n" +
"At least one optional attribute is required.",
Attributes: blueprint.DatacenterRoutingZoneConstraint{}.DataSourceAttributes(),
}
}

func (o *dataSourceDatacenterRoutingZoneConstraint) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
// Retrieve values from config.
var config blueprint.DatacenterRoutingZoneConstraint
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}

// get a client for the datacenter reference design
bp, err := o.getBpClientFunc(ctx, config.BlueprintId.ValueString())
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddError(fmt.Sprintf(errBpNotFoundSummary, config.BlueprintId), err.Error())
return
}
resp.Diagnostics.AddError(fmt.Sprintf(errBpClientCreateSummary, config.BlueprintId), err.Error())
return
}

var api *apstra.RoutingZoneConstraint
switch {
case !config.Id.IsNull():
api, err = bp.GetRoutingZoneConstraint(ctx, apstra.ObjectId(config.Id.ValueString()))
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("id"),
"Routing Zone not found",
fmt.Sprintf("Routing Zone Constraint with ID %s not found", config.Id))
return
}
case !config.Name.IsNull():
api, err = bp.GetRoutingZoneConstraintByName(ctx, config.Name.ValueString())
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("name"),
"Routing Zone not found",
fmt.Sprintf("Routing Zone Constraint with Name %s not found", config.Name))
return
}
}
if err != nil {
resp.Diagnostics.AddError("failed reading Routing Zone Constraint", err.Error())
return
}
if api == nil || api.Data == nil {
resp.Diagnostics.AddError("failed reading Routing Zone Constraint", "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 *dataSourceDatacenterRoutingZoneConstraint) setBpClientFunc(f func(context.Context, string) (*apstra.TwoStageL3ClosClient, error)) {
o.getBpClientFunc = f
}
Loading

0 comments on commit 80365a3

Please sign in to comment.