mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 10:33:46 +00:00
196 lines
5.0 KiB
Go
196 lines
5.0 KiB
Go
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
package structure
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
|
|
|
"github.com/bpg/terraform-provider-proxmox/utils"
|
|
)
|
|
|
|
// MergeSchema merges the map[string]*schema.Schema from src into dst.
|
|
// Panicking enforces safety against conflicts.
|
|
func MergeSchema(dst, src map[string]*schema.Schema) {
|
|
for k, v := range src {
|
|
if _, ok := dst[k]; ok {
|
|
panic(fmt.Errorf("conflicting schema key: %s", k))
|
|
}
|
|
|
|
dst[k] = v
|
|
}
|
|
}
|
|
|
|
// GetSchemaBlock returns a map[string]interface{} of a nested resource by key(s) from a schema.ResourceData.
|
|
func GetSchemaBlock(
|
|
r *schema.Resource,
|
|
d *schema.ResourceData,
|
|
k []string,
|
|
i int,
|
|
allowDefault bool,
|
|
) (map[string]interface{}, error) {
|
|
var resourceBlock map[string]interface{}
|
|
|
|
var resourceData interface{}
|
|
|
|
var resourceSchema *schema.Schema
|
|
|
|
for ki, kv := range k {
|
|
if ki == 0 {
|
|
resourceData = d.Get(kv)
|
|
resourceSchema = r.Schema[kv]
|
|
} else if resourceSchema != nil {
|
|
mapValues := resourceData.([]interface{})
|
|
|
|
if len(mapValues) <= i {
|
|
return resourceBlock, fmt.Errorf("index out of bounds %d", i)
|
|
}
|
|
|
|
mapValue := mapValues[i].(map[string]interface{})
|
|
|
|
resourceData = mapValue[kv]
|
|
resourceSchema = resourceSchema.Elem.(*schema.Resource).Schema[kv]
|
|
}
|
|
}
|
|
|
|
if resourceSchema == nil {
|
|
return nil, fmt.Errorf("schema not found for %s", strings.Join(k, "."))
|
|
}
|
|
|
|
list := resourceData.([]interface{})
|
|
|
|
if len(list) == 0 {
|
|
if allowDefault {
|
|
listDefault, err := resourceSchema.DefaultValue()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get default value for %s: %w", strings.Join(k, "."), err)
|
|
}
|
|
|
|
list = listDefault.([]interface{})
|
|
}
|
|
}
|
|
|
|
if len(list) > i {
|
|
resourceBlock = list[i].(map[string]interface{})
|
|
}
|
|
|
|
return resourceBlock, nil
|
|
}
|
|
|
|
// SuppressIfListsAreEqualIgnoringOrder is a customdiff.SuppressionFunc that suppresses
|
|
// changes to a list if the old and new lists are equal, ignoring the order of the
|
|
// elements.
|
|
// It will be called for each list item, so it is not super efficient. It is
|
|
// recommended to use it only for small lists.
|
|
// Ref: https://github.com/hashicorp/terraform-plugin-sdk/issues/477
|
|
func SuppressIfListsAreEqualIgnoringOrder(key, _, _ string, d *schema.ResourceData) bool {
|
|
// the key is a path to the list item, not the list itself, e.g. "tags.#"
|
|
lastDotIndex := strings.LastIndex(key, ".")
|
|
if lastDotIndex != -1 {
|
|
key = key[:lastDotIndex]
|
|
}
|
|
|
|
oldData, newData := d.GetChange(key)
|
|
if oldData == nil || newData == nil {
|
|
return false
|
|
}
|
|
|
|
oldArray := oldData.([]interface{})
|
|
newArray := newData.([]interface{})
|
|
|
|
if len(oldArray) != len(newArray) {
|
|
return false
|
|
}
|
|
|
|
oldEvents := make([]string, len(oldArray))
|
|
newEvents := make([]string, len(newArray))
|
|
|
|
for i, oldEvt := range oldArray {
|
|
oldEvents[i] = fmt.Sprint(oldEvt)
|
|
}
|
|
|
|
for j, newEvt := range newArray {
|
|
newEvents[j] = fmt.Sprint(newEvt)
|
|
}
|
|
|
|
sort.Strings(oldEvents)
|
|
sort.Strings(newEvents)
|
|
|
|
return reflect.DeepEqual(oldEvents, newEvents)
|
|
}
|
|
|
|
// SuppressIfListsOfMapsAreEqualIgnoringOrderByKey is a customdiff.SuppressionFunc that suppresses
|
|
// changes to a list of resources if the old and new lists are equal, ignoring the order of the
|
|
// elements.
|
|
// It will be called for each resource attribute, so it is not super efficient. It is
|
|
// recommended to use it only for small lists / small resources.
|
|
// The keyAttr is the attribute that will be used as the key to compare the resources.
|
|
// The ignoreKeys are the keys that will be ignored when comparing the resources. Include computed read-only
|
|
// attributes here.
|
|
//
|
|
// Ref: https://github.com/hashicorp/terraform-plugin-sdk/issues/477
|
|
func SuppressIfListsOfMapsAreEqualIgnoringOrderByKey(
|
|
keyAttr string,
|
|
ignoreKeys ...string,
|
|
) schema.SchemaDiffSuppressFunc {
|
|
// the attr is a path to the item's attribute, not the list itself, e.g. "numa.0.device"
|
|
return func(attr, _, _ string, d *schema.ResourceData) bool {
|
|
lastDotIndex := strings.LastIndex(attr, ".")
|
|
if lastDotIndex != -1 {
|
|
attr = attr[:lastDotIndex]
|
|
}
|
|
|
|
lastDotIndex = strings.LastIndex(attr, ".")
|
|
if lastDotIndex != -1 {
|
|
attr = attr[:lastDotIndex]
|
|
}
|
|
|
|
oldData, newData := d.GetChange(attr)
|
|
if oldData == nil || newData == nil {
|
|
return false
|
|
}
|
|
|
|
oldArray, ok := oldData.([]interface{})
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
newArray, ok := newData.([]interface{})
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
if len(oldArray) != len(newArray) {
|
|
return false
|
|
}
|
|
|
|
oldMap := utils.MapResourcesByAttribute(oldArray, keyAttr)
|
|
newMap := utils.MapResourcesByAttribute(newArray, keyAttr)
|
|
|
|
for k, v := range oldMap {
|
|
if _, ok := newMap[k]; !ok {
|
|
return false
|
|
}
|
|
|
|
for _, ignoreKey := range ignoreKeys {
|
|
delete(v.(map[string]interface{}), ignoreKey)
|
|
delete(newMap[k].(map[string]interface{}), ignoreKey)
|
|
}
|
|
|
|
if !reflect.DeepEqual(v, newMap[k]) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
}
|