0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-06-29 18:21:10 +00:00
terraform-provider-proxmox/proxmoxtf/structure/schema.go
Pavel Boldyrev d23f7ab1c0
chore(code): fix new qodana warnings (#1951)
Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
2025-05-05 22:11:19 -04:00

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
}
}