Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deploy_mode attribute to apstra_datacenter_generic_system resource #463

Merged
merged 4 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions apstra/blueprint/datacenter_generic_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand All @@ -36,6 +37,7 @@ type DatacenterGenericSystem struct {
LoopbackIpv4 cidrtypes.IPv4Prefix `tfsdk:"loopback_ipv4"`
LoopbackIpv6 cidrtypes.IPv6Prefix `tfsdk:"loopback_ipv6"`
External types.Bool `tfsdk:"external"`
DeployMode types.String `tfsdk:"deploy_mode"`
}

func (o DatacenterGenericSystem) ResourceAttributes() map[string]resourceSchema.Attribute {
Expand Down Expand Up @@ -109,6 +111,14 @@ func (o DatacenterGenericSystem) ResourceAttributes() map[string]resourceSchema.
Default: booldefault.StaticBool(false),
PlanModifiers: []planmodifier.Bool{boolplanmodifier.RequiresReplace()},
},
"deploy_mode": resourceSchema.StringAttribute{
MarkdownDescription: fmt.Sprintf("Set the Apstra Deploy Mode for this Generic System. Default: `%s`",
apstra.NodeDeployModeDeploy),
Optional: true,
Computed: true,
Default: stringdefault.StaticString(apstra.NodeDeployModeDeploy.String()),
Validators: []validator.String{stringvalidator.OneOf(utils.AllNodeDeployModes()...)},
},
}
}

Expand Down Expand Up @@ -268,6 +278,14 @@ func (o *DatacenterGenericSystem) ReadSystemProperties(ctx context.Context, bp *
o.External = types.BoolValue(nodeInfo.External)
}

if overwriteKnownValues || o.DeployMode.IsUnknown() {
deployMode, err := utils.GetNodeDeployMode(ctx, bp, o.Id.ValueString())
if err != nil {
return err
}
o.DeployMode = types.StringValue(deployMode)
}

