2015-07-10 22:08:49 +02:00
package terraform
import (
2018-09-05 23:35:30 +02:00
"errors"
2015-07-10 22:08:49 +02:00
"fmt"
"strings"
"testing"
2018-05-05 04:24:06 +02:00
"github.com/zclconf/go-cty/cty"
2018-05-11 23:52:29 +02:00
2021-05-17 21:00:50 +02:00
"github.com/hashicorp/terraform/internal/addrs"
2021-05-17 21:17:09 +02:00
"github.com/hashicorp/terraform/internal/configs/configschema"
2021-05-17 19:40:40 +02:00
"github.com/hashicorp/terraform/internal/providers"
2021-05-17 19:51:48 +02:00
"github.com/hashicorp/terraform/internal/provisioners"
2021-05-17 21:43:35 +02:00
"github.com/hashicorp/terraform/internal/states"
2021-05-17 19:11:06 +02:00
"github.com/hashicorp/terraform/internal/tfdiags"
2015-07-10 22:08:49 +02:00
)
2016-08-16 22:48:12 +02:00
func TestContext2Validate_badCount ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema ( & ProviderSchema {
2018-05-12 00:30:27 +02:00
ResourceTypes : map [ string ] * configschema . Block {
"aws_instance" : {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
} ,
2021-01-11 21:45:50 +01:00
} )
2018-05-12 00:30:27 +02:00
2016-08-16 22:48:12 +02:00
m := testModule ( t , "validate-bad-count" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2016-08-16 22:48:12 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2016-08-16 22:48:12 +02:00
}
}
2020-06-02 15:01:12 +02:00
func TestContext2Validate_badResource_reference ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema ( & ProviderSchema {
2020-06-02 15:01:12 +02:00
ResourceTypes : map [ string ] * configschema . Block {
"aws_instance" : {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
} ,
2021-01-11 21:45:50 +01:00
} )
2020-06-02 15:01:12 +02:00
m := testModule ( t , "validate-bad-resource-count" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2016-08-16 22:48:12 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2016-08-16 22:48:12 +02:00
}
}
2015-07-10 22:08:49 +02:00
func TestContext2Validate_badVar ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema ( & ProviderSchema {
2018-05-12 00:30:27 +02:00
ResourceTypes : map [ string ] * configschema . Block {
"aws_instance" : {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
"num" : { Type : cty . String , Optional : true } ,
} ,
} ,
} ,
2021-01-11 21:45:50 +01:00
} )
2018-05-12 00:30:27 +02:00
2015-07-10 22:08:49 +02:00
m := testModule ( t , "validate-bad-var" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2016-01-21 20:33:16 +01:00
}
}
func TestContext2Validate_varNoDefaultExplicitType ( t * testing . T ) {
m := testModule ( t , "validate-var-no-default-explicit-type" )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
c , diags := NewContext ( & ContextOpts { } )
if diags . HasErrors ( ) {
t . Fatalf ( "unexpected NewContext errors: %s" , diags . Err ( ) )
}
// NOTE: This test has grown idiosyncratic because originally Terraform
// would (optionally) check variables during validation, and then in
// Terraform v0.12 we switched to checking variables during NewContext,
// and now most recently we've switched to checking variables only during
// planning because root variables are a plan option. Therefore this has
// grown into a plan test rather than a validate test, but it lives on
// here in order to make it easier to navigate through that history in
// version control.
_ , diags = c . Plan ( m , states . NewState ( ) , DefaultPlanOpts )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2019-10-08 00:24:16 +02:00
// Error should be: The input variable "maybe_a_map" has not been assigned a value.
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_computedVar ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
Provider : providers . Schema {
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"value" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
}
pt := testProvider ( "test" )
2021-02-18 16:13:43 +01:00
pt . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"test_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"id" : { Type : cty . String , Computed : true } ,
"value" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
m := testModule ( t , "validate-computed-var" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
addrs . NewDefaultProvider ( "test" ) : testProviderFuncFixed ( pt ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2021-02-18 16:13:43 +01:00
p . ValidateProviderConfigFn = func ( req providers . ValidateProviderConfigRequest ) ( resp providers . ValidateProviderConfigResponse ) {
2020-10-08 18:43:46 +02:00
val := req . Config . GetAttr ( "value" )
if val . IsKnown ( ) {
resp . Diagnostics = resp . Diagnostics . Append ( fmt . Errorf ( "value isn't computed" ) )
2015-07-10 22:08:49 +02:00
}
2020-10-08 18:43:46 +02:00
return
2015-07-10 22:08:49 +02:00
}
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
2021-02-18 16:13:43 +01:00
if p . ConfigureProviderCalled {
2020-10-08 18:26:12 +02:00
t . Fatal ( "Configure should not be called for provider" )
}
2015-07-10 22:08:49 +02:00
}
2018-10-02 10:45:17 +02:00
func TestContext2Validate_computedInFunction ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-10-02 10:45:17 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"attr" : { Type : cty . Number , Optional : true } ,
} ,
2018-10-02 10:45:17 +02:00
} ,
} ,
} ,
2021-01-11 21:45:50 +01:00
DataSources : map [ string ] providers . Schema {
2018-10-02 10:45:17 +02:00
"aws_data_source" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"optional_attr" : { Type : cty . String , Optional : true } ,
"computed" : { Type : cty . String , Computed : true } ,
} ,
2018-10-02 10:45:17 +02:00
} ,
} ,
} ,
}
m := testModule ( t , "validate-computed-in-function" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2018-10-02 10:45:17 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2018-10-02 10:45:17 +02:00
if diags . HasErrors ( ) {
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
}
}
2017-01-28 06:15:43 +01:00
// 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" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
2021-01-11 21:45:50 +01:00
DataSources : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_data_source" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"compute" : { Type : cty . String , Optional : true } ,
"value" : { Type : cty . String , Computed : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2017-01-28 06:15:43 +01:00
m := testModule ( t , "validate-count-computed" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2017-01-28 06:15:43 +01:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2017-01-28 06:15:43 +01:00
}
}
2015-07-10 22:08:49 +02:00
func TestContext2Validate_countNegative ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
}
2015-07-10 22:08:49 +02:00
m := testModule ( t , "validate-count-negative" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_countVariable ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
m := testModule ( t , "apply-count-variable" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_countVariableNoDefault ( t * testing . T ) {
p := testProvider ( "aws" )
m := testModule ( t , "validate-count-variable" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
2017-04-22 02:40:46 +02:00
} ,
2018-05-12 00:30:27 +02:00
} ,
2015-07-10 22:08:49 +02:00
}
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
c , diags := NewContext ( & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
assertNoDiagnostics ( t , diags )
_ , diags = c . Plan ( m , nil , & PlanOpts { } )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2019-10-08 00:24:16 +02:00
// Error should be: The input variable "foo" has not been assigned a value.
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_moduleBadOutput ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
m := testModule ( t , "validate-bad-module-output" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_moduleGood ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
m := testModule ( t , "validate-good-module" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_moduleBadResource ( t * testing . T ) {
m := testModule ( t , "validate-module-bad-rc" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
}
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2021-02-18 16:13:43 +01:00
p . ValidateResourceConfigResponse = & providers . ValidateResourceConfigResponse {
2018-09-05 23:35:30 +02:00
Diagnostics : tfdiags . Diagnostics { } . Append ( fmt . Errorf ( "bad" ) ) ,
}
2015-07-10 22:08:49 +02:00
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_moduleDepsShouldNotCycle ( t * testing . T ) {
m := testModule ( t , "validate-module-deps-cycle" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"id" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_moduleProviderVar ( t * testing . T ) {
m := testModule ( t , "validate-module-pc-vars" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
Provider : providers . Schema {
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2021-02-18 16:13:43 +01:00
p . ValidateProviderConfigFn = func ( req providers . ValidateProviderConfigRequest ) ( resp providers . ValidateProviderConfigResponse ) {
2020-10-08 18:43:46 +02:00
if req . Config . GetAttr ( "foo" ) . IsNull ( ) {
resp . Diagnostics = resp . Diagnostics . Append ( errors . New ( "foo is null" ) )
}
return
2015-07-10 22:08:49 +02:00
}
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_moduleProviderInheritUnused ( t * testing . T ) {
m := testModule ( t , "validate-module-pc-inherit-unused" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
Provider : providers . Schema {
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2021-02-18 16:13:43 +01:00
p . ValidateProviderConfigFn = func ( req providers . ValidateProviderConfigRequest ) ( resp providers . ValidateProviderConfigResponse ) {
2020-10-08 18:43:46 +02:00
if req . Config . GetAttr ( "foo" ) . IsNull ( ) {
resp . Diagnostics = resp . Diagnostics . Append ( errors . New ( "foo is null" ) )
}
return
2015-07-10 22:08:49 +02:00
}
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_orphans ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
"num" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
m := testModule ( t , "validate-good" )
2020-04-01 19:13:40 +02:00
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2021-02-18 16:13:43 +01:00
p . ValidateResourceConfigFn = func ( req providers . ValidateResourceConfigRequest ) providers . ValidateResourceConfigResponse {
2018-09-05 23:35:30 +02:00
var diags tfdiags . Diagnostics
if req . Config . GetAttr ( "foo" ) . IsNull ( ) {
2020-11-20 19:21:56 +01:00
diags = diags . Append ( errors . New ( "foo is not set" ) )
2018-09-05 23:35:30 +02:00
}
2021-02-18 16:13:43 +01:00
return providers . ValidateResourceConfigResponse {
2018-09-05 23:35:30 +02:00
Diagnostics : diags ,
}
2015-07-10 22:08:49 +02:00
}
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_providerConfig_bad ( t * testing . T ) {
m := testModule ( t , "validate-bad-pc" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
Provider : providers . Schema {
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
}
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2021-02-18 16:13:43 +01:00
p . ValidateProviderConfigResponse = & providers . ValidateProviderConfigResponse {
2018-09-05 23:35:30 +02:00
Diagnostics : tfdiags . Diagnostics { } . Append ( fmt . Errorf ( "bad" ) ) ,
}
2015-07-10 22:08:49 +02:00
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if len ( diags ) != 1 {
t . Fatalf ( "wrong number of diagnostics %d; want %d" , len ( diags ) , 1 )
2015-07-10 22:08:49 +02:00
}
2017-11-22 00:08:00 +01:00
if ! strings . Contains ( diags . Err ( ) . Error ( ) , "bad" ) {
t . Fatalf ( "bad: %s" , diags . Err ( ) . Error ( ) )
2015-07-10 22:08:49 +02:00
}
}
2020-05-08 07:26:21 +02:00
func TestContext2Validate_providerConfig_skippedEmpty ( t * testing . T ) {
2020-12-06 18:35:35 +01:00
m := testModule ( t , "validate-skipped-pc-empty" )
2015-07-10 22:08:49 +02:00
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
Provider : providers . Schema {
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
}
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2021-02-18 16:13:43 +01:00
p . ValidateProviderConfigResponse = & providers . ValidateProviderConfigResponse {
2020-05-08 07:23:11 +02:00
Diagnostics : tfdiags . Diagnostics { } . Append ( fmt . Errorf ( "should not be called" ) ) ,
2018-09-05 23:35:30 +02:00
}
2015-07-10 22:08:49 +02:00
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2020-05-08 07:23:11 +02:00
if diags . HasErrors ( ) {
2020-11-10 01:26:17 +01:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_providerConfig_good ( t * testing . T ) {
m := testModule ( t , "validate-bad-pc" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
Provider : providers . Schema {
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
}
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
2020-10-13 15:21:05 +02:00
// In this test there is a mismatch between the provider's fqn (hashicorp/test)
// and it's local name set in required_providers (arbitrary).
func TestContext2Validate_requiredProviderConfig ( t * testing . T ) {
m := testModule ( t , "validate-required-provider-config" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
Provider : providers . Schema {
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"required_attribute" : { Type : cty . String , Required : true } ,
} ,
2020-10-13 15:21:05 +02:00
} ,
} ,
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2020-10-13 15:21:05 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute { } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
}
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_provisionerConfig_bad ( t * testing . T ) {
m := testModule ( t , "validate-bad-prov-conf" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
pr := simpleMockProvisioner ( )
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2020-11-13 18:43:28 +01:00
Provisioners : map [ string ] provisioners . Factory {
2015-07-10 22:08:49 +02:00
"shell" : testProvisionerFuncFixed ( pr ) ,
} ,
} )
2021-02-18 16:13:43 +01:00
p . ValidateProviderConfigResponse = & providers . ValidateProviderConfigResponse {
2018-09-05 23:35:30 +02:00
Diagnostics : tfdiags . Diagnostics { } . Append ( fmt . Errorf ( "bad" ) ) ,
}
2015-07-10 22:08:49 +02:00
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2015-07-10 22:08:49 +02:00
}
}
2019-03-26 14:11:43 +01:00
func TestContext2Validate_badResourceConnection ( t * testing . T ) {
m := testModule ( t , "validate-bad-resource-connection" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2019-03-26 14:11:43 +01:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2019-03-26 14:11:43 +01:00
} ,
} ,
} ,
}
pr := simpleMockProvisioner ( )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2020-11-13 18:43:28 +01:00
Provisioners : map [ string ] provisioners . Factory {
2019-03-26 14:11:43 +01:00
"shell" : testProvisionerFuncFixed ( pr ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2019-03-26 14:11:43 +01:00
t . Log ( diags . Err ( ) )
if ! diags . HasErrors ( ) {
t . Fatalf ( "succeeded; want error" )
}
}
func TestContext2Validate_badProvisionerConnection ( t * testing . T ) {
m := testModule ( t , "validate-bad-prov-connection" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2019-03-26 14:11:43 +01:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2019-03-26 14:11:43 +01:00
} ,
} ,
} ,
}
pr := simpleMockProvisioner ( )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2020-11-13 18:43:28 +01:00
Provisioners : map [ string ] provisioners . Factory {
2019-03-26 14:11:43 +01:00
"shell" : testProvisionerFuncFixed ( pr ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2019-03-26 14:11:43 +01:00
t . Log ( diags . Err ( ) )
if ! diags . HasErrors ( ) {
t . Fatalf ( "succeeded; want error" )
}
}
2015-07-10 22:08:49 +02:00
func TestContext2Validate_provisionerConfig_good ( t * testing . T ) {
m := testModule ( t , "validate-bad-prov-conf" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
Provider : providers . Schema {
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
pr := simpleMockProvisioner ( )
2018-09-05 23:35:30 +02:00
pr . ValidateProvisionerConfigFn = func ( req provisioners . ValidateProvisionerConfigRequest ) provisioners . ValidateProvisionerConfigResponse {
var diags tfdiags . Diagnostics
if req . Config . GetAttr ( "test_string" ) . IsNull ( ) {
2020-11-20 19:21:56 +01:00
diags = diags . Append ( errors . New ( "test_string is not set" ) )
2018-09-05 23:35:30 +02:00
}
return provisioners . ValidateProvisionerConfigResponse {
Diagnostics : diags ,
2015-07-10 22:08:49 +02:00
}
}
2018-05-12 00:30:27 +02:00
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2020-11-13 18:43:28 +01:00
Provisioners : map [ string ] provisioners . Factory {
2015-07-10 22:08:49 +02:00
"shell" : testProvisionerFuncFixed ( pr ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_requiredVar ( t * testing . T ) {
m := testModule ( t , "validate-required-var" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"ami" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
c , diags := NewContext ( & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
assertNoDiagnostics ( t , diags )
// NOTE: This test has grown idiosyncratic because originally Terraform
// would (optionally) check variables during validation, and then in
// Terraform v0.12 we switched to checking variables during NewContext,
// and now most recently we've switched to checking variables only during
// planning because root variables are a plan option. Therefore this has
// grown into a plan test rather than a validate test, but it lives on
// here in order to make it easier to navigate through that history in
// version control.
_ , diags = c . Plan ( m , states . NewState ( ) , DefaultPlanOpts )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2019-10-08 00:24:16 +02:00
// Error should be: The input variable "foo" has not been assigned a value.
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_resourceConfig_bad ( t * testing . T ) {
m := testModule ( t , "validate-bad-rc" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2021-02-18 16:13:43 +01:00
p . ValidateResourceConfigResponse = & providers . ValidateResourceConfigResponse {
2018-09-05 23:35:30 +02:00
Diagnostics : tfdiags . Diagnostics { } . Append ( fmt . Errorf ( "bad" ) ) ,
}
2015-07-10 22:08:49 +02:00
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if ! diags . HasErrors ( ) {
2018-05-12 00:30:27 +02:00
t . Fatalf ( "succeeded; want error" )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_resourceConfig_good ( t * testing . T ) {
m := testModule ( t , "validate-bad-rc" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
2017-04-22 02:40:46 +02:00
} ,
2018-05-12 00:30:27 +02:00
} ,
2015-07-10 22:08:49 +02:00
}
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2018-05-12 00:30:27 +02:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_tainted ( t * testing . T ) {
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
"num" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
m := testModule ( t , "validate-good" )
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2021-02-18 16:13:43 +01:00
p . ValidateResourceConfigFn = func ( req providers . ValidateResourceConfigRequest ) providers . ValidateResourceConfigResponse {
2018-09-05 23:35:30 +02:00
var diags tfdiags . Diagnostics
if req . Config . GetAttr ( "foo" ) . IsNull ( ) {
2020-11-20 19:21:56 +01:00
diags = diags . Append ( errors . New ( "foo is not set" ) )
2018-09-05 23:35:30 +02:00
}
2021-02-18 16:13:43 +01:00
return providers . ValidateResourceConfigResponse {
2018-09-05 23:35:30 +02:00
Diagnostics : diags ,
}
2015-07-10 22:08:49 +02:00
}
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
func TestContext2Validate_targetedDestroy ( t * testing . T ) {
m := testModule ( t , "validate-targeted" )
p := testProvider ( "aws" )
2018-05-12 00:30:27 +02:00
pr := simpleMockProvisioner ( )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
"num" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2020-04-01 19:13:40 +02:00
state := states . NewState ( )
root := state . EnsureModule ( addrs . RootModuleInstance )
testSetResourceInstanceCurrent ( root , "aws_instance.foo" , ` { "id":"i-bcd345"} ` , ` provider["registry.terraform.io/hashicorp/aws"] ` )
testSetResourceInstanceCurrent ( root , "aws_instance.bar" , ` { "id":"i-abc123"} ` , ` provider["registry.terraform.io/hashicorp/aws"] ` )
2015-07-10 22:08:49 +02:00
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2020-11-13 18:43:28 +01:00
Provisioners : map [ string ] provisioners . Factory {
2015-07-10 22:08:49 +02:00
"shell" : testProvisionerFuncFixed ( pr ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2015-07-10 22:08:49 +02:00
}
}
2019-04-17 17:48:39 +02:00
func TestContext2Validate_varRefUnknown ( t * testing . T ) {
2015-07-10 22:08:49 +02:00
m := testModule ( t , "validate-variable-ref" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2015-07-10 22:08:49 +02:00
c := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2015-07-10 22:08:49 +02:00
} )
2018-09-05 23:35:30 +02:00
var value cty . Value
2021-02-18 16:13:43 +01:00
p . ValidateResourceConfigFn = func ( req providers . ValidateResourceConfigRequest ) providers . ValidateResourceConfigResponse {
2018-09-05 23:35:30 +02:00
value = req . Config . GetAttr ( "foo" )
2021-02-18 16:13:43 +01:00
return providers . ValidateResourceConfigResponse { }
2015-07-10 22:08:49 +02:00
}
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
c . Validate ( m )
2019-04-17 17:48:39 +02:00
// Input variables are always unknown during the validate walk, because
// we're checking for validity of all possible input values. Validity
// against specific input values is checked during the plan walk.
if ! value . RawEquals ( cty . UnknownVal ( cty . String ) ) {
2015-07-10 22:08:49 +02:00
t . Fatalf ( "bad: %#v" , value )
}
}
2016-05-23 18:46:06 +02:00
// Module variables weren't being interpolated during Validate phase.
// related to https://github.com/hashicorp/terraform/issues/5322
func TestContext2Validate_interpolateVar ( t * testing . T ) {
input := new ( MockUIInput )
m := testModule ( t , "input-interpolate-var" )
p := testProvider ( "null" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-12 00:30:27 +02:00
"template_file" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"template" : { Type : cty . String , Optional : true } ,
} ,
2018-05-12 00:30:27 +02:00
} ,
} ,
} ,
}
2016-05-23 18:46:06 +02:00
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "template" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2016-05-23 18:46:06 +02:00
UIInput : input ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2016-05-23 18:46:06 +02:00
}
}
2016-05-23 22:54:14 +02:00
// When module vars reference something that is actually computed, this
// shouldn't cause validation to fail.
func TestContext2Validate_interpolateComputedModuleVarDef ( t * testing . T ) {
input := new ( MockUIInput )
m := testModule ( t , "validate-computed-module-var-ref" )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2018-05-11 23:52:29 +02:00
"aws_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"attr" : { Type : cty . String , Optional : true } ,
} ,
2018-05-11 23:52:29 +02:00
} ,
} ,
} ,
}
2016-05-23 22:54:14 +02:00
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2016-05-23 22:54:14 +02:00
UIInput : input ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2016-05-23 22:54:14 +02:00
}
}
2016-10-26 22:05:50 +02:00
// Computed values are lost when a map is output from a module
func TestContext2Validate_interpolateMap ( t * testing . T ) {
input := new ( MockUIInput )
m := testModule ( t , "issue-9549" )
2018-06-01 20:06:25 +02:00
p := testProvider ( "template" )
2016-10-26 22:05:50 +02:00
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "template" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2016-10-26 22:05:50 +02:00
UIInput : input ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2017-11-22 00:08:00 +01:00
if diags . HasErrors ( ) {
2018-05-11 23:52:29 +02:00
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
2016-10-26 22:05:50 +02:00
}
}
2016-12-02 16:53:29 +01:00
2020-11-20 16:58:40 +01:00
func TestContext2Validate_varSensitive ( t * testing . T ) {
// Smoke test through validate where a variable has sensitive applied
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
variable "foo" {
default = "xyz"
sensitive = true
}
variable "bar" {
sensitive = true
}
data "aws_data_source" "bar" {
foo = var . bar
}
resource "aws_instance" "foo" {
foo = var . foo
}
` ,
} )
p := testProvider ( "aws" )
2021-02-18 16:13:43 +01:00
p . ValidateResourceConfigFn = func ( req providers . ValidateResourceConfigRequest ) providers . ValidateResourceConfigResponse {
2020-11-20 16:58:40 +01:00
// Providers receive unmarked values
if got , want := req . Config . GetAttr ( "foo" ) , cty . UnknownVal ( cty . String ) ; ! got . RawEquals ( want ) {
t . Fatalf ( "wrong value for foo\ngot: %#v\nwant: %#v" , got , want )
}
2021-02-18 16:13:43 +01:00
return providers . ValidateResourceConfigResponse { }
2020-11-20 16:58:40 +01:00
}
2021-02-24 18:04:28 +01:00
p . ValidateDataResourceConfigFn = func ( req providers . ValidateDataResourceConfigRequest ) ( resp providers . ValidateDataResourceConfigResponse ) {
2020-11-20 16:58:40 +01:00
if got , want := req . Config . GetAttr ( "foo" ) , cty . UnknownVal ( cty . String ) ; ! got . RawEquals ( want ) {
t . Fatalf ( "wrong value for foo\ngot: %#v\nwant: %#v" , got , want )
}
2021-02-24 18:04:28 +01:00
return providers . ValidateDataResourceConfigResponse { }
2020-11-20 16:58:40 +01:00
}
ctx := testContext2 ( t , & ContextOpts {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2020-11-20 16:58:40 +01:00
if diags . HasErrors ( ) {
t . Fatal ( diags . Err ( ) )
}
2021-02-18 16:13:43 +01:00
if ! p . ValidateResourceConfigCalled {
t . Fatal ( "expected ValidateResourceConfigFn to be called" )
2020-11-20 16:58:40 +01:00
}
2021-02-24 18:04:28 +01:00
if ! p . ValidateDataResourceConfigCalled {
2020-11-20 16:58:40 +01:00
t . Fatal ( "expected ValidateDataSourceConfigFn to be called" )
}
}
2018-05-22 20:16:08 +02:00
func TestContext2Validate_invalidOutput ( t * testing . T ) {
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
data "aws_data_source" "name" { }
output "out" {
value = "${data.aws_data_source.name.missing}"
} ` ,
} )
p := testProvider ( "aws" )
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2018-05-22 20:16:08 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2018-05-22 20:16:08 +02:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
2018-11-21 02:25:05 +01:00
// Should get this error:
// Unsupported attribute: This object does not have an attribute named "missing"
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , "Unsupported attribute" ; ! strings . Contains ( got , want ) {
2018-11-21 02:25:05 +01:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
2018-05-22 20:16:08 +02:00
}
func TestContext2Validate_invalidModuleOutput ( t * testing . T ) {
m := testModuleInline ( t , map [ string ] string {
"child/main.tf" : `
data "aws_data_source" "name" { }
output "out" {
value = "${data.aws_data_source.name.missing}"
} ` ,
"main.tf" : `
module "child" {
source = "./child"
}
resource "aws_instance" "foo" {
foo = "${module.child.out}"
} ` ,
} )
p := testProvider ( "aws" )
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2018-05-22 20:16:08 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2018-05-22 20:16:08 +02:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
2018-11-21 02:25:05 +01:00
// Should get this error:
// Unsupported attribute: This object does not have an attribute named "missing"
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , "Unsupported attribute" ; ! strings . Contains ( got , want ) {
2018-11-21 02:25:05 +01:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
}
2021-04-21 00:49:54 +02:00
func TestContext2Validate_sensitiveRootModuleOutput ( t * testing . T ) {
2020-11-05 22:02:40 +01:00
m := testModuleInline ( t , map [ string ] string {
"child/main.tf" : `
variable "foo" {
default = "xyz"
sensitive = true
}
output "out" {
value = var . foo
} ` ,
"main.tf" : `
module "child" {
source = "./child"
}
2021-04-21 00:49:54 +02:00
output "root" {
value = module . child . out
sensitive = true
2020-11-05 22:02:40 +01:00
} ` ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
ctx := testContext2 ( t , & ContextOpts { } )
2020-11-05 22:02:40 +01:00
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2021-04-21 00:49:54 +02:00
if diags . HasErrors ( ) {
t . Fatal ( diags . Err ( ) )
2018-11-21 02:25:05 +01:00
}
}
func TestContext2Validate_legacyResourceCount ( t * testing . T ) {
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
resource "aws_instance" "test" { }
output "out" {
value = aws_instance . test . count
} ` ,
} )
p := testProvider ( "aws" )
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2018-11-21 02:25:05 +01:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2018-11-21 02:25:05 +01:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
// Should get this error:
// Invalid resource count attribute: The special "count" attribute is no longer supported after Terraform v0.12. Instead, use length(aws_instance.test) to count resource instances.
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , "Invalid resource count attribute:" ; ! strings . Contains ( got , want ) {
2018-11-21 02:25:05 +01:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
2018-05-22 20:16:08 +02:00
}
2018-11-28 20:25:44 +01:00
func TestContext2Validate_invalidModuleRef ( t * testing . T ) {
// This test is verifying that we properly validate and report on references
// to modules that are not declared, since we were missing some validation
// here in early 0.12.0 alphas that led to a panic.
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
output "out" {
# Intentionally referencing undeclared module to ensure error
value = module . foo
} ` ,
} )
p := testProvider ( "aws" )
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2018-11-28 20:25:44 +01:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2018-11-28 20:25:44 +01:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
// Should get this error:
// Reference to undeclared module: No module call named "foo" is declared in the root module.
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , "Reference to undeclared module:" ; ! strings . Contains ( got , want ) {
2018-11-28 20:25:44 +01:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
}
func TestContext2Validate_invalidModuleOutputRef ( t * testing . T ) {
// This test is verifying that we properly validate and report on references
// to modules that are not declared, since we were missing some validation
// here in early 0.12.0 alphas that led to a panic.
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
output "out" {
# Intentionally referencing undeclared module to ensure error
value = module . foo . bar
} ` ,
} )
p := testProvider ( "aws" )
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2018-11-28 20:25:44 +01:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2018-11-28 20:25:44 +01:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
// Should get this error:
// Reference to undeclared module: No module call named "foo" is declared in the root module.
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , "Reference to undeclared module:" ; ! strings . Contains ( got , want ) {
2018-11-28 20:25:44 +01:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
}
2018-12-15 02:14:17 +01:00
func TestContext2Validate_invalidDependsOnResourceRef ( t * testing . T ) {
// This test is verifying that we raise an error if depends_on
// refers to something that doesn't exist in configuration.
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
resource "test_instance" "bar" {
depends_on = [ test_resource . nonexistant ]
}
` ,
} )
p := testProvider ( "test" )
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "test" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2018-12-15 02:14:17 +01:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2018-12-15 02:14:17 +01:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
// Should get this error:
// Reference to undeclared module: No module call named "foo" is declared in the root module.
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , "Reference to undeclared resource:" ; ! strings . Contains ( got , want ) {
2018-12-15 02:14:17 +01:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
}
func TestContext2Validate_invalidResourceIgnoreChanges ( t * testing . T ) {
// This test is verifying that we raise an error if ignore_changes
// refers to something that can be statically detected as not conforming
// to the resource type schema.
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
resource "test_instance" "bar" {
lifecycle {
ignore_changes = [ does_not_exist_in_schema ]
}
}
` ,
} )
p := testProvider ( "test" )
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "test" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2018-12-15 02:14:17 +01:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2018-12-15 02:14:17 +01:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
// Should get this error:
// Reference to undeclared module: No module call named "foo" is declared in the root module.
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , ` no argument, nested block, or exported attribute named "does_not_exist_in_schema" ` ; ! strings . Contains ( got , want ) {
2018-12-15 02:14:17 +01:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
}
2020-01-04 02:12:49 +01:00
func TestContext2Validate_variableCustomValidationsFail ( t * testing . T ) {
// This test is for custom validation rules associated with root module
// variables, and specifically that we handle the situation where the
// given value is invalid in a child module.
m := testModule ( t , "validate-variable-custom-validations-child" )
p := testProvider ( "test" )
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "test" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2020-01-04 02:12:49 +01:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2020-01-04 02:12:49 +01:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , ` Invalid value for variable: Value must not be "nope". ` ; ! strings . Contains ( got , want ) {
2020-01-04 02:12:49 +01:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
}
func TestContext2Validate_variableCustomValidationsRoot ( t * testing . T ) {
// This test is for custom validation rules associated with root module
// variables, and specifically that we handle the situation where their
// values are unknown during validation, skipping the validation check
// altogether. (Root module variables are never known during validation.)
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
variable "test" {
type = string
validation {
condition = var . test != "nope"
error_message = "Value must not be \"nope\"."
}
}
` ,
} )
p := testProvider ( "test" )
ctx := testContext2 ( t , & ContextOpts {
2020-03-31 20:03:33 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
2020-04-01 19:13:40 +02:00
addrs . NewDefaultProvider ( "test" ) : testProviderFuncFixed ( p ) ,
2020-03-31 20:03:33 +02:00
} ,
2020-01-04 02:12:49 +01:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2020-01-04 02:12:49 +01:00
if diags . HasErrors ( ) {
t . Fatalf ( "unexpected error\ngot: %s" , diags . Err ( ) . Error ( ) )
}
}
2020-04-05 17:17:28 +02:00
func TestContext2Validate_expandModules ( t * testing . T ) {
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
module "mod1" {
for_each = toset ( [ "a" , "b" ] )
source = "./mod"
}
module "mod2" {
for_each = module . mod1
source = "./mod"
2020-04-17 18:36:17 +02:00
input = module . mod1 [ "a" ] . out
2020-04-05 17:17:28 +02:00
}
module "mod3" {
2020-05-05 22:44:22 +02:00
count = length ( module . mod2 )
2020-04-05 17:17:28 +02:00
source = "./mod"
}
` ,
"mod/main.tf" : `
resource "aws_instance" "foo" {
}
2020-04-17 18:36:17 +02:00
output "out" {
value = 1
}
variable "input" {
type = number
default = 0
}
2020-04-05 17:17:28 +02:00
module "nested" {
count = 2
source = "./nested"
2020-04-08 01:30:18 +02:00
input = count . index
2020-04-05 17:17:28 +02:00
}
` ,
"mod/nested/main.tf" : `
variable "input" {
}
resource "aws_instance" "foo" {
count = var . input
}
` ,
} )
p := testProvider ( "aws" )
ctx := testContext2 ( t , & ContextOpts {
2020-04-06 18:50:37 +02:00
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
} ,
2020-04-05 17:17:28 +02:00
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2020-04-05 17:17:28 +02:00
if diags . HasErrors ( ) {
t . Fatal ( diags . ErrWithWarnings ( ) )
}
}
2020-05-05 22:44:22 +02:00
func TestContext2Validate_expandModulesInvalidCount ( t * testing . T ) {
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
module "mod1" {
count = - 1
source = "./mod"
}
` ,
"mod/main.tf" : `
resource "aws_instance" "foo" {
}
` ,
} )
p := testProvider ( "aws" )
ctx := testContext2 ( t , & ContextOpts {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2020-05-05 22:44:22 +02:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , ` Invalid count argument ` ; ! strings . Contains ( got , want ) {
2020-05-05 22:44:22 +02:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
}
func TestContext2Validate_expandModulesInvalidForEach ( t * testing . T ) {
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
module "mod1" {
for_each = [ "a" , "b" ]
source = "./mod"
}
` ,
"mod/main.tf" : `
resource "aws_instance" "foo" {
}
` ,
} )
p := testProvider ( "aws" )
ctx := testContext2 ( t , & ContextOpts {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2020-05-05 22:44:22 +02:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
2020-12-01 21:51:54 +01:00
if got , want := diags . Err ( ) . Error ( ) , ` Invalid for_each argument ` ; ! strings . Contains ( got , want ) {
2020-05-05 22:44:22 +02:00
t . Fatalf ( "wrong error:\ngot: %s\nwant: message containing %q" , got , want )
}
}
2020-06-07 03:27:29 +02:00
func TestContext2Validate_expandMultipleNestedModules ( t * testing . T ) {
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
module "modA" {
for_each = {
first = "m"
second = "n"
}
source = "./modA"
}
` ,
"modA/main.tf" : `
locals {
m = {
first = "m"
second = "n"
}
}
module "modB" {
for_each = local . m
source = "./modB"
y = each . value
}
module "modC" {
for_each = local . m
source = "./modC"
x = module . modB [ each . key ] . out
y = module . modB [ each . key ] . out
}
` ,
"modA/modB/main.tf" : `
variable "y" {
type = string
}
resource "aws_instance" "foo" {
foo = var . y
}
output "out" {
value = aws_instance . foo . id
}
` ,
"modA/modC/main.tf" : `
variable "x" {
type = string
}
variable "y" {
type = string
}
resource "aws_instance" "foo" {
foo = var . x
}
output "out" {
value = var . y
}
` ,
} )
p := testProvider ( "aws" )
ctx := testContext2 ( t , & ContextOpts {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2020-06-07 03:27:29 +02:00
if diags . HasErrors ( ) {
t . Fatal ( diags . ErrWithWarnings ( ) )
}
}
2020-06-16 19:17:21 +02:00
func TestContext2Validate_invalidModuleDependsOn ( t * testing . T ) {
// validate module and output depends_on
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
module "mod1" {
source = "./mod"
depends_on = [ resource_foo . bar . baz ]
}
module "mod2" {
source = "./mod"
depends_on = [ resource_foo . bar . baz ]
}
` ,
"mod/main.tf" : `
output "out" {
value = "foo"
}
` ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := testContext2 ( t , & ContextOpts { } ) . Validate ( m )
2020-06-16 19:17:21 +02:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
if len ( diags ) != 2 {
t . Fatalf ( "wanted 2 diagnostic errors, got %q" , diags )
}
for _ , d := range diags {
des := d . Description ( ) . Summary
if ! strings . Contains ( des , "Invalid depends_on reference" ) {
t . Fatalf ( ` expected "Invalid depends_on reference", got %q ` , des )
}
}
}
func TestContext2Validate_invalidOutputDependsOn ( t * testing . T ) {
// validate module and output depends_on
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
module "mod1" {
source = "./mod"
}
output "out" {
value = "bar"
depends_on = [ resource_foo . bar . baz ]
}
` ,
"mod/main.tf" : `
output "out" {
value = "bar"
depends_on = [ resource_foo . bar . baz ]
}
` ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := testContext2 ( t , & ContextOpts { } ) . Validate ( m )
2020-06-16 19:17:21 +02:00
if ! diags . HasErrors ( ) {
t . Fatal ( "succeeded; want errors" )
}
if len ( diags ) != 2 {
t . Fatalf ( "wanted 2 diagnostic errors, got %q" , diags )
}
for _ , d := range diags {
des := d . Description ( ) . Summary
if ! strings . Contains ( des , "Invalid depends_on reference" ) {
t . Fatalf ( ` expected "Invalid depends_on reference", got %q ` , des )
}
}
}
2020-09-25 22:59:58 +02:00
2020-10-28 19:51:04 +01:00
func TestContext2Validate_rpcDiagnostics ( t * testing . T ) {
// validate module and output depends_on
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
resource "test_instance" "a" {
}
` ,
} )
p := testProvider ( "test" )
2021-02-18 16:13:43 +01:00
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
2021-01-11 21:45:50 +01:00
ResourceTypes : map [ string ] providers . Schema {
2020-10-28 19:51:04 +01:00
"test_instance" : {
2021-01-11 21:45:50 +01:00
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"id" : { Type : cty . String , Computed : true } ,
} ,
2020-10-28 19:51:04 +01:00
} ,
} ,
} ,
}
2021-02-18 16:13:43 +01:00
p . ValidateResourceConfigResponse = & providers . ValidateResourceConfigResponse {
2020-10-28 19:51:04 +01:00
Diagnostics : tfdiags . Diagnostics ( nil ) . Append ( tfdiags . SimpleWarning ( "don't frobble" ) ) ,
}
ctx := testContext2 ( t , & ContextOpts {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "test" ) : testProviderFuncFixed ( p ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2020-10-28 19:51:04 +01:00
if diags . HasErrors ( ) {
t . Fatal ( diags . Err ( ) )
}
2020-10-28 22:38:01 +01:00
if len ( diags ) == 0 {
t . Fatal ( "expected warnings" )
}
2020-10-28 19:51:04 +01:00
for _ , d := range diags {
des := d . Description ( ) . Summary
if ! strings . Contains ( des , "frobble" ) {
t . Fatalf ( ` expected frobble, got %q ` , des )
}
}
}
2021-02-18 16:41:43 +01:00
func TestContext2Validate_sensitiveProvisionerConfig ( t * testing . T ) {
m := testModule ( t , "validate-sensitive-provisioner-config" )
p := testProvider ( "aws" )
p . GetProviderSchemaResponse = & providers . GetProviderSchemaResponse {
ResourceTypes : map [ string ] providers . Schema {
"aws_instance" : {
Block : & configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"foo" : { Type : cty . String , Optional : true } ,
} ,
} ,
} ,
} ,
}
pr := simpleMockProvisioner ( )
c := testContext2 ( t , & ContextOpts {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "aws" ) : testProviderFuncFixed ( p ) ,
} ,
Provisioners : map [ string ] provisioners . Factory {
"test" : testProvisionerFuncFixed ( pr ) ,
} ,
} )
pr . ValidateProvisionerConfigFn = func ( r provisioners . ValidateProvisionerConfigRequest ) provisioners . ValidateProvisionerConfigResponse {
if r . Config . ContainsMarked ( ) {
t . Errorf ( "provisioner config contains marked values" )
}
return pr . ValidateProvisionerConfigResponse
}
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := c . Validate ( m )
2021-02-18 16:41:43 +01:00
if diags . HasErrors ( ) {
t . Fatalf ( "unexpected error: %s" , diags . Err ( ) )
}
if ! pr . ValidateProvisionerConfigCalled {
t . Fatal ( "ValidateProvisionerConfig not called" )
}
}
2021-04-15 23:34:33 +02:00
func TestContext2Plan_validateMinMaxDynamicBlock ( t * testing . T ) {
p := new ( MockProvider )
p . GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema ( & ProviderSchema {
ResourceTypes : map [ string ] * configschema . Block {
"test_instance" : {
Attributes : map [ string ] * configschema . Attribute {
"id" : {
Type : cty . String ,
Computed : true ,
} ,
"things" : {
Type : cty . List ( cty . String ) ,
Computed : true ,
} ,
} ,
BlockTypes : map [ string ] * configschema . NestedBlock {
"foo" : {
Block : configschema . Block {
Attributes : map [ string ] * configschema . Attribute {
"bar" : { Type : cty . String , Optional : true } ,
} ,
} ,
Nesting : configschema . NestingList ,
MinItems : 2 ,
MaxItems : 3 ,
} ,
} ,
} ,
} ,
} )
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
resource "test_instance" "a" {
// MinItems 2
foo {
bar = "a"
}
foo {
bar = "b"
}
}
resource "test_instance" "b" {
// one dymamic block can satisfy MinItems of 2
dynamic "foo" {
for_each = test_instance . a . things
content {
bar = foo . value
}
}
}
resource "test_instance" "c" {
// we may have more than MaxItems dynamic blocks when they are unknown
foo {
bar = "b"
}
dynamic "foo" {
for_each = test_instance . a . things
content {
bar = foo . value
}
}
dynamic "foo" {
for_each = test_instance . a . things
content {
bar = "${foo.value}-2"
}
}
dynamic "foo" {
for_each = test_instance . b . things
content {
bar = foo . value
}
}
}
` } )
ctx := testContext2 ( t , & ContextOpts {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "test" ) : testProviderFuncFixed ( p ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2021-04-15 23:34:33 +02:00
if diags . HasErrors ( ) {
t . Fatal ( diags . ErrWithWarnings ( ) )
}
}
2021-05-03 22:17:41 +02:00
func TestContext2Validate_passInheritedProvider ( t * testing . T ) {
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
terraform {
required_providers {
test = {
source = "hashicorp/test"
}
}
}
module "first" {
source = "./first"
providers = {
test = test
}
}
` ,
// This module does not define a config for the test provider, but we
// should be able to pass whatever the implied config is to a child
// module.
"first/main.tf" : `
terraform {
required_providers {
test = {
source = "hashicorp/test"
}
}
}
module "second" {
source = "./second"
providers = {
test . alias = test
}
} ` ,
"first/second/main.tf" : `
terraform {
required_providers {
test = {
source = "hashicorp/test"
configuration_aliases = [ test . alias ]
}
}
}
resource "test_object" "t" {
provider = test . alias
}
` ,
} )
p := simpleMockProvider ( )
ctx := testContext2 ( t , & ContextOpts {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "test" ) : testProviderFuncFixed ( p ) ,
} ,
} )
core: Functional-style API for terraform.Context
Previously terraform.Context was built in an unfortunate way where all of
the data was provided up front in terraform.NewContext and then mutated
directly by subsequent operations. That made the data flow hard to follow,
commonly leading to bugs, and also meant that we were forced to take
various actions too early in terraform.NewContext, rather than waiting
until a more appropriate time during an operation.
This (enormous) commit changes terraform.Context so that its fields are
broadly just unchanging data about the execution context (current
workspace name, available plugins, etc) whereas the main data Terraform
works with arrives via individual method arguments and is returned in
return values.
Specifically, this means that terraform.Context no longer "has-a" config,
state, and "planned changes", instead holding on to those only temporarily
during an operation. The caller is responsible for propagating the outcome
of one step into the next step so that the data flow between operations is
actually visible.
However, since that's a change to the main entry points in the "terraform"
package, this commit also touches every file in the codebase which
interacted with those APIs. Most of the noise here is in updating tests
to take the same actions using the new API style, but this also affects
the main-code callers in the backends and in the command package.
My goal here was to refactor without changing observable behavior, but in
practice there are a couple externally-visible behavior variations here
that seemed okay in service of the broader goal:
- The "terraform graph" command is no longer hooked directly into the
core graph builders, because that's no longer part of the public API.
However, I did include a couple new Context functions whose contract
is to produce a UI-oriented graph, and _for now_ those continue to
return the physical graph we use for those operations. There's no
exported API for generating the "validate" and "eval" graphs, because
neither is particularly interesting in its own right, and so
"terraform graph" no longer supports those graph types.
- terraform.NewContext no longer has the responsibility for collecting
all of the provider schemas up front. Instead, we wait until we need
them. However, that means that some of our error messages now have a
slightly different shape due to unwinding through a differently-shaped
call stack. As of this commit we also end up reloading the schemas
multiple times in some cases, which is functionally acceptable but
likely represents a performance regression. I intend to rework this to
use caching, but I'm saving that for a later commit because this one is
big enough already.
The proximal reason for this change is to resolve the chicken/egg problem
whereby there was previously no single point where we could apply "moved"
statements to the previous run state before creating a plan. With this
change in place, we can now do that as part of Context.Plan, prior to
forking the input state into the three separate state artifacts we use
during planning.
However, this is at least the third project in a row where the previous
API design led to piling more functionality into terraform.NewContext and
then working around the incorrect order of operations that produces, so
I intend that by paying the cost/risk of this large diff now we can in
turn reduce the cost/risk of future projects that relate to our main
workflow actions.
2021-08-24 21:06:38 +02:00
diags := ctx . Validate ( m )
2021-05-03 22:17:41 +02:00
if diags . HasErrors ( ) {
t . Fatal ( diags . ErrWithWarnings ( ) )
}
}
2021-12-15 00:29:38 +01:00
func TestContext2Plan_lookupMismatchedObjectTypes ( t * testing . T ) {
p := new ( MockProvider )
p . GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema ( & ProviderSchema {
ResourceTypes : map [ string ] * configschema . Block {
"test_instance" : {
Attributes : map [ string ] * configschema . Attribute {
"id" : {
Type : cty . String ,
Computed : true ,
} ,
"things" : {
Type : cty . List ( cty . String ) ,
Optional : true ,
} ,
} ,
} ,
} ,
} )
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
variable "items" {
type = list ( string )
default = [ ]
}
resource "test_instance" "a" {
for_each = length ( var . items ) > 0 ? { default = { } } : { }
}
output "out" {
// Strictly speaking, this expression is incorrect because the map element
// type is a different type from the default value, and the lookup
// implementation expects to be able to convert the default to match the
// element type.
// There are two reasons this works which we need to maintain for
// compatibility. First during validation the 'test_instance.a' expression
// only returns a dynamic value, preventing any type comparison. Later during
// plan and apply 'test_instance.a' is an object and not a map, and the
// lookup implementation skips the type comparison when the keys are known
// statically.
value = lookup ( test_instance . a , "default" , { id = null } ) [ "id" ]
}
` } )
ctx := testContext2 ( t , & ContextOpts {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "test" ) : testProviderFuncFixed ( p ) ,
} ,
} )
diags := ctx . Validate ( m )
if diags . HasErrors ( ) {
t . Fatal ( diags . ErrWithWarnings ( ) )
}
}
2022-01-10 22:14:30 +01:00
func TestContext2Validate_nonNullableVariableDefaultValidation ( t * testing . T ) {
m := testModuleInline ( t , map [ string ] string {
"main.tf" : `
module "first" {
source = "./mod"
input = null
}
` ,
"mod/main.tf" : `
variable "input" {
type = string
default = "default"
nullable = false
// Validation expressions should receive the default with nullable=false and
// a null input.
validation {
condition = var . input != null
error_message = "Input cannot be null!"
}
}
` ,
} )
ctx := testContext2 ( t , & ContextOpts { } )
diags := ctx . Validate ( m )
if diags . HasErrors ( ) {
t . Fatal ( diags . ErrWithWarnings ( ) )
}
}