mirror of
https://github.com/hashicorp/terraform.git
synced 2026-02-18 18:29:44 -05:00
161 lines
5.3 KiB
Go
161 lines
5.3 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package backendbase
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"os"
|
|
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/convert"
|
|
)
|
|
|
|
// GetPathDefault traverses the steps of the given path through the given
|
|
// value, and then returns either that value or the value given in def,
|
|
// if the found value was null.
|
|
//
|
|
// This function expects the given path to be valid for the given value, and
|
|
// will panic if not. This should be used only for values that have already
|
|
// been coerced into a known-good data type, which is typically achieved by
|
|
// passing the value that was returned by [Base.PrepareConfig], which is also
|
|
// the value passed to [Backend.Configure].
|
|
func GetPathDefault(v cty.Value, path cty.Path, def cty.Value) cty.Value {
|
|
v, err := path.Apply(v)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("invalid path: %s", tfdiags.FormatError(err)))
|
|
}
|
|
if v.IsNull() {
|
|
return def
|
|
}
|
|
return v
|
|
}
|
|
|
|
// GetAttrDefault is like [GetPathDefault] but more convenient for the common
|
|
// case of looking up a single top-level attribute.
|
|
func GetAttrDefault(v cty.Value, attrName string, def cty.Value) cty.Value {
|
|
return GetPathDefault(v, cty.GetAttrPath(attrName), def)
|
|
}
|
|
|
|
// GetPathEnvDefault is like [GetPathDefault] except that the default value
|
|
// is taken from an environment variable of the name given in defEnv, returned
|
|
// as a string value.
|
|
//
|
|
// If that environment variable is unset or has an empty-string value then
|
|
// the result is null, as a convenience to callers so that they don't need to
|
|
// handle both null-ness and empty-string-ness as variants of "unset".
|
|
//
|
|
// This function panics in the same situations as [GetPathDefault].
|
|
func GetPathEnvDefault(v cty.Value, path cty.Path, defEnv string) cty.Value {
|
|
v, err := path.Apply(v)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("invalid path: %s", tfdiags.FormatError(err)))
|
|
}
|
|
if v.IsNull() {
|
|
if defStr := os.Getenv(defEnv); defStr != "" {
|
|
return cty.StringVal(defStr)
|
|
}
|
|
}
|
|
return v
|
|
}
|
|
|
|
// GetAttrEnvDefault is like [GetPathEnvDefault] but more convenient for the
|
|
// common case of looking up a single top-level attribute.
|
|
func GetAttrEnvDefault(v cty.Value, attrName string, defEnv string) cty.Value {
|
|
return GetPathEnvDefault(v, cty.GetAttrPath(attrName), defEnv)
|
|
}
|
|
|
|
// GetPathEnvDefaultFallback is like [GetPathEnvDefault] except that if
|
|
// neither the attribute nor the environment variable are set then instead
|
|
// of returning null it will return the given fallback value.
|
|
//
|
|
// Unless the fallback value is null itself, this function guarantees to never
|
|
// return null.
|
|
func GetPathEnvDefaultFallback(v cty.Value, path cty.Path, defEnv string, fallback cty.Value) cty.Value {
|
|
ret := GetPathEnvDefault(v, path, defEnv)
|
|
if ret.IsNull() {
|
|
return fallback
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// GetAttrEnvDefaultFallback is like [GetPathEnvDefault] except that if
|
|
// neither the attribute nor the environment variable are set then instead
|
|
// of returning null it will return the given fallback value.
|
|
//
|
|
// Unless the fallback value is null itself, this function guarantees to never
|
|
// return null.
|
|
func GetAttrEnvDefaultFallback(v cty.Value, attrName string, defEnv string, fallback cty.Value) cty.Value {
|
|
ret := GetAttrEnvDefault(v, attrName, defEnv)
|
|
if ret.IsNull() {
|
|
return fallback
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// IntValue converts a cty value into a Go int64, or returns an error if that's
|
|
// not possible.
|
|
func IntValue(v cty.Value) (int64, error) {
|
|
v, err := convert.Convert(v, cty.Number)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if v.IsNull() {
|
|
return 0, fmt.Errorf("must not be null")
|
|
}
|
|
bf := v.AsBigFloat()
|
|
ret, acc := bf.Int64()
|
|
if acc != big.Exact {
|
|
return 0, fmt.Errorf("must not be a whole number")
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
// BoolValue converts a cty value Go bool, or returns an error if that's not
|
|
// possible.
|
|
func BoolValue(v cty.Value) (bool, error) {
|
|
v, err := convert.Convert(v, cty.Bool)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if v.IsNull() {
|
|
return false, fmt.Errorf("must not be null")
|
|
}
|
|
return v.True(), nil
|
|
}
|
|
|
|
// MustBoolValue converts a cty value Go bool, or panics if that's not possible.
|
|
func MustBoolValue(v cty.Value) bool {
|
|
ret, err := BoolValue(v)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("MustBoolValue: %s", err))
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// ErrorAsDiagnostics wraps a non-nil error as a tfdiags.Diagnostics.
|
|
//
|
|
// Panics if the given error is nil, since the caller should only be using
|
|
// this if they've encountered a non-nil error.
|
|
//
|
|
// This is here just as a temporary measure to preserve the old treatment of
|
|
// errors returned from legacy helper/schema-based backend implementations,
|
|
// so that we can minimize the churn caused in the first iteration of adopting
|
|
// backendbase.
|
|
//
|
|
// In the long run backends should produce higher-quality diagnostics directly
|
|
// themselves, but we wanted to first complete the deprecation of the
|
|
// legacy/helper/schema package with only mechanical code updates and then
|
|
// save diagnostic quality improvements for a later time.
|
|
func ErrorAsDiagnostics(err error) tfdiags.Diagnostics {
|
|
if err == nil {
|
|
panic("ErrorAsDiagnostics with nil error")
|
|
}
|
|
var diags tfdiags.Diagnostics
|
|
// This produces a very low-quality diagnostic message, but it matches
|
|
// how legacy helper/schema dealt with the same situation.
|
|
diags = diags.Append(err)
|
|
return diags
|
|
}
|