mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
config: validate that multi-variables are only used in slices
This commit is contained in:
parent
75e79da9c3
commit
7b48924532
@ -388,6 +388,17 @@ func (c *Config) Validate() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that all variables are in the proper context
|
||||||
|
for source, rc := range c.rawConfigs() {
|
||||||
|
walker := &interpolationWalker{
|
||||||
|
ContextF: c.validateVarContextFn(source, &errs),
|
||||||
|
}
|
||||||
|
if err := reflectwalk.Walk(rc.Raw, walker); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf(
|
||||||
|
"%s: error reading config: %s", source, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return &multierror.Error{Errors: errs}
|
return &multierror.Error{Errors: errs}
|
||||||
}
|
}
|
||||||
@ -400,33 +411,57 @@ func (c *Config) Validate() error {
|
|||||||
// are valid in the Validate step.
|
// are valid in the Validate step.
|
||||||
func (c *Config) InterpolatedVariables() map[string][]InterpolatedVariable {
|
func (c *Config) InterpolatedVariables() map[string][]InterpolatedVariable {
|
||||||
result := make(map[string][]InterpolatedVariable)
|
result := make(map[string][]InterpolatedVariable)
|
||||||
for _, pc := range c.ProviderConfigs {
|
for source, rc := range c.rawConfigs() {
|
||||||
source := fmt.Sprintf("provider config '%s'", pc.Name)
|
for _, v := range rc.Variables {
|
||||||
for _, v := range pc.RawConfig.Variables {
|
|
||||||
result[source] = append(result[source], v)
|
result[source] = append(result[source], v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawConfigs returns all of the RawConfigs that are available keyed by
|
||||||
|
// a human-friendly source.
|
||||||
|
func (c *Config) rawConfigs() map[string]*RawConfig {
|
||||||
|
result := make(map[string]*RawConfig)
|
||||||
|
for _, pc := range c.ProviderConfigs {
|
||||||
|
source := fmt.Sprintf("provider config '%s'", pc.Name)
|
||||||
|
result[source] = pc.RawConfig
|
||||||
|
}
|
||||||
|
|
||||||
for _, rc := range c.Resources {
|
for _, rc := range c.Resources {
|
||||||
source := fmt.Sprintf("resource '%s'", rc.Id())
|
source := fmt.Sprintf("resource '%s'", rc.Id())
|
||||||
for _, v := range rc.RawCount.Variables {
|
result[source+" count"] = rc.RawCount
|
||||||
result[source] = append(result[source], v)
|
result[source+" config"] = rc.RawConfig
|
||||||
}
|
|
||||||
for _, v := range rc.RawConfig.Variables {
|
|
||||||
result[source] = append(result[source], v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range c.Outputs {
|
for _, o := range c.Outputs {
|
||||||
source := fmt.Sprintf("output '%s'", o.Name)
|
source := fmt.Sprintf("output '%s'", o.Name)
|
||||||
for _, v := range o.RawConfig.Variables {
|
result[source] = o.RawConfig
|
||||||
result[source] = append(result[source], v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) validateVarContextFn(
|
||||||
|
source string, errs *[]error) interpolationWalkerContextFunc {
|
||||||
|
return func(loc reflectwalk.Location, i Interpolation) {
|
||||||
|
vi, ok := i.(*VariableInterpolation)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rv, ok := vi.Variable.(*ResourceVariable)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if rv.Multi && loc != reflectwalk.SliceElem {
|
||||||
|
*errs = append(*errs, fmt.Errorf(
|
||||||
|
"%s: multi-variable must be in a slice", source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Module) mergerName() string {
|
func (m *Module) mergerName() string {
|
||||||
return m.Id()
|
return m.Id()
|
||||||
}
|
}
|
||||||
|
@ -221,6 +221,13 @@ func TestConfigValidate_varDefaultInterpolate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigValidate_varMultiNonSlice(t *testing.T) {
|
||||||
|
c := testConfig(t, "validate-var-multi-non-slice")
|
||||||
|
if err := c.Validate(); err == nil {
|
||||||
|
t.Fatal("should not be valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfigValidate_varModule(t *testing.T) {
|
func TestConfigValidate_varModule(t *testing.T) {
|
||||||
c := testConfig(t, "validate-var-module")
|
c := testConfig(t, "validate-var-module")
|
||||||
if err := c.Validate(); err != nil {
|
if err := c.Validate(); err != nil {
|
||||||
|
@ -22,8 +22,17 @@ var interpRegexp *regexp.Regexp = regexp.MustCompile(
|
|||||||
// (github.com/mitchellh/reflectwalk) that can be used to automatically
|
// (github.com/mitchellh/reflectwalk) that can be used to automatically
|
||||||
// execute a callback for an interpolation.
|
// execute a callback for an interpolation.
|
||||||
type interpolationWalker struct {
|
type interpolationWalker struct {
|
||||||
F interpolationWalkerFunc
|
// F is the function to call for every interpolation. It can be nil.
|
||||||
Replace bool
|
//
|
||||||
|
// If Replace is true, then the return value of F will be used to
|
||||||
|
// replace the interpolation.
|
||||||
|
F interpolationWalkerFunc
|
||||||
|
Replace bool
|
||||||
|
|
||||||
|
// ContextF is an advanced version of F that also receives the
|
||||||
|
// location of where it is in the structure. This lets you do
|
||||||
|
// context-aware validation.
|
||||||
|
ContextF interpolationWalkerContextFunc
|
||||||
|
|
||||||
key []string
|
key []string
|
||||||
lastValue reflect.Value
|
lastValue reflect.Value
|
||||||
@ -43,6 +52,14 @@ type interpolationWalker struct {
|
|||||||
// value can be anything as it will have no effect.
|
// value can be anything as it will have no effect.
|
||||||
type interpolationWalkerFunc func(Interpolation) (string, error)
|
type interpolationWalkerFunc func(Interpolation) (string, error)
|
||||||
|
|
||||||
|
// interpolationWalkerContextFunc is called by interpolationWalk if
|
||||||
|
// ContextF is set. This receives both the interpolation and the location
|
||||||
|
// where the interpolation is.
|
||||||
|
//
|
||||||
|
// This callback can be used to validate the location of the interpolation
|
||||||
|
// within the configuration.
|
||||||
|
type interpolationWalkerContextFunc func(reflectwalk.Location, Interpolation)
|
||||||
|
|
||||||
func (w *interpolationWalker) Enter(loc reflectwalk.Location) error {
|
func (w *interpolationWalker) Enter(loc reflectwalk.Location) error {
|
||||||
w.loc = loc
|
w.loc = loc
|
||||||
return nil
|
return nil
|
||||||
@ -129,6 +146,14 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if w.ContextF != nil {
|
||||||
|
w.ContextF(w.loc, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.F == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
replaceVal, err := w.F(i)
|
replaceVal, err := w.F(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
resource "aws_instance" "foo" {
|
||||||
|
count = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "bar" {
|
||||||
|
foo = "${aws_instance.foo.*.id}"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user