Merge pull request #19222 from hashicorp/jbardin/timeout
provider timeouts
This commit is contained in:
commit
28a881a670
|
@ -20,6 +20,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"test_resource": testResource(),
|
"test_resource": testResource(),
|
||||||
"test_resource_gh12183": testResourceGH12183(),
|
"test_resource_gh12183": testResourceGH12183(),
|
||||||
"test_resource_with_custom_diff": testResourceCustomDiff(),
|
"test_resource_with_custom_diff": testResourceCustomDiff(),
|
||||||
|
"test_resource_timeout": testResourceTimeout(),
|
||||||
},
|
},
|
||||||
DataSourcesMap: map[string]*schema.Resource{
|
DataSourcesMap: map[string]*schema.Resource{
|
||||||
"test_data_source": testDataSource(),
|
"test_data_source": testDataSource(),
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testResourceTimeout() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: testResourceTimeoutCreate,
|
||||||
|
Read: testResourceTimeoutRead,
|
||||||
|
Update: testResourceTimeoutUpdate,
|
||||||
|
Delete: testResourceTimeoutDelete,
|
||||||
|
|
||||||
|
Timeouts: &schema.ResourceTimeout{
|
||||||
|
Create: schema.DefaultTimeout(time.Second),
|
||||||
|
Update: schema.DefaultTimeout(time.Second),
|
||||||
|
Delete: schema.DefaultTimeout(time.Second),
|
||||||
|
},
|
||||||
|
|
||||||
|
Importer: &schema.ResourceImporter{
|
||||||
|
State: schema.ImportStatePassthrough,
|
||||||
|
},
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"create_delay": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"read_delay": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"update_delay": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"delete_delay": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResourceTimeoutCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
delayString := d.Get("create_delay").(string)
|
||||||
|
var delay time.Duration
|
||||||
|
var err error
|
||||||
|
if delayString != "" {
|
||||||
|
delay, err = time.ParseDuration(delayString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if delay > d.Timeout(schema.TimeoutCreate) {
|
||||||
|
return fmt.Errorf("timeout while creating resource")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("testId")
|
||||||
|
|
||||||
|
return testResourceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResourceTimeoutRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
delayString := d.Get("read_delay").(string)
|
||||||
|
var delay time.Duration
|
||||||
|
var err error
|
||||||
|
if delayString != "" {
|
||||||
|
delay, err = time.ParseDuration(delayString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if delay > d.Timeout(schema.TimeoutRead) {
|
||||||
|
return fmt.Errorf("timeout while reading resource")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResourceTimeoutUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
delayString := d.Get("update_delay").(string)
|
||||||
|
var delay time.Duration
|
||||||
|
var err error
|
||||||
|
if delayString != "" {
|
||||||
|
delay, err = time.ParseDuration(delayString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if delay > d.Timeout(schema.TimeoutUpdate) {
|
||||||
|
return fmt.Errorf("timeout while updating resource")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResourceTimeoutDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
delayString := d.Get("delete_delay").(string)
|
||||||
|
var delay time.Duration
|
||||||
|
var err error
|
||||||
|
if delayString != "" {
|
||||||
|
delay, err = time.ParseDuration(delayString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if delay > d.Timeout(schema.TimeoutDelete) {
|
||||||
|
return fmt.Errorf("timeout while deleting resource")
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResourceTimeout_create(t *testing.T) {
|
||||||
|
resource.UnitTest(t, resource.TestCase{
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckResourceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: strings.TrimSpace(`
|
||||||
|
resource "test_resource_timeout" "foo" {
|
||||||
|
create_delay = "2s"
|
||||||
|
timeouts {
|
||||||
|
create = "1s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`),
|
||||||
|
ExpectError: regexp.MustCompile("timeout while creating resource"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
func TestResourceTimeout_update(t *testing.T) {
|
||||||
|
resource.UnitTest(t, resource.TestCase{
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckResourceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: strings.TrimSpace(`
|
||||||
|
resource "test_resource_timeout" "foo" {
|
||||||
|
update_delay = "1s"
|
||||||
|
timeouts {
|
||||||
|
update = "1s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: strings.TrimSpace(`
|
||||||
|
resource "test_resource_timeout" "foo" {
|
||||||
|
update_delay = "2s"
|
||||||
|
timeouts {
|
||||||
|
update = "1s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`),
|
||||||
|
ExpectError: regexp.MustCompile("timeout while updating resource"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResourceTimeout_read(t *testing.T) {
|
||||||
|
resource.UnitTest(t, resource.TestCase{
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckResourceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: strings.TrimSpace(`
|
||||||
|
resource "test_resource_timeout" "foo" {
|
||||||
|
}
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: strings.TrimSpace(`
|
||||||
|
resource "test_resource_timeout" "foo" {
|
||||||
|
read_delay = "30m"
|
||||||
|
}
|
||||||
|
`),
|
||||||
|
ExpectError: regexp.MustCompile("timeout while reading resource"),
|
||||||
|
},
|
||||||
|
// we need to remove the read_delay so that the resource can be
|
||||||
|
// destroyed in the final step, but expect an error here from the
|
||||||
|
// pre-existing delay.
|
||||||
|
resource.TestStep{
|
||||||
|
Config: strings.TrimSpace(`
|
||||||
|
resource "test_resource_timeout" "foo" {
|
||||||
|
}
|
||||||
|
`),
|
||||||
|
ExpectError: regexp.MustCompile("timeout while reading resource"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
|
@ -412,20 +412,22 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
||||||
// helper/schema should always copy the ID over, but do it again just to be safe
|
// helper/schema should always copy the ID over, but do it again just to be safe
|
||||||
newInstanceState.Attributes["id"] = newInstanceState.ID
|
newInstanceState.Attributes["id"] = newInstanceState.ID
|
||||||
|
|
||||||
newConfigVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, block.ImpliedType())
|
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, block.ImpliedType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newConfigMP, err := msgpack.Marshal(newConfigVal, block.ImpliedType())
|
newStateVal = copyTimeoutValues(newStateVal, stateVal)
|
||||||
|
|
||||||
|
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.NewState = &proto.DynamicValue{
|
resp.NewState = &proto.DynamicValue{
|
||||||
Msgpack: newConfigMP,
|
Msgpack: newStateMP,
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
@ -461,9 +463,10 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
priorState.Meta = priorPrivate
|
priorState.Meta = priorPrivate
|
||||||
|
|
||||||
// turn the propsed state into a legacy configuration
|
// turn the proposed state into a legacy configuration
|
||||||
config := terraform.NewResourceConfigShimmed(proposedNewStateVal, block)
|
config := terraform.NewResourceConfigShimmed(proposedNewStateVal, block)
|
||||||
|
|
||||||
diff, err := s.provider.SimpleDiff(info, priorState, config)
|
diff, err := s.provider.SimpleDiff(info, priorState, config)
|
||||||
|
@ -488,6 +491,8 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plannedStateVal = copyTimeoutValues(plannedStateVal, proposedNewStateVal)
|
||||||
|
|
||||||
plannedMP, err := msgpack.Marshal(plannedStateVal, block.ImpliedType())
|
plannedMP, err := msgpack.Marshal(plannedStateVal, block.ImpliedType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
|
@ -498,12 +503,14 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
||||||
}
|
}
|
||||||
|
|
||||||
// the Meta field gets encoded into PlannedPrivate
|
// the Meta field gets encoded into PlannedPrivate
|
||||||
|
if diff.Meta != nil {
|
||||||
plannedPrivate, err := json.Marshal(diff.Meta)
|
plannedPrivate, err := json.Marshal(diff.Meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
resp.PlannedPrivate = plannedPrivate
|
resp.PlannedPrivate = plannedPrivate
|
||||||
|
}
|
||||||
|
|
||||||
// collect the attributes that require instance replacement, and convert
|
// collect the attributes that require instance replacement, and convert
|
||||||
// them to cty.Paths.
|
// them to cty.Paths.
|
||||||
|
@ -594,7 +601,10 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
||||||
Meta: make(map[string]interface{}),
|
Meta: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if private != nil {
|
||||||
diff.Meta = private
|
diff.Meta = private
|
||||||
|
}
|
||||||
|
|
||||||
newInstanceState, err := s.provider.Apply(info, priorState, diff)
|
newInstanceState, err := s.provider.Apply(info, priorState, diff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -614,6 +624,8 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newStateVal = copyTimeoutValues(newStateVal, plannedStateVal)
|
||||||
|
|
||||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
|
@ -726,6 +738,8 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newStateVal = copyTimeoutValues(newStateVal, configVal)
|
||||||
|
|
||||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
|
@ -770,3 +784,28 @@ func pathToAttributePath(path cty.Path) *proto.AttributePath {
|
||||||
|
|
||||||
return &proto.AttributePath{Steps: steps}
|
return &proto.AttributePath{Steps: steps}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper/schema throws away timeout values from the config and stores them in
|
||||||
|
// the Private/Meta fields. we need to copy those values into the planned state
|
||||||
|
// so that core doesn't see a perpetual diff with the timeout block.
|
||||||
|
func copyTimeoutValues(to cty.Value, from cty.Value) cty.Value {
|
||||||
|
// if `from` is null, then there are no attributes, and if `to` is null we
|
||||||
|
// are planning to remove it altogether.
|
||||||
|
if from.IsNull() || to.IsNull() {
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
|
fromAttrs := from.AsValueMap()
|
||||||
|
timeouts, ok := fromAttrs[schema.TimeoutsConfigKey]
|
||||||
|
|
||||||
|
// no timeouts to copy
|
||||||
|
// timeouts shouldn't be unknown, but don't copy possibly invalid values
|
||||||
|
if !ok || timeouts.IsNull() || !timeouts.IsWhollyKnown() {
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
|
toAttrs := to.AsValueMap()
|
||||||
|
toAttrs[schema.TimeoutsConfigKey] = timeouts
|
||||||
|
|
||||||
|
return cty.ObjectVal(toAttrs)
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
@ -388,8 +389,8 @@ func TestApplyResourceChange(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A propsed state with only the ID unknown will produce a nil diff, and
|
// A proposed state with only the ID unknown will produce a nil diff, and
|
||||||
// should return the propsed state value.
|
// should return the proposed state value.
|
||||||
plannedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
plannedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
||||||
"id": cty.UnknownVal(cty.String),
|
"id": cty.UnknownVal(cty.String),
|
||||||
}))
|
}))
|
||||||
|
@ -595,3 +596,44 @@ func TestPrepareProviderConfig(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetSchemaTimeouts(t *testing.T) {
|
||||||
|
r := &schema.Resource{
|
||||||
|
SchemaVersion: 4,
|
||||||
|
Timeouts: &schema.ResourceTimeout{
|
||||||
|
Create: schema.DefaultTimeout(time.Second),
|
||||||
|
Read: schema.DefaultTimeout(2 * time.Second),
|
||||||
|
Update: schema.DefaultTimeout(3 * time.Second),
|
||||||
|
Default: schema.DefaultTimeout(10 * time.Second),
|
||||||
|
},
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"foo": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that the timeouts appear in the schema as defined
|
||||||
|
block := r.CoreConfigSchema()
|
||||||
|
timeoutsBlock := block.BlockTypes["timeouts"]
|
||||||
|
if timeoutsBlock == nil {
|
||||||
|
t.Fatal("missing timeouts in schema")
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeoutsBlock.Attributes["create"] == nil {
|
||||||
|
t.Fatal("missing create timeout in schema")
|
||||||
|
}
|
||||||
|
if timeoutsBlock.Attributes["read"] == nil {
|
||||||
|
t.Fatal("missing read timeout in schema")
|
||||||
|
}
|
||||||
|
if timeoutsBlock.Attributes["update"] == nil {
|
||||||
|
t.Fatal("missing update timeout in schema")
|
||||||
|
}
|
||||||
|
if d := timeoutsBlock.Attributes["delete"]; d != nil {
|
||||||
|
t.Fatalf("unexpected delete timeout in schema: %#v", d)
|
||||||
|
}
|
||||||
|
if timeoutsBlock.Attributes["default"] == nil {
|
||||||
|
t.Fatal("missing default timeout in schema")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ func testStep(opts terraform.ContextOpts, state *terraform.State, step TestStep,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to keep a copy of the state prior to destroying
|
// We need to keep a copy of the state prior to destroying
|
||||||
// such that destroy steps can verify their behaviour in the check
|
// such that destroy steps can verify their behavior in the check
|
||||||
// function
|
// function
|
||||||
stateBeforeApplication := state.DeepCopy()
|
stateBeforeApplication := state.DeepCopy()
|
||||||
|
|
||||||
|
|
|
@ -172,6 +172,57 @@ func (r *Resource) CoreConfigSchema() *configschema.Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, timeoutsAttr := block.Attributes[TimeoutsConfigKey]
|
||||||
|
_, timeoutsBlock := block.BlockTypes[TimeoutsConfigKey]
|
||||||
|
|
||||||
|
// Insert configured timeout values into the schema, as long as the schema
|
||||||
|
// didn't define anything else by that name.
|
||||||
|
if r.Timeouts != nil && !timeoutsAttr && !timeoutsBlock {
|
||||||
|
timeouts := configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Timeouts.Create != nil {
|
||||||
|
timeouts.Attributes[TimeoutCreate] = &configschema.Attribute{
|
||||||
|
Type: cty.String,
|
||||||
|
Optional: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Timeouts.Read != nil {
|
||||||
|
timeouts.Attributes[TimeoutRead] = &configschema.Attribute{
|
||||||
|
Type: cty.String,
|
||||||
|
Optional: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Timeouts.Update != nil {
|
||||||
|
timeouts.Attributes[TimeoutUpdate] = &configschema.Attribute{
|
||||||
|
Type: cty.String,
|
||||||
|
Optional: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Timeouts.Delete != nil {
|
||||||
|
timeouts.Attributes[TimeoutDelete] = &configschema.Attribute{
|
||||||
|
Type: cty.String,
|
||||||
|
Optional: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Timeouts.Default != nil {
|
||||||
|
timeouts.Attributes[TimeoutDefault] = &configschema.Attribute{
|
||||||
|
Type: cty.String,
|
||||||
|
Optional: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block.BlockTypes[TimeoutsConfigKey] = &configschema.NestedBlock{
|
||||||
|
Nesting: configschema.NestingSingle,
|
||||||
|
Block: timeouts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,7 @@ func newResourceConfigShimmedComputedKeys(obj cty.Value, schema *configschema.Bl
|
||||||
}
|
}
|
||||||
|
|
||||||
blockVal := obj.GetAttr(typeName)
|
blockVal := obj.GetAttr(typeName)
|
||||||
if !blockVal.IsKnown() || blockVal.IsNull() {
|
if blockVal.IsNull() || !blockVal.IsKnown() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue