Skip to content
This repository has been archived by the owner on Jun 17, 2024. It is now read-only.

Commit

Permalink
Issue with Terraform PLAN (Fixes #73) (#83)
Browse files Browse the repository at this point in the history
Plan was showing a diff even if nothing was changed in the .tf file
Fixing this issue by introducing another property in the resource_configuration called 
resource_state. 'resource_state' is a computed property and it will have the updated state
of the deployment. The 'configuration' that was previously also used for detailed view of 
resource machines will only behave like an input.

Signed-off-by: Prativa Bawri <bawrip@vmware.com>
  • Loading branch information
Prativa20 authored Aug 27, 2020
1 parent e4a64ff commit 9401e95
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 117 deletions.
1 change: 1 addition & 0 deletions sdk/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type ResourceConfigurationStruct struct {
RequestState string `json:"request_state,omitempty"`
ResourceType string `json:"resource_type,omitempty"`
Configuration map[string]interface{} `json:"configuration,omitempty"`
ResourceState map[string]interface{} `json:"resource_state,omitempty"`
DateCreated string `json:"last_created,omitempty"`
LastUpdated string `json:"last_updated,omitempty"`
ParentResourceID string `json:"parent_resource_id,omitempty"`
Expand Down
80 changes: 0 additions & 80 deletions utils/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/json"
"reflect"
"strconv"
"strings"

"github.com/op/go-logging"
)
Expand Down Expand Up @@ -77,82 +76,3 @@ func UnmarshalJSONStringIfNecessary(field string, value interface{}) interface{}

return jsonValue
}

// UpdateResourceConfigurationMap updates the resource configuration with
//the deployment resource data if there is difference
// between the config data and deployment data, return true
func UpdateResourceConfigurationMap(
resourceConfiguration map[string]interface{}, vmData map[string]map[string]interface{}) (map[string]interface{}, bool) {
var changed bool
for configKey1, configValue1 := range resourceConfiguration {
for configKey2, configValue2 := range vmData {
if strings.HasPrefix(configKey1, configKey2+".") {
trimmedKey := strings.TrimPrefix(configKey1, configKey2+".")
currentValue := configValue1
updatedValue := ConvertInterfaceToString(configValue2[trimmedKey])

if updatedValue != "" && updatedValue != currentValue {
resourceConfiguration[configKey1] = updatedValue
changed = true
}
}
}
}
return resourceConfiguration, changed
}

// ReplaceValueInRequestTemplate replaces the value for a given key in a catalog
// request template.
func ReplaceValueInRequestTemplate(templateInterface map[string]interface{}, field string, value interface{}) bool {
var replaced bool
//Iterate over the map to get field provided as an argument
for key, val := range templateInterface {
//If value type is map then set recursive call which will fiend field in one level down of map interface
if reflect.ValueOf(val).Kind() == reflect.Map {
replaced = ReplaceValueInRequestTemplate(val.(map[string]interface{}), field, value)
if replaced {
return true
}
} else if key == field && val != value {
//If value type is not map then compare field name with provided field name
//If both matches then update field value with provided value
templateInterface[key] = value
if reflect.ValueOf(value).Kind() == reflect.String {
templateInterface[key] = UnmarshalJSONStringIfNecessary(field, value)
}
return true
}
}
return replaced
}

// AddValueToRequestTemplate modeled after replaceValueInRequestTemplate
// for values being added to template vs updating existing ones
func AddValueToRequestTemplate(templateInterface map[string]interface{}, field string, value interface{}) map[string]interface{} {
//simplest case is adding a simple value. Leaving as a func in case there's a need to do more complicated additions later
// templateInterface[data]
for k, v := range templateInterface {
if reflect.ValueOf(v).Kind() == reflect.Map && k == "data" {
template, _ := v.(map[string]interface{})
_ = AddValueToRequestTemplate(template, field, value)
} else { //if i == "data" {
templateInterface[field] = UnmarshalJSONStringIfNecessary(field, value)
}
}
//Return updated map interface type
return templateInterface
}

