Skip to content

Commit

Permalink
WIP: 2# maniputale TF stae based on FMC job history
Browse files Browse the repository at this point in the history
  • Loading branch information
wojciech-bainhelixpe committed Nov 27, 2024
1 parent 27c727d commit 4bcae2b
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 82 deletions.
5 changes: 3 additions & 2 deletions docs/resources/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ resource "fmc_deployment" "example" {

### Required

- `device_list` (Set of String) List of device ids to be deployed.
- `version` (String) Epoch unix time stamp (13 digits).
- `device_list` (List of String) List of device ids to be deployed.

### Optional

- `deployment_note` (String) User note related to deployment.
- `domain` (String) The name of the FMC domain
- `force_deploy` (Boolean) Force deployment (even if there are no configuration changes).
- `ignore_warning` (Boolean) Ignore warnings during deployment.
- `jobid` (String) JobId of deployment.
- `version` (String) Epoch unix time stamp (13 digits).

### Read-Only

Expand Down
10 changes: 8 additions & 2 deletions gen/definitions/deploy_device.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,15 @@ attributes:
- model_name: version
type: String
description: Epoch unix time stamp (13 digits).
mandatory: true
mandatory: false
example: "1457566762351"
test_value: var.timestamp
- model_name: jobid
type: String
description: JobId of deployment.
mandatory: false
exclude_example: true
exclude_test: true
- model_name: ForceDeploy
tf_name: force_deploy
type: Bool
Expand All @@ -34,7 +40,7 @@ attributes:
minimum_test_value: true
- model_name: deviceList
tf_name: device_list
type: Set
type: List
description: List of device ids to be deployed.
mandatory: true
element_type: String
Expand Down
24 changes: 19 additions & 5 deletions internal/provider/model_fmc_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ type Deployment struct {
Id types.String `tfsdk:"id"`
Domain types.String `tfsdk:"domain"`
Version types.String `tfsdk:"version"`
Jobid types.String `tfsdk:"jobid"`
ForceDeploy types.Bool `tfsdk:"force_deploy"`
IgnoreWarning types.Bool `tfsdk:"ignore_warning"`
DeviceList types.Set `tfsdk:"device_list"`
DeviceList types.List `tfsdk:"device_list"`
DeploymentNote types.String `tfsdk:"deployment_note"`
}

Expand All @@ -62,6 +63,9 @@ func (data Deployment) toBody(ctx context.Context, state Deployment) string {
if !data.Version.IsNull() {
body, _ = sjson.Set(body, "version", data.Version.ValueString())
}
if !data.Jobid.IsNull() {
body, _ = sjson.Set(body, "jobid", data.Jobid.ValueString())
}
if !data.ForceDeploy.IsNull() {
body, _ = sjson.Set(body, "ForceDeploy", data.ForceDeploy.ValueBool())
}
Expand Down Expand Up @@ -89,6 +93,11 @@ func (data *Deployment) fromBody(ctx context.Context, res gjson.Result) {
} else {
data.Version = types.StringNull()
}
if value := res.Get("jobid"); value.Exists() {
data.Jobid = types.StringValue(value.String())
} else {
data.Jobid = types.StringNull()
}
if value := res.Get("ForceDeploy"); value.Exists() {
data.ForceDeploy = types.BoolValue(value.Bool())
} else {
Expand All @@ -100,9 +109,9 @@ func (data *Deployment) fromBody(ctx context.Context, res gjson.Result) {
data.IgnoreWarning = types.BoolNull()
}
if value := res.Get("deviceList"); value.Exists() {
data.DeviceList = helpers.GetStringSet(value.Array())
data.DeviceList = helpers.GetStringList(value.Array())
} else {
data.DeviceList = types.SetNull(types.StringType)
data.DeviceList = types.ListNull(types.StringType)
}
if value := res.Get("deploymentNote"); value.Exists() {
data.DeploymentNote = types.StringValue(value.String())
Expand All @@ -125,6 +134,11 @@ func (data *Deployment) fromBodyPartial(ctx context.Context, res gjson.Result) {
} else {
data.Version = types.StringNull()
}
if value := res.Get("jobid"); value.Exists() && !data.Jobid.IsNull() {
data.Jobid = types.StringValue(value.String())
} else {
data.Jobid = types.StringNull()
}
if value := res.Get("ForceDeploy"); value.Exists() && !data.ForceDeploy.IsNull() {
data.ForceDeploy = types.BoolValue(value.Bool())
} else {
Expand All @@ -136,9 +150,9 @@ func (data *Deployment) fromBodyPartial(ctx context.Context, res gjson.Result) {
data.IgnoreWarning = types.BoolNull()
}
if value := res.Get("deviceList"); value.Exists() && !data.DeviceList.IsNull() {
data.DeviceList = helpers.GetStringSet(value.Array())
data.DeviceList = helpers.GetStringList(value.Array())
} else {
data.DeviceList = types.SetNull(types.StringType)
data.DeviceList = types.ListNull(types.StringType)
}
if value := res.Get("deploymentNote"); value.Exists() && !data.DeploymentNote.IsNull() {
data.DeploymentNote = types.StringValue(value.String())
Expand Down
125 changes: 53 additions & 72 deletions internal/provider/resource_fmc_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,26 @@ import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/tidwall/gjson"
// "net/url"
"strings"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/resource"
"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/types"
"github.com/tidwall/gjson"

// "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/netascode/go-fmc"
"github.com/netascode/terraform-provider-fmc/internal/provider/helpers"
)

// End of section. //template:end imports

// Section below is generated&owned by "gen/generator.go". //template:begin model

// Ensure provider defined types fully satisfy framework interfaces
Expand Down Expand Up @@ -76,7 +82,11 @@ func (r *DeploymentResource) Schema(ctx context.Context, req resource.SchemaRequ
},
"version": schema.StringAttribute{
MarkdownDescription: helpers.NewAttributeDescription("Epoch unix time stamp (13 digits).").String,
Required: true,
Optional: true,
},
"jobid": schema.StringAttribute{
MarkdownDescription: helpers.NewAttributeDescription("JobId of deployment.").String,
Optional: true,
},
"force_deploy": schema.BoolAttribute{
MarkdownDescription: helpers.NewAttributeDescription("Force deployment (even if there are no configuration changes).").String,
Expand All @@ -86,7 +96,7 @@ func (r *DeploymentResource) Schema(ctx context.Context, req resource.SchemaRequ
MarkdownDescription: helpers.NewAttributeDescription("Ignore warnings during deployment.").String,
Optional: true,
},
"device_list": schema.SetAttribute{
"device_list": schema.ListAttribute{
MarkdownDescription: helpers.NewAttributeDescription("List of device ids to be deployed.").String,
ElementType: types.StringType,
Required: true,
Expand Down Expand Up @@ -254,76 +264,11 @@ func (r *DeploymentResource) Read(ctx context.Context, req resource.ReadRequest,
// BoCODE

// Target job ID to match
// my_job_id := "0050568A-2561-0ed3-0000-004294994451"
my_job_id := state.Id.ValueString()

// https://10.50.202.94/api/fmc_config/v1/domain/e276abec-e0f2-11e3-8169-6d9ed49b625f/deployment/jobhistories?expanded=true
// urlPath := state.getPath() + "/" + url.QueryEscape(state.Id.ValueString())

// urlPath := "/api/fmc_config/v1/domain/{DOMAIN_UUID}/deployment/jobhistories?expanded=true"
urlPath := "/api/fmc_config/v1/domain/{DOMAIN_UUID}/deployment/jobhistories/" + my_job_id
jobId := state.Id.ValueString()
// state.Jobid = types.StringValue(jobId)

urlPath := "/api/fmc_config/v1/domain/{DOMAIN_UUID}/deployment/jobhistories/" + jobId
res, err := r.client.Get(urlPath, reqMods...)
jsonString := res.String()
var resMap map[string]interface{}
err = json.Unmarshal([]byte(jsonString), &resMap)
if err != nil {
tflog.Debug(ctx, fmt.Sprintf("%s: Error parsing JSON data (jobhistories)", ""))
}

// Get resource details from job history
resDeviceList := state.DeviceList.Elements()
// Modify matchingItem to be in resource format
resMap["deviceList"] = resDeviceList

// Convert the modified resMap back to JSON
updatedJSON, err := json.Marshal(resMap)
if err != nil {
fmt.Printf("Error converting map to JSON: %v\n", err)
return
}

res = gjson.Parse(string(updatedJSON))

matchingItem := resMap

// // Variable to store the matching item
// var matchingItem map[string]interface{}

// // Access "items"
// items, ok := resMap["items"].([]interface{})
// if !ok {
// tflog.Debug(ctx, fmt.Sprintf("%s: Error: 'items' is not a valid array", ""))
// }

// // Iterate over items to find the matching ID
// for _, item := range items {
// itemMap, ok := item.(map[string]interface{})
// if !ok {
// continue
// }

// // // Navigate to data
// // data, ok := itemMap["data"].(map[string]interface{})
// // if !ok {
// // continue
// // }

// // Check if the ID matches
// if id, ok := itemMap["id"].(string); ok && id == my_job_id {
// matchingItem = itemMap
// break // Exit the loop as we found the match
// }
// }

// Print the matching item
if matchingItem != nil {
tflog.Debug(ctx, fmt.Sprintf("Matching item: %+v\n", matchingItem))
} else {
tflog.Debug(ctx, fmt.Sprintf("No matching item found. %+v\n", ""))
}

// EoCODE

if err != nil && strings.Contains(err.Error(), "StatusCode 404") {
resp.State.RemoveResource(ctx)
Expand All @@ -338,13 +283,49 @@ func (r *DeploymentResource) Read(ctx context.Context, req resource.ReadRequest,
return
}

// Update res with required key (tfsate)
jsonString := res.String()
var resMap map[string]interface{}
err = json.Unmarshal([]byte(jsonString), &resMap)
if err != nil {
tflog.Debug(ctx, fmt.Sprintf("%s: Error parsing JSON data (jobhistories)", ""))
}
resMap["version"] = "1732274866000"
// resMap["ignore_warning"] = "true"
resMapJSON, err := json.Marshal(resMap)
if err != nil {
panic(fmt.Sprintf("Failed to marshal JSON: %v", err))
}
res = gjson.Parse(string(resMapJSON))

// Read device list
resDeviceId := res.Get("deviceList.0.deviceUUID").String()
// resIgnoreWarning := res.Get("ignoreWarning").Bool()
// resVersion := res.Get("version").String()

// Define the type of elements in the list
elementType := types.StringType
// Define the list values
values := []attr.Value{
types.StringValue(resDeviceId),
}
// Create the ListValue
deviceList, diags := types.ListValue(elementType, values)
if diags.HasError() {
tflog.Debug(ctx, fmt.Sprintf("%s: Error creating deviceList (jobhistories)", ""))
}

// After `terraform import` we switch to a full read.
if imp {
state.fromBody(ctx, res)
} else {
state.fromBodyPartial(ctx, res)
}

state.DeviceList = deviceList
// state.IgnoreWarning = types.BoolValue(resIgnoreWarning)
// state.Version = types.StringValue(resVersion)

tflog.Debug(ctx, fmt.Sprintf("%s: Read finished successfully", state.Id.ValueString()))

diags = resp.State.Set(ctx, &state)
Expand Down
1 change: 0 additions & 1 deletion internal/provider/resource_fmc_deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ variable "device_id_list" { // tests will set $TF_VAR_device_id_list

func testAccFmcDeploymentConfig_minimum() string {
config := `resource "fmc_deployment" "test" {` + "\n"
config += ` version = var.timestamp` + "\n"
config += ` ignore_warning = true` + "\n"
config += ` device_list = var.device_id_list` + "\n"
config += `}` + "\n"
Expand Down

0 comments on commit 4bcae2b

Please sign in to comment.