// asn isn't computed, so will never be unknown
if overwriteKnownValues && nodeInfo.Asn != nil {
o.Asn = types.Int64Value(int64(*nodeInfo.Asn))
Expand Down Expand Up @@ -643,16 +661,33 @@ func (o *DatacenterGenericSystem) SetProperties(ctx context.Context, bp *apstra.
// set ASN if we don't have prior state or the ASN needs to be updated
if state == nil || !o.Asn.Equal(state.Asn) {
o.setAsn(ctx, bp, diags)
if diags.HasError() {
return
}
}

// set loopback v4 if we don't have prior state or the v4 address needs to be updated
if state == nil || !o.LoopbackIpv4.Equal(state.LoopbackIpv4) {
o.setLoopbackIPv4(ctx, bp, diags)
if diags.HasError() {
return
}
}

// set loopback v6 if we don't have prior state or the v6 address needs to be updated
if state == nil || !o.LoopbackIpv6.Equal(state.LoopbackIpv6) {
o.setLoopbackIPv6(ctx, bp, diags)
if diags.HasError() {
return
}
}

// set deploy mode if we don't have prior state or the deploy mode needs to be updated
if state == nil || !o.DeployMode.Equal(state.DeployMode) {
err := utils.SetNodeDeployMode(ctx, bp, o.Id.ValueString(), o.DeployMode.ValueString())
if err != nil {
diags.AddError("failed to set node deploy mode", err.Error())
}
}
}

Expand Down
69 changes: 0 additions & 69 deletions apstra/blueprint/device_allocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -642,72 +642,3 @@ func (o *DeviceAllocation) deviceProfileNodeIdFromDeviceKey(ctx context.Context,

o.DeviceProfileNodeId = types.StringValue(result.Items[0].DeviceProfile.Id)
}

func (o *DeviceAllocation) SetNodeDeployMode(ctx context.Context, client *apstra.Client, diags *diag.Diagnostics) {
if o.DeployMode.IsNull() {
return
}

if o.DeployMode.IsUnknown() {
o.GetNodeDeployMode(ctx, client, diags)
return
}

var deployMode apstra.NodeDeployMode
err := utils.ApiStringerFromFriendlyString(&deployMode, o.DeployMode.ValueString())
if err != nil {
diags.AddError(fmt.Sprintf("error parsing deploy mode %q", o.DeployMode.ValueString()), err.Error())
return
}

type patch struct {
Id string `json:"id"`
DeployMode *string `json:"deploy_mode"`
}

if err != nil {
diags.AddError(fmt.Sprintf("error parsing deploy mode %q", o.DeployMode.ValueString()), err.Error())
return
}

var stringPtr *string
if deployMode == apstra.NodeDeployModeNone {
stringPtr = nil
} else {
s := deployMode.String()
stringPtr = &s
}

setDeployMode := patch{
Id: o.NodeId.ValueString(),
DeployMode: stringPtr,
}
deployModeResponse := patch{}

err = client.PatchNode(ctx, apstra.ObjectId(o.BlueprintId.ValueString()), apstra.ObjectId(o.NodeId.ValueString()), &setDeployMode, &deployModeResponse)
if err != nil {
diags.AddError("error setting deploy mode", err.Error())
return
}
}

func (o *DeviceAllocation) GetNodeDeployMode(ctx context.Context, client *apstra.Client, diags *diag.Diagnostics) {
var node struct {
Id string `json:"id"`
Type string `json:"type"`
DeployMode string `json:"deploy_mode"`
}
err := client.GetNode(ctx, apstra.ObjectId(o.BlueprintId.ValueString()), apstra.ObjectId(o.NodeId.ValueString()), &node)
if err != nil {
diags.AddError("failed to fetch blueprint node", err.Error())
}

var deployMode apstra.NodeDeployMode
err = deployMode.FromString(node.DeployMode)
if err != nil {
diags.AddError(fmt.Sprintf("error parsing deploy mode %q", node.DeployMode), err.Error())
return
}

o.DeployMode = types.StringValue(utils.StringersToFriendlyString(deployMode))
}
89 changes: 68 additions & 21 deletions apstra/resource_datacenter_device_allocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tfapstra

import (
"context"
"errors"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/blueprint"
Expand Down Expand Up @@ -44,19 +45,19 @@ func (o *resourceDeviceAllocation) Create(ctx context.Context, req resource.Crea
return
}

// Ensure the blueprint exists.
if !utils.BlueprintExists(ctx, o.client, apstra.ObjectId(plan.BlueprintId.ValueString()), &resp.Diagnostics) {
if resp.Diagnostics.HasError() {
// create a client for the datacenter reference design
bp, err := o.client.NewTwoStageL3ClosClient(ctx, apstra.ObjectId(plan.BlueprintId.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddError(fmt.Sprintf("blueprint %s not found", plan.BlueprintId), err.Error())
return
}
resp.Diagnostics.AddError("no such blueprint", fmt.Sprintf("blueprint %s not found", plan.BlueprintId))
}
if resp.Diagnostics.HasError() {
resp.Diagnostics.AddError(fmt.Sprintf(blueprint.ErrDCBlueprintCreate, plan.BlueprintId), err.Error())
return
}

// Lock the blueprint mutex.
err := o.lockFunc(ctx, plan.BlueprintId.ValueString())
err = o.lockFunc(ctx, plan.BlueprintId.ValueString())
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("error locking blueprint %q mutex", plan.BlueprintId.ValueString()),
Expand Down Expand Up @@ -92,7 +93,22 @@ func (o *resourceDeviceAllocation) Create(ctx context.Context, req resource.Crea
}
}

plan.SetNodeDeployMode(ctx, o.client, &resp.Diagnostics)
switch {
case plan.DeployMode.IsNull(): // not expected with Optional+Computed, nothing to do here
case plan.DeployMode.IsUnknown(): // config is null, get the Computed value
deployMode, err := utils.GetNodeDeployMode(ctx, bp, plan.NodeId.ValueString())
if err != nil {
resp.Diagnostics.AddError("failed to fetch node deploy mode", err.Error())
return
}
plan.DeployMode = types.StringValue(deployMode)
default: // value provided via config
err = utils.SetNodeDeployMode(ctx, bp, plan.NodeId.ValueString(), plan.DeployMode.ValueString())
if err != nil {
resp.Diagnostics.AddError("failed while setting node deploy mode", err.Error())
return
}
}
if resp.Diagnostics.HasError() {
return
}
Expand All @@ -112,15 +128,14 @@ func (o *resourceDeviceAllocation) Read(ctx context.Context, req resource.ReadRe
previousInterfaceMapCatalogId := state.InitialInterfaceMapId
previousInterfaceMapName := state.InterfaceMapName

// Ensure the blueprint still exists.
if !utils.BlueprintExists(ctx, o.client, apstra.ObjectId(state.BlueprintId.ValueString()), &resp.Diagnostics) {
if resp.Diagnostics.HasError() {
// create a client for the datacenter reference design
bp, err := o.client.NewTwoStageL3ClosClient(ctx, apstra.ObjectId(state.BlueprintId.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.State.RemoveResource(ctx)
return
}
resp.State.RemoveResource(ctx)
return
}
if resp.Diagnostics.HasError() {
resp.Diagnostics.AddError(fmt.Sprintf(blueprint.ErrDCBlueprintCreate, state.BlueprintId), err.Error())
return
}

Expand Down Expand Up @@ -151,10 +166,15 @@ func (o *resourceDeviceAllocation) Read(ctx context.Context, req resource.ReadRe
return
}

state.GetNodeDeployMode(ctx, o.client, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
deployMode, err := utils.GetNodeDeployMode(ctx, bp, state.NodeId.ValueString())
if err != nil {
var ace apstra.ClientErr
if errors.As(err, &ace) && ace.Type() == apstra.ErrNotfound {
resp.State.RemoveResource(ctx)
return
}
}
state.DeployMode = types.StringValue(deployMode)

state.GetInterfaceMapName(ctx, o.client, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
Expand Down Expand Up @@ -200,20 +220,47 @@ func (o *resourceDeviceAllocation) Update(ctx context.Context, req resource.Upda
return
}

// create a client for the datacenter reference design
bp, err := o.client.NewTwoStageL3ClosClient(ctx, apstra.ObjectId(plan.BlueprintId.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.State.RemoveResource(ctx)
return
}
resp.Diagnostics.AddError(fmt.Sprintf(blueprint.ErrDCBlueprintCreate, plan.BlueprintId), err.Error())
return
}

// Lock the blueprint mutex.
err := o.lockFunc(ctx, plan.BlueprintId.ValueString())
err = o.lockFunc(ctx, plan.BlueprintId.ValueString())
if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("error locking blueprint %q mutex", plan.BlueprintId.ValueString()),
err.Error())
return
}

state.DeployMode = plan.DeployMode
state.SetNodeDeployMode(ctx, o.client, &resp.Diagnostics)
switch {
case plan.DeployMode.IsNull(): // not expected with Optional+Computed, nothing to do here
case plan.DeployMode.IsUnknown(): // config is null, get the Computed value
deployMode, err := utils.GetNodeDeployMode(ctx, bp, plan.NodeId.ValueString())
if err != nil {
resp.Diagnostics.AddError("failed reading node deploy mode", err.Error())
return
}

plan.DeployMode = types.StringValue(deployMode)
default: // value provided via config
err := utils.SetNodeDeployMode(ctx, bp, plan.NodeId.ValueString(), plan.DeployMode.ValueString())
if err != nil {
resp.Diagnostics.AddError("failed setting node deploy mode", err.Error())
return
}
}
if resp.Diagnostics.HasError() {
return
}
state.DeployMode = plan.DeployMode

if !plan.DeviceKey.Equal(state.DeviceKey) {
// device key has changed
Expand Down
Loading