// ResourceMapper returns the mapping of resource attributes from ResourceView APIs
// to Catalog Item Request Template APIs
func ResourceMapper() map[string]string {
m := make(map[string]string)
m["MachineName"] = "name"
m["MachineDescription"] = "description"
m["MachineMemory"] = "memory"
m["MachineStorage"] = "storage"
m["MachineCPU"] = "cpu"
m["MachineStatus"] = "status"
m["MachineType"] = "type"
return m
}
2 changes: 1 addition & 1 deletion vra7/data_source_vra7_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func dataSourceVra7DeploymentRead(d *schema.ResourceData, meta interface{}) erro
componentName := data["Component"].(string)
parentResourceID := rMap["parentResourceId"].(string)
var resourceConfigStruct sdk.ResourceConfigurationStruct
resourceConfigStruct.Configuration = data
resourceConfigStruct.ResourceState = data
resourceConfigStruct.ComponentName = componentName
resourceConfigStruct.Name = name
resourceConfigStruct.DateCreated = dateCreated
Expand Down
59 changes: 38 additions & 21 deletions vra7/resource_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/hashicorp/terraform/helper/schema"
"github.com/vmware/terraform-provider-vra7/sdk"
"github.com/vmware/terraform-provider-vra7/utils"
)

func resourceConfigurationSchema(optional bool) *schema.Schema {
Expand All @@ -29,6 +28,13 @@ func resourceConfigurationSchema(optional bool) *schema.Schema {
Type: schema.TypeString,
},
},
"resource_state": {
Type: schema.TypeMap,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"cluster": {
Type: schema.TypeInt,
Optional: optional,
Expand Down Expand Up @@ -92,6 +98,7 @@ func expandResourceConfiguration(rConfigurations []interface{}) []sdk.ResourceCo
rConfig := sdk.ResourceConfigurationStruct{
ComponentName: configMap["component_name"].(string),
Configuration: configMap["configuration"].(map[string]interface{}),
ResourceState: configMap["resource_state"].(map[string]interface{}),
Cluster: configMap["cluster"].(int),
Name: configMap["name"].(string),
Description: configMap["description"].(string),
Expand All @@ -109,15 +116,16 @@ func expandResourceConfiguration(rConfigurations []interface{}) []sdk.ResourceCo
return configs
}

func flattenResourceConfigurations(configs []sdk.ResourceConfigurationStruct, clusterCountMap map[string]int) []map[string]interface{} {
if len(configs) == 0 {
func flattenResourceConfigurations(resourceConfigList []sdk.ResourceConfigurationStruct, clusterCountMap map[string]int) []map[string]interface{} {
if len(resourceConfigList) == 0 {
return make([]map[string]interface{}, 0)
}
rConfigs := make([]map[string]interface{}, 0, len(configs))
for _, config := range configs {
resourceDataMap := parseDataMap(config.Configuration)
rConfigs := make([]map[string]interface{}, 0, len(resourceConfigList))
for _, config := range resourceConfigList {
stateMap, configurationMap := parseDataMap(config.ResourceState, config.Configuration)
helper := make(map[string]interface{})
helper["configuration"] = resourceDataMap
helper["resource_state"] = stateMap
helper["configuration"] = configurationMap
helper["component_name"] = config.ComponentName
helper["name"] = config.Name
helper["date_created"] = config.DateCreated
Expand All @@ -135,9 +143,9 @@ func flattenResourceConfigurations(configs []sdk.ResourceConfigurationStruct, cl
return rConfigs
}

func parseDataMap(resourceData map[string]interface{}) map[string]interface{} {
m := make(map[string]interface{})
resourcePropertyMapper := utils.ResourceMapper()
func parseDataMap(resourceData map[string]interface{}, configurationMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
stateMap := make(map[string]interface{})
resourcePropertyMapper := ResourceMapper()
for key, value := range resourceData {

if i, ok := resourcePropertyMapper[key]; ok {
Expand All @@ -146,33 +154,39 @@ func parseDataMap(resourceData map[string]interface{}) map[string]interface{} {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.Slice:
parseArray(key, m, value.([]interface{}))
parseArray(key, stateMap, configurationMap, value.([]interface{}))
case reflect.Map:
parseMap(key, m, value.(map[string]interface{}))
parseMap(key, stateMap, configurationMap, value.(map[string]interface{}))
default:
m[key] = convToString(value)
stateMap[key] = convToString(value)
if _, ok := configurationMap[key]; ok {
configurationMap[key] = convToString(value)
}
}
}
return m
return stateMap, configurationMap
}

func parseMap(prefix string, m map[string]interface{}, data map[string]interface{}) {
func parseMap(prefix string, stateMap map[string]interface{}, configurationMap map[string]interface{}, data map[string]interface{}) {

for key, value := range data {
v := reflect.ValueOf(value)

switch v.Kind() {
case reflect.Slice:
parseArray(prefix+"."+key, m, value.([]interface{}))
parseArray(prefix+"."+key, stateMap, configurationMap, value.([]interface{}))
case reflect.Map:
parseMap(prefix+"."+key, m, value.(map[string]interface{}))
parseMap(prefix+"."+key, stateMap, configurationMap, value.(map[string]interface{}))
default:
m[prefix+"."+key] = convToString(value)
stateMap[prefix+"."+key] = convToString(value)
if _, ok := configurationMap[prefix+"."+key]; ok {
configurationMap[key] = convToString(value)
}
}
}
}

func parseArray(prefix string, m map[string]interface{}, value []interface{}) {
func parseArray(prefix string, stateMap map[string]interface{}, configurationMap map[string]interface{}, value []interface{}) {

for index, val := range value {
v := reflect.ValueOf(val)
Expand All @@ -196,11 +210,14 @@ func parseArray(prefix string, m map[string]interface{}, value []interface{}) {
objMap := val.(map[string]interface{})
for k, v := range objMap {
if k == "data" {
parseMap(prefix+"."+convToString(index), m, v.(map[string]interface{}))
parseMap(prefix+"."+convToString(index), stateMap, configurationMap, v.(map[string]interface{}))
}
}
default:
m[prefix+"."+convToString(index)] = convToString(val)
stateMap[prefix+"."+convToString(index)] = convToString(val)
if _, ok := configurationMap[prefix+"."+convToString(index)]; ok {
configurationMap[prefix+"."+convToString(index)] = convToString(value)
}
}
}
}
Expand Down
42 changes: 28 additions & 14 deletions vra7/resource_vra7_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,14 @@ func resourceVra7DeploymentCreate(d *schema.ResourceData, meta interface{}) erro
}

for _, rConfig := range p.ResourceConfiguration {
tempConfigMap := make(map[string]interface{})
for index, element := range rConfig.Configuration {
tempConfigMap[index] = element
}
if rConfig.Cluster != 0 {
rConfig.Configuration["_cluster"] = rConfig.Cluster
tempConfigMap["_cluster"] = rConfig.Cluster
}
for propertyName, propertyValue := range rConfig.Configuration {
for propertyName, propertyValue := range tempConfigMap {
requestTemplate.Data[rConfig.ComponentName] = updateRequestTemplate(
requestTemplate.Data[rConfig.ComponentName].(map[string]interface{}),
propertyName,
Expand All @@ -212,10 +216,10 @@ func resourceVra7DeploymentCreate(d *schema.ResourceData, meta interface{}) erro
}

func updateRequestTemplate(templateInterface map[string]interface{}, field string, value interface{}) map[string]interface{} {
replaced := utils.ReplaceValueInRequestTemplate(templateInterface, field, value)
replaced := ReplaceValueInRequestTemplate(templateInterface, field, value)

if !replaced {
templateInterface["data"] = utils.AddValueToRequestTemplate(templateInterface["data"].(map[string]interface{}), field, value)
templateInterface["data"] = AddValueToRequestTemplate(templateInterface["data"].(map[string]interface{}), field, value)
}
return templateInterface
}
Expand Down Expand Up @@ -250,7 +254,7 @@ func resourceVra7DeploymentUpdate(d *schema.ResourceData, meta interface{}) erro
newLeaseEnd := currTime.Add(time.Hour * 24 * time.Duration(int64(p.Lease))) // format 2020-04-06 00:15:44 -0700 PDT
extendLeaseTo := ConvertToISO8601(newLeaseEnd)
log.Info("Starting Change Lease action on the deployment with id %v. The lease will be extended by %v days.", p.DeploymentID, p.Lease)
_ = utils.ReplaceValueInRequestTemplate(
_ = ReplaceValueInRequestTemplate(
resourceActionTemplate.Data, "provider-ExpirationDate", extendLeaseTo)
requestID, err := vraClient.PostResourceAction(p.DeploymentID, changeLeaseActionID, resourceActionTemplate)
if err != nil {
Expand Down Expand Up @@ -289,9 +293,9 @@ func resourceVra7DeploymentUpdate(d *schema.ResourceData, meta interface{}) erro
// get the map from the action template corresponding to the key which is the component name
actionTemplateDataMap := GetActionTemplateDataByComponent(resourceActionTemplate.Data, newResourceConfig.ComponentName)
// update the template with the new cluster size
log.Info("Starting Scale In action on the deployment with id %v for the component %v. The cluster size will be reduced from %v to %v.",
log.Info("Starting Scale Out action on the deployment with id %v for the component %v. The cluster size will be increased from %v to %v.",
p.DeploymentID, oldResourceConfig.ComponentName, oldResourceConfig.Cluster, newResourceConfig.Cluster)
_ = utils.ReplaceValueInRequestTemplate(
_ = ReplaceValueInRequestTemplate(
actionTemplateDataMap, "_cluster", newResourceConfig.Cluster)
requestID, err := vraClient.PostResourceAction(p.DeploymentID, scaleOutActionID, resourceActionTemplate)
if err != nil {
Expand All @@ -304,7 +308,7 @@ func resourceVra7DeploymentUpdate(d *schema.ResourceData, meta interface{}) erro
log.Errorf("The scale out request failed with error: %v ", err)
return err
}
log.Info("Successfully completed the Scale In action for the deployment with id %v.", p.DeploymentID)
log.Info("Successfully completed the Scale Out action for the deployment with id %v.", p.DeploymentID)
} else if oldResourceConfig.Cluster > newResourceConfig.Cluster && deploymentActionsMap[sdk.ScaleIn] != "" {
// Scale In Day 2 operation
scaleInActionID := deploymentActionsMap[sdk.ScaleIn]
Expand All @@ -316,9 +320,9 @@ func resourceVra7DeploymentUpdate(d *schema.ResourceData, meta interface{}) erro
// get the map from the action template corresponding to the key which is the component name
actionTemplateDataMap := GetActionTemplateDataByComponent(resourceActionTemplate.Data, newResourceConfig.ComponentName)
// update the template with the new cluster size
log.Info("Starting Scale Out action on the deployment with id %v for the component %v. The cluster size will be increased from %v to %v.",
log.Info("Starting Scale In action on the deployment with id %v for the component %v. The cluster size will be decresed from %v to %v.",
p.DeploymentID, oldResourceConfig.ComponentName, oldResourceConfig.Cluster, newResourceConfig.Cluster)
_ = utils.ReplaceValueInRequestTemplate(
_ = ReplaceValueInRequestTemplate(
actionTemplateDataMap, "_cluster", newResourceConfig.Cluster)
requestID, err := vraClient.PostResourceAction(p.DeploymentID, scaleInActionID, resourceActionTemplate)
if err != nil {
Expand All @@ -331,7 +335,7 @@ func resourceVra7DeploymentUpdate(d *schema.ResourceData, meta interface{}) erro
log.Errorf("The scale in request failed with error: %v ", err)
return err
}
log.Info("Successfully completed the Scale Out action for the deployment with id %v.", p.DeploymentID)
log.Info("Successfully completed the Scale In action for the deployment with id %v.", p.DeploymentID)
}
}
}
Expand All @@ -345,6 +349,7 @@ func resourceVra7DeploymentUpdate(d *schema.ResourceData, meta interface{}) erro
for _, resource := range resources.Content {
oldResourceConfig := GetResourceByID(oldResourceConfigList, resource.ID)
if oldResourceConfig.ComponentName != "" {

vmResourceActions, _ := vraClient.GetResourceActions(oldResourceConfig.ResourceID)
vmResourceActionsMap := GetActionNameIDMap(vmResourceActions)
if vmResourceActionsMap[sdk.Reconfigure] != "" {
Expand All @@ -355,7 +360,7 @@ func resourceVra7DeploymentUpdate(d *schema.ResourceData, meta interface{}) erro
actionTemplateDataMap := resourceActionTemplate.Data
for propertyName, propertyValue := range newResourceConfig.Configuration {
if oldResourceConfig.Configuration[propertyName] != propertyValue {
_ = utils.ReplaceValueInRequestTemplate(
_ = ReplaceValueInRequestTemplate(
actionTemplateDataMap, propertyName, propertyValue)
if !configChanged {
configChanged = true
Expand All @@ -369,7 +374,7 @@ func resourceVra7DeploymentUpdate(d *schema.ResourceData, meta interface{}) erro
log.Errorf("The reconfigure request failed with error: %v ", err)
return err
}
log.Info("The Scale In operation for the component %v has been submitted", oldResourceConfig.ComponentName)
log.Info("The Reconfigure operation for the component %v has been submitted", oldResourceConfig.ComponentName)
_, err = waitForRequestCompletion(d, meta, requestID)
if err != nil {
log.Errorf("The reconfigure request for component %v failed with error: %v ", oldResourceConfig.ComponentName, err)
Expand All @@ -391,6 +396,11 @@ func resourceVra7DeploymentRead(d *schema.ResourceData, meta interface{}) error
log.Info("Reading the resource vra7_deployment with request id %s ", d.Id())
vraClient := meta.(*sdk.APIClient)

p, err := readProviderConfiguration(d, vraClient)
if err != nil {
return err
}

// Get the ID of the catalog request that was used to provision this Deployment. This id
// will remain the same for this deployment across any actions on the machines like reconfigure, etc.
catalogItemRequestID := d.Id()
Expand Down Expand Up @@ -423,7 +433,7 @@ func resourceVra7DeploymentRead(d *schema.ResourceData, meta interface{}) error
componentName := data["Component"].(string)
parentResourceID := rMap["parentResourceId"].(string)
var resourceConfigStruct sdk.ResourceConfigurationStruct
resourceConfigStruct.Configuration = data
resourceConfigStruct.ResourceState = data
resourceConfigStruct.ComponentName = componentName
resourceConfigStruct.Name = name
resourceConfigStruct.DateCreated = dateCreated
Expand All @@ -435,6 +445,10 @@ func resourceVra7DeploymentRead(d *schema.ResourceData, meta interface{}) error
resourceConfigStruct.ParentResourceID = parentResourceID
resourceConfigStruct.IPAddress = data["ip_address"].(string)

if p != nil && p.ResourceConfiguration != nil {
resourceConfigStruct.Configuration = GetConfiguration(componentName, p.ResourceConfiguration)
}

if rMap["description"] != nil {
resourceConfigStruct.Description = rMap["description"].(string)
}
Expand Down
Loading

0 comments on commit 9401e95

Please sign in to comment.