Skip to content

Commit

Permalink
Merge pull request #135 from Juniper/datasource_datacenter_property_sets
Browse files Browse the repository at this point in the history
Datasource and Resource for datacenter property sets
  • Loading branch information
rajagopalans authored Jun 23, 2023
2 parents 220e145 + 25890ab commit f4ad91f
Show file tree
Hide file tree
Showing 17 changed files with 1,062 additions and 1 deletion.
125 changes: 125 additions & 0 deletions apstra/blueprint/datacenter_property_set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package blueprint

import (
"context"
"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
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/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"terraform-provider-apstra/apstra/utils"
)

type DatacenterPropertySet struct {
BlueprintId types.String `tfsdk:"blueprint_id"`
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Data types.String `tfsdk:"data"`
Stale types.Bool `tfsdk:"stale"`
Keys types.Set `tfsdk:"keys"`
}

func (o DatacenterPropertySet) AttrTypes() map[string]attr.Type {
return map[string]attr.Type{
"blueprint_id": types.StringType,
"id": types.StringType,
"name": types.StringType,
"data": types.StringType,
"stale": types.BoolType,
"keys": types.SetType{ElemType: types.StringType},
}
}

func (o DatacenterPropertySet) DataSourceAttributes() map[string]dataSourceSchema.Attribute {
return map[string]dataSourceSchema.Attribute{
"blueprint_id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Apstra Blueprint ID. Used to identify " +
"the Blueprint that the Property Set has been imported into.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"id": dataSourceSchema.StringAttribute{
MarkdownDescription: "Populate this field to look up an imported Property Set 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 an imported Property Set by `name`. Required when `id` is omitted.",
Optional: true,
Computed: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"data": dataSourceSchema.StringAttribute{
MarkdownDescription: "A map of values in the Property Set in JSON format",
Computed: true,
},
"keys": dataSourceSchema.SetAttribute{
MarkdownDescription: "List of Keys that have been imported.",
Computed: true,
ElementType: types.StringType,
},
"stale": dataSourceSchema.BoolAttribute{
MarkdownDescription: "Stale as reported in the Web UI.",
Computed: true,
},
}
}

func (o DatacenterPropertySet) ResourceAttributes() map[string]resourceSchema.Attribute {
return map[string]resourceSchema.Attribute{
"blueprint_id": resourceSchema.StringAttribute{
MarkdownDescription: "Apstra Blueprint ID. Used to identify the Blueprint that the Property Set is imported into.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"id": resourceSchema.StringAttribute{
MarkdownDescription: "ID of the Property Set ID to be imported.",
Required: true,
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"name": resourceSchema.StringAttribute{
MarkdownDescription: "Property Set name as shown in the Web UI.",
Computed: true,
},
"keys": resourceSchema.SetAttribute{
MarkdownDescription: "Subset of Keys to import, at least one Key is required.",
Required: true,
ElementType: types.StringType,
Validators: []validator.Set{setvalidator.SizeAtLeast(1)},
},
"data": resourceSchema.StringAttribute{
MarkdownDescription: "A map of values in the Property Set in JSON format.",
Computed: true,
},
"stale": resourceSchema.BoolAttribute{
MarkdownDescription: "Stale as reported in the Web UI.",
Computed: true,
},
}
}

func (o *DatacenterPropertySet) LoadApiData(_ context.Context, in *apstra.TwoStageL3ClosPropertySet, diags *diag.Diagnostics) {
o.Id = types.StringValue(in.Id.String())
o.Name = types.StringValue(in.Label)
o.Data = types.StringValue(string(in.Values))
o.Stale = types.BoolValue(in.Stale)
keys, err := utils.GetKeysFromJSON(o.Data)
if err != nil {
diags.AddError("Error parsing Keys from API response", err.Error())
}
o.Keys = types.SetValueMust(types.StringType, keys)
}
106 changes: 106 additions & 0 deletions apstra/data_source_datacenter_property_set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package tfapstra

import (
"context"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"terraform-provider-apstra/apstra/blueprint"
"terraform-provider-apstra/apstra/utils"
)

var _ datasource.DataSourceWithConfigure = &dataSourceDatacenterPropertySet{}

type dataSourceDatacenterPropertySet struct {
client *apstra.Client
}

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

func (o *dataSourceDatacenterPropertySet) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
o.client = DataSourceGetClient(ctx, req, resp)
}

func (o *dataSourceDatacenterPropertySet) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "This data source provides details of a specific Property Set imported into a Blueprint.\n\n" +
"At least one optional attribute is required. ",
Attributes: blueprint.DatacenterPropertySet{}.DataSourceAttributes(),
}
}

func (o *dataSourceDatacenterPropertySet) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
if o.client == nil {
resp.Diagnostics.AddError(errDataSourceUnconfiguredSummary, errDatasourceUnconfiguredDetail)
return
}

var config blueprint.DatacenterPropertySet
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}

bpClient, err := o.client.NewTwoStageL3ClosClient(ctx, apstra.ObjectId(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.TwoStageL3ClosPropertySet
switch {
case !config.Name.IsNull():
api, err = bpClient.GetPropertySetByName(ctx, config.Name.ValueString())
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("name"),
"DatacenterPropertySet not found",
fmt.Sprintf("DatacenterPropertySet with label %s not found", config.Name))
return
}
resp.Diagnostics.AddAttributeError(
path.Root("name"), "Failed reading DatacenterPropertySet", err.Error(),
)
return
}
case !config.Id.IsNull():
api, err = bpClient.GetPropertySet(ctx, apstra.ObjectId(config.Id.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("id"),
"DatacenterPropertySet not found",
fmt.Sprintf("DatacenterPropertySet with ID %s not found", config.Id))
return
}
resp.Diagnostics.AddAttributeError(
path.Root("name"), "Failed reading DatacenterPropertySet", err.Error(),
)
return
}
default:
resp.Diagnostics.AddError(errInsufficientConfigElements, "neither 'name' nor 'id' set")
return
}

// create new state object
var state blueprint.DatacenterPropertySet
state.BlueprintId = config.BlueprintId
state.LoadApiData(ctx, api, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}

// Set state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
92 changes: 92 additions & 0 deletions apstra/data_source_datacenter_property_sets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package tfapstra

import (
"context"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"terraform-provider-apstra/apstra/utils"
)

var _ datasource.DataSourceWithConfigure = &dataSourceDatacenterPropertySets{}

type dataSourceDatacenterPropertySets struct {
client *apstra.Client
}

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

func (o *dataSourceDatacenterPropertySets) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
o.client = DataSourceGetClient(ctx, req, resp)
}

func (o *dataSourceDatacenterPropertySets) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "This data source returns the ID numbers of all Property Sets in the Blueprint.",
Attributes: map[string]schema.Attribute{
"blueprint_id": schema.StringAttribute{
MarkdownDescription: "Apstra Blueprint ID.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"ids": schema.SetAttribute{
MarkdownDescription: "Set of IDs of Property Sets imported into the Blueprint.",
Computed: true,
ElementType: types.StringType,
},
},
}
}

func (o *dataSourceDatacenterPropertySets) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
if o.client == nil {
resp.Diagnostics.AddError(errDataSourceUnconfiguredSummary, errDatasourceUnconfiguredDetail)
return
}

var config struct {
BlueprintId types.String `tfsdk:"blueprint_id"`
Ids types.Set `tfsdk:"ids"`
}

// get the configuration
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}

bpClient, err := o.client.NewTwoStageL3ClosClient(ctx, apstra.ObjectId(config.BlueprintId.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddError(fmt.Sprintf("blueprint %s not found",
config.BlueprintId), err.Error())
} else {
resp.Diagnostics.AddError("failed creating blueprint client", err.Error())
}
return
}

// read all PropertySets from the API because there's no ID-list-only API option
propertySets, err := bpClient.GetAllPropertySets(ctx)
if err != nil {
resp.Diagnostics.AddError("failed retrieving property sets", err.Error())
return
}

// organize the property set IDs into a set, shove 'em into the "config" object
psIds := make([]attr.Value, len(propertySets))
for i, propertySet := range propertySets {
psIds[i] = types.StringValue(propertySet.Id.String())
}
config.Ids = types.SetValueMust(types.StringType, psIds)

// set state
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
}
3 changes: 3 additions & 0 deletions apstra/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
func() datasource.DataSource { return &dataSourceConfiglet{} },
func() datasource.DataSource { return &dataSourceConfiglets{} },
func() datasource.DataSource { return &dataSourceDatacenterBlueprint{} },
func() datasource.DataSource { return &dataSourceDatacenterPropertySet{} },
func() datasource.DataSource { return &dataSourceDatacenterPropertySets{} },
func() datasource.DataSource { return &dataSourceDatacenterRoutingZone{} },
func() datasource.DataSource { return &dataSourceDatacenterRoutingZones{} },
func() datasource.DataSource { return &dataSourceDatacenterSystemNode{} },
Expand Down Expand Up @@ -336,6 +338,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
func() resource.Resource { return &resourceConfiglet{} },
func() resource.Resource { return &resourceDatacenterBlueprint{} },
func() resource.Resource { return &resourceDatacenterGenericSystem{} },
func() resource.Resource { return &resourceDatacenterPropertySet{} },
func() resource.Resource { return &resourceDatacenterRoutingZone{} },
func() resource.Resource { return &resourceDatacenterRoutingPolicy{} },
func() resource.Resource { return &resourceDatacenterVirtualNetwork{} },
Expand Down
Loading

0 comments on commit f4ad91f

Please sign in to comment.