terraform: don't validate computed values in validate
This disables the computed value check for `count` during the validation pass. This enables partial support for #3888 or #1497: as long as the value is non-computed during the plan, complex values will work in counts. **Notably, this allows data source values to be present in counts!** The "count" value can be disabled during validation safely because we can treat it as if any field that uses `count.index` is computed for validation. We then validate a single instance (as if `count = 1`) just to make sure all required fields are set.
This commit is contained in:
parent
0ba3fcdc63
commit
cf46e1c3e0
|
@ -505,23 +505,17 @@ func (c *Config) Validate() error {
|
|||
"%s: resource count can't reference count variable: %s",
|
||||
n,
|
||||
v.FullKey()))
|
||||
case *ModuleVariable:
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"%s: resource count can't reference module variable: %s",
|
||||
n,
|
||||
v.FullKey()))
|
||||
case *ResourceVariable:
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"%s: resource count can't reference resource variable: %s",
|
||||
n,
|
||||
v.FullKey()))
|
||||
case *SimpleVariable:
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"%s: resource count can't reference variable: %s",
|
||||
n,
|
||||
v.FullKey()))
|
||||
case *UserVariable:
|
||||
|
||||
// Good
|
||||
case *ModuleVariable:
|
||||
case *ResourceVariable:
|
||||
case *UserVariable:
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown type in count var in %s: %T", n, v))
|
||||
}
|
||||
|
|
|
@ -254,13 +254,6 @@ func TestConfigValidate_countCountVar(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countModuleVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-module-var")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countNotInt(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-not-int")
|
||||
if err := c.Validate(); err == nil {
|
||||
|
@ -268,20 +261,6 @@ func TestConfigValidate_countNotInt(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countResourceVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-resource-var")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countResourceVarMulti(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-resource-var-multi")
|
||||
if err := c.Validate(); err == nil {
|
||||
t.Fatal("should not be valid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidate_countUserVar(t *testing.T) {
|
||||
c := testConfig(t, "validate-count-user-var")
|
||||
if err := c.Validate(); err != nil {
|
||||
|
|
|
@ -115,6 +115,28 @@ func TestContext2Validate_computedVar(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test that validate allows through computed counts. We do this and allow
|
||||
// them to fail during "plan" since we can't know if the computed values
|
||||
// can be realized during a plan.
|
||||
func TestContext2Validate_countComputed(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "validate-count-computed")
|
||||
c := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
w, e := c.Validate()
|
||||
if len(w) > 0 {
|
||||
t.Fatalf("bad: %#v", w)
|
||||
}
|
||||
if len(e) > 0 {
|
||||
t.Fatalf("bad: %s", e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Validate_countNegative(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "validate-count-negative")
|
||||
|
|
|
@ -7,6 +7,10 @@ type EvalSequence struct {
|
|||
|
||||
func (n *EvalSequence) Eval(ctx EvalContext) (interface{}, error) {
|
||||
for _, n := range n.Nodes {
|
||||
if n == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := EvalRaw(n, ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ func (n *EvalValidateCount) Eval(ctx EvalContext) (interface{}, error) {
|
|||
c[n.Resource.RawCount.Key] = "1"
|
||||
count = 1
|
||||
}
|
||||
err = nil
|
||||
|
||||
if count < 0 {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
|
|
|
@ -14,6 +14,14 @@ type NodeAbstractCountResource struct {
|
|||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodeAbstractCountResource) EvalTree() EvalNode {
|
||||
// We only check if the count is computed if we're not validating.
|
||||
// If we're validating we allow computed counts since they just turn
|
||||
// into more computed values.
|
||||
var evalCountCheckComputed EvalNode
|
||||
if !n.Validate {
|
||||
evalCountCheckComputed = &EvalCountCheckComputed{Resource: n.Config}
|
||||
}
|
||||
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
// The EvalTree for a plannable resource primarily involves
|
||||
|
@ -24,7 +32,8 @@ func (n *NodeAbstractCountResource) EvalTree() EvalNode {
|
|||
// into the proper number of instances.
|
||||
&EvalInterpolate{Config: n.Config.RawCount},
|
||||
|
||||
&EvalCountCheckComputed{Resource: n.Config},
|
||||
// Check if the count is computed
|
||||
evalCountCheckComputed,
|
||||
|
||||
// If validation is enabled, perform the validation
|
||||
&EvalIf{
|
||||
|
|
|
@ -26,10 +26,14 @@ func (n *NodeValidatableResource) DynamicExpand(ctx EvalContext) (*Graph, error)
|
|||
defer lock.RUnlock()
|
||||
|
||||
// Expand the resource count which must be available by now from EvalTree
|
||||
count, err := n.Config.Count()
|
||||
count := 1
|
||||
if n.Config.RawCount.Value() != unknownValue() {
|
||||
var err error
|
||||
count, err = n.Config.Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// The concrete resource factory we'll use
|
||||
concreteResource := func(a *NodeAbstractResource) dag.Vertex {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
data "aws_data_source" "foo" {
|
||||
compute = "value"
|
||||
}
|
||||
|
||||
resource "aws_instance" "bar" {
|
||||
count = "${data.aws_data_source.foo.value}"
|
||||
}
|
Loading…
Reference in New Issue