copy timouts into plan and apply state
helper/schema will remove "timeouts" from the config, and stash them in the diff.Meta map. Terraform sees "timeouts" as a regular config block, so needs them to be present in the state in order to not show a diff. Have the GRPCProviderServer shim copy all timeout values into any state it returns to provide consistent diffs in core.
This commit is contained in:
parent
121c9c127f
commit
e38a5a769d
|
@ -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,6 +463,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
priorState.Meta = priorPrivate
|
priorState.Meta = priorPrivate
|
||||||
|
|
||||||
// turn the proposed state into a legacy configuration
|
// turn the proposed state into a legacy configuration
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue