mirror of
https://github.com/hashicorp/terraform.git
synced 2026-05-28 04:03:27 -04:00
send resource identities to provider calls
This commit is contained in:
parent
b2b42c0fb4
commit
fec6e4b552
35 changed files with 2214 additions and 466 deletions
|
|
@ -116,6 +116,7 @@ func (p *providerServer5) ApplyResourceChange(ctx context.Context, req *proto5.A
|
|||
defer p.Unlock()
|
||||
|
||||
p.applyResourceChangeCalled = true
|
||||
|
||||
return p.ProviderServer.ApplyResourceChange(ctx, req)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8234,7 +8234,9 @@ func TestResourceChange_deferredActions(t *testing.T) {
|
|||
}
|
||||
var changes []*plans.DeferredResourceInstanceChangeSrc
|
||||
for _, change := range tc.changes {
|
||||
changeSrc, err := change.Encode(blockSchema.ImpliedType())
|
||||
changeSrc, err := change.Encode(providers.Schema{
|
||||
Body: blockSchema,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encode change: %s", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@ func marshalResourceChange(rc *plans.ResourceInstanceChangeSrc, schemas *terrafo
|
|||
return r, fmt.Errorf("no schema found for %s (in provider %s)", r.Address, rc.ProviderAddr.Provider)
|
||||
}
|
||||
|
||||
changeV, err := rc.Decode(schema.Body.ImpliedType())
|
||||
changeV, err := rc.Decode(schema)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ func marshalPlanResources(changes *plans.ChangesSrc, ris []addrs.AbsResourceInst
|
|||
return nil, fmt.Errorf("no schema found for %s", r.Addr.String())
|
||||
}
|
||||
resource.SchemaVersion = uint64(schema.Version)
|
||||
changeV, err := r.Decode(schema.Body.ImpliedType())
|
||||
changeV, err := r.Decode(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ func TestOperation_planNoChanges(t *testing.T) {
|
|||
}),
|
||||
},
|
||||
}
|
||||
rcs, err := rc.Encode(ty)
|
||||
rcs, err := rc.Encode(schema)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -180,7 +180,7 @@ func TestOperation_planNoChanges(t *testing.T) {
|
|||
}),
|
||||
},
|
||||
}
|
||||
rcs, err := rc.Encode(ty)
|
||||
rcs, err := rc.Encode(schema)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -227,7 +227,7 @@ func TestOperation_planNoChanges(t *testing.T) {
|
|||
}),
|
||||
},
|
||||
}
|
||||
rcs, err := rc.Encode(ty)
|
||||
rcs, err := rc.Encode(schema)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -257,7 +257,6 @@ func TestOperation_planNoChanges(t *testing.T) {
|
|||
addr.Resource.Resource.Mode,
|
||||
addr.Resource.Resource.Type,
|
||||
)
|
||||
ty := schema.Body.ImpliedType()
|
||||
rc := &plans.ResourceInstanceChange{
|
||||
Addr: addr,
|
||||
PrevRunAddr: addrPrev,
|
||||
|
|
@ -276,7 +275,7 @@ func TestOperation_planNoChanges(t *testing.T) {
|
|||
}),
|
||||
},
|
||||
}
|
||||
rcs, err := rc.Encode(ty)
|
||||
rcs, err := rc.Encode(schema)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,7 +219,8 @@ func (p *provider) Configure(_ context.Context, req *tfplugin5.Configure_Request
|
|||
|
||||
func (p *provider) ReadResource(_ context.Context, req *tfplugin5.ReadResource_Request) (*tfplugin5.ReadResource_Response, error) {
|
||||
resp := &tfplugin5.ReadResource_Response{}
|
||||
ty := p.schema.ResourceTypes[req.TypeName].Body.ImpliedType()
|
||||
resSchema := p.schema.ResourceTypes[req.TypeName]
|
||||
ty := resSchema.Body.ImpliedType()
|
||||
|
||||
stateVal, err := decodeDynamicValue(req.CurrentState, ty)
|
||||
if err != nil {
|
||||
|
|
@ -234,11 +235,24 @@ func (p *provider) ReadResource(_ context.Context, req *tfplugin5.ReadResource_R
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
var currentIdentity cty.Value
|
||||
if req.CurrentIdentity != nil && req.CurrentIdentity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
currentIdentity, err = decodeDynamicValue(req.CurrentIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
readResp := p.provider.ReadResource(providers.ReadResourceRequest{
|
||||
TypeName: req.TypeName,
|
||||
PriorState: stateVal,
|
||||
Private: req.Private,
|
||||
ProviderMeta: metaVal,
|
||||
TypeName: req.TypeName,
|
||||
PriorState: stateVal,
|
||||
Private: req.Private,
|
||||
ProviderMeta: metaVal,
|
||||
CurrentIdentity: currentIdentity,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics)
|
||||
if readResp.Diagnostics.HasErrors() {
|
||||
|
|
@ -253,12 +267,27 @@ func (p *provider) ReadResource(_ context.Context, req *tfplugin5.ReadResource_R
|
|||
}
|
||||
resp.NewState = dv
|
||||
|
||||
if !readResp.Identity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
identity, err := encodeDynamicValue(readResp.Identity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.NewIdentity = &tfplugin5.ResourceIdentityData{
|
||||
IdentityData: identity,
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider) PlanResourceChange(_ context.Context, req *tfplugin5.PlanResourceChange_Request) (*tfplugin5.PlanResourceChange_Response, error) {
|
||||
resp := &tfplugin5.PlanResourceChange_Response{}
|
||||
ty := p.schema.ResourceTypes[req.TypeName].Body.ImpliedType()
|
||||
resSchema := p.schema.ResourceTypes[req.TypeName]
|
||||
ty := resSchema.Body.ImpliedType()
|
||||
|
||||
priorStateVal, err := decodeDynamicValue(req.PriorState, ty)
|
||||
if err != nil {
|
||||
|
|
@ -285,6 +314,20 @@ func (p *provider) PlanResourceChange(_ context.Context, req *tfplugin5.PlanReso
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
var priorIdentity cty.Value
|
||||
if req.PriorIdentity != nil && req.PriorIdentity.IdentityData != nil {
|
||||
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
priorIdentity, err = decodeDynamicValue(req.PriorIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
planResp := p.provider.PlanResourceChange(providers.PlanResourceChangeRequest{
|
||||
TypeName: req.TypeName,
|
||||
PriorState: priorStateVal,
|
||||
|
|
@ -292,6 +335,7 @@ func (p *provider) PlanResourceChange(_ context.Context, req *tfplugin5.PlanReso
|
|||
Config: configVal,
|
||||
PriorPrivate: req.PriorPrivate,
|
||||
ProviderMeta: metaVal,
|
||||
PriorIdentity: priorIdentity,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, planResp.Diagnostics)
|
||||
if planResp.Diagnostics.HasErrors() {
|
||||
|
|
@ -310,12 +354,30 @@ func (p *provider) PlanResourceChange(_ context.Context, req *tfplugin5.PlanReso
|
|||
resp.RequiresReplace = append(resp.RequiresReplace, convert.PathToAttributePath(path))
|
||||
}
|
||||
|
||||
if !planResp.PlannedIdentity.IsNull() {
|
||||
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
plannedIdentity, err := encodeDynamicValue(planResp.PlannedIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
resp.PlannedIdentity = &tfplugin5.ResourceIdentityData{
|
||||
IdentityData: plannedIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider) ApplyResourceChange(_ context.Context, req *tfplugin5.ApplyResourceChange_Request) (*tfplugin5.ApplyResourceChange_Response, error) {
|
||||
resp := &tfplugin5.ApplyResourceChange_Response{}
|
||||
ty := p.schema.ResourceTypes[req.TypeName].Body.ImpliedType()
|
||||
resSchema := p.schema.ResourceTypes[req.TypeName]
|
||||
ty := resSchema.Body.ImpliedType()
|
||||
|
||||
priorStateVal, err := decodeDynamicValue(req.PriorState, ty)
|
||||
if err != nil {
|
||||
|
|
@ -342,13 +404,27 @@ func (p *provider) ApplyResourceChange(_ context.Context, req *tfplugin5.ApplyRe
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
var plannedIdentity cty.Value
|
||||
if req.PlannedIdentity != nil && req.PlannedIdentity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
plannedIdentity, err = decodeDynamicValue(req.PlannedIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
applyResp := p.provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{
|
||||
TypeName: req.TypeName,
|
||||
PriorState: priorStateVal,
|
||||
PlannedState: plannedStateVal,
|
||||
Config: configVal,
|
||||
PlannedPrivate: req.PlannedPrivate,
|
||||
ProviderMeta: metaVal,
|
||||
TypeName: req.TypeName,
|
||||
PriorState: priorStateVal,
|
||||
PlannedState: plannedStateVal,
|
||||
Config: configVal,
|
||||
PlannedPrivate: req.PlannedPrivate,
|
||||
ProviderMeta: metaVal,
|
||||
PlannedIdentity: plannedIdentity,
|
||||
})
|
||||
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, applyResp.Diagnostics)
|
||||
|
|
@ -356,38 +432,85 @@ func (p *provider) ApplyResourceChange(_ context.Context, req *tfplugin5.ApplyRe
|
|||
return resp, nil
|
||||
}
|
||||
resp.Private = applyResp.Private
|
||||
|
||||
resp.NewState, err = encodeDynamicValue(applyResp.NewState, ty)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if !applyResp.NewIdentity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
newIdentity, err := encodeDynamicValue(applyResp.NewIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.NewIdentity = &tfplugin5.ResourceIdentityData{
|
||||
IdentityData: newIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider) ImportResourceState(_ context.Context, req *tfplugin5.ImportResourceState_Request) (*tfplugin5.ImportResourceState_Response, error) {
|
||||
resp := &tfplugin5.ImportResourceState_Response{}
|
||||
var identity cty.Value
|
||||
var err error
|
||||
if req.Identity != nil && req.Identity.IdentityData != nil {
|
||||
resSchema := p.schema.ResourceTypes[req.TypeName]
|
||||
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
identity, err = decodeDynamicValue(req.Identity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
importResp := p.provider.ImportResourceState(providers.ImportResourceStateRequest{
|
||||
TypeName: req.TypeName,
|
||||
ID: req.Id,
|
||||
Identity: identity,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, importResp.Diagnostics)
|
||||
|
||||
for _, res := range importResp.ImportedResources {
|
||||
ty := p.schema.ResourceTypes[res.TypeName].Body.ImpliedType()
|
||||
importSchema := p.schema.ResourceTypes[res.TypeName]
|
||||
ty := importSchema.Body.ImpliedType()
|
||||
state, err := encodeDynamicValue(res.State, ty)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
continue
|
||||
}
|
||||
|
||||
resp.ImportedResources = append(resp.ImportedResources, &tfplugin5.ImportResourceState_ImportedResource{
|
||||
resource := &tfplugin5.ImportResourceState_ImportedResource{
|
||||
TypeName: res.TypeName,
|
||||
State: state,
|
||||
Private: res.Private,
|
||||
})
|
||||
}
|
||||
|
||||
if !res.Identity.IsNull() {
|
||||
if importSchema.Identity == nil {
|
||||
return nil, fmt.Errorf("identity schema not found for type %s", res.TypeName)
|
||||
}
|
||||
identity, err := encodeDynamicValue(res.Identity, importSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
continue
|
||||
}
|
||||
resource.Identity = &tfplugin5.ResourceIdentityData{
|
||||
IdentityData: identity,
|
||||
}
|
||||
}
|
||||
|
||||
resp.ImportedResources = append(resp.ImportedResources, resource)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
|
@ -396,12 +519,19 @@ func (p *provider) ImportResourceState(_ context.Context, req *tfplugin5.ImportR
|
|||
func (p *provider) MoveResourceState(_ context.Context, request *tfplugin5.MoveResourceState_Request) (*tfplugin5.MoveResourceState_Response, error) {
|
||||
resp := &tfplugin5.MoveResourceState_Response{}
|
||||
|
||||
var sourceIdentity []byte
|
||||
var err error
|
||||
if request.SourceIdentity != nil && len(request.SourceIdentity.Json) > 0 {
|
||||
sourceIdentity = request.SourceIdentity.Json
|
||||
}
|
||||
|
||||
moveResp := p.provider.MoveResourceState(providers.MoveResourceStateRequest{
|
||||
SourceProviderAddress: request.SourceProviderAddress,
|
||||
SourceTypeName: request.SourceTypeName,
|
||||
SourceSchemaVersion: request.SourceSchemaVersion,
|
||||
SourceStateJSON: request.SourceState.Json,
|
||||
SourcePrivate: request.SourcePrivate,
|
||||
SourceIdentity: sourceIdentity,
|
||||
TargetTypeName: request.TargetTypeName,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, moveResp.Diagnostics)
|
||||
|
|
@ -409,7 +539,8 @@ func (p *provider) MoveResourceState(_ context.Context, request *tfplugin5.MoveR
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
targetType := p.schema.ResourceTypes[request.TargetTypeName].Body.ImpliedType()
|
||||
targetSchema := p.schema.ResourceTypes[request.TargetTypeName]
|
||||
targetType := targetSchema.Body.ImpliedType()
|
||||
targetState, err := encodeDynamicValue(moveResp.TargetState, targetType)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
|
|
@ -417,6 +548,21 @@ func (p *provider) MoveResourceState(_ context.Context, request *tfplugin5.MoveR
|
|||
}
|
||||
resp.TargetState = targetState
|
||||
resp.TargetPrivate = moveResp.TargetPrivate
|
||||
|
||||
if !moveResp.TargetIdentity.IsNull() {
|
||||
if targetSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", request.TargetTypeName)
|
||||
}
|
||||
targetIdentity, err := encodeDynamicValue(moveResp.TargetIdentity, targetSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.TargetIdentity = &tfplugin5.ResourceIdentityData{
|
||||
IdentityData: targetIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
|
@ -558,11 +704,11 @@ func (p *provider) GetResourceIdentitySchemas(_ context.Context, req *tfplugin5.
|
|||
|
||||
func (p *provider) UpgradeResourceIdentity(_ context.Context, req *tfplugin5.UpgradeResourceIdentity_Request) (*tfplugin5.UpgradeResourceIdentity_Response, error) {
|
||||
resp := &tfplugin5.UpgradeResourceIdentity_Response{}
|
||||
resource, ok := p.identitySchemas.IdentityTypes[req.TypeName]
|
||||
resource, ok := p.schema.ResourceTypes[req.TypeName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("resource identity schema not found for type %q", req.TypeName)
|
||||
}
|
||||
ty := resource.Body.ImpliedType()
|
||||
ty := resource.Identity.ImpliedType()
|
||||
upgradeResp := p.provider.UpgradeResourceIdentity(providers.UpgradeResourceIdentityRequest{
|
||||
TypeName: req.TypeName,
|
||||
Version: req.Version,
|
||||
|
|
|
|||
|
|
@ -223,7 +223,8 @@ func (p *provider6) ConfigureProvider(_ context.Context, req *tfplugin6.Configur
|
|||
|
||||
func (p *provider6) ReadResource(_ context.Context, req *tfplugin6.ReadResource_Request) (*tfplugin6.ReadResource_Response, error) {
|
||||
resp := &tfplugin6.ReadResource_Response{}
|
||||
ty := p.schema.ResourceTypes[req.TypeName].Body.ImpliedType()
|
||||
resSchema := p.schema.ResourceTypes[req.TypeName]
|
||||
ty := resSchema.Body.ImpliedType()
|
||||
|
||||
stateVal, err := decodeDynamicValue6(req.CurrentState, ty)
|
||||
if err != nil {
|
||||
|
|
@ -238,11 +239,25 @@ func (p *provider6) ReadResource(_ context.Context, req *tfplugin6.ReadResource_
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
var currentIdentity cty.Value
|
||||
if req.CurrentIdentity != nil && req.CurrentIdentity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
currentIdentity, err = decodeDynamicValue6(req.CurrentIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
readResp := p.provider.ReadResource(providers.ReadResourceRequest{
|
||||
TypeName: req.TypeName,
|
||||
PriorState: stateVal,
|
||||
Private: req.Private,
|
||||
ProviderMeta: metaVal,
|
||||
TypeName: req.TypeName,
|
||||
PriorState: stateVal,
|
||||
Private: req.Private,
|
||||
ProviderMeta: metaVal,
|
||||
CurrentIdentity: currentIdentity,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics)
|
||||
if readResp.Diagnostics.HasErrors() {
|
||||
|
|
@ -257,12 +272,27 @@ func (p *provider6) ReadResource(_ context.Context, req *tfplugin6.ReadResource_
|
|||
}
|
||||
resp.NewState = dv
|
||||
|
||||
if !readResp.Identity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
newIdentity, err := encodeDynamicValue6(readResp.Identity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.NewIdentity = &tfplugin6.ResourceIdentityData{
|
||||
IdentityData: newIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider6) PlanResourceChange(_ context.Context, req *tfplugin6.PlanResourceChange_Request) (*tfplugin6.PlanResourceChange_Response, error) {
|
||||
resp := &tfplugin6.PlanResourceChange_Response{}
|
||||
ty := p.schema.ResourceTypes[req.TypeName].Body.ImpliedType()
|
||||
resSchema := p.schema.ResourceTypes[req.TypeName]
|
||||
ty := resSchema.Body.ImpliedType()
|
||||
|
||||
priorStateVal, err := decodeDynamicValue6(req.PriorState, ty)
|
||||
if err != nil {
|
||||
|
|
@ -289,6 +319,19 @@ func (p *provider6) PlanResourceChange(_ context.Context, req *tfplugin6.PlanRes
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
var priorIdentity cty.Value
|
||||
if req.PriorIdentity != nil && req.PriorIdentity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
priorIdentity, err = decodeDynamicValue6(req.PriorIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
planResp := p.provider.PlanResourceChange(providers.PlanResourceChangeRequest{
|
||||
TypeName: req.TypeName,
|
||||
PriorState: priorStateVal,
|
||||
|
|
@ -296,6 +339,7 @@ func (p *provider6) PlanResourceChange(_ context.Context, req *tfplugin6.PlanRes
|
|||
Config: configVal,
|
||||
PriorPrivate: req.PriorPrivate,
|
||||
ProviderMeta: metaVal,
|
||||
PriorIdentity: priorIdentity,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, planResp.Diagnostics)
|
||||
if planResp.Diagnostics.HasErrors() {
|
||||
|
|
@ -314,12 +358,29 @@ func (p *provider6) PlanResourceChange(_ context.Context, req *tfplugin6.PlanRes
|
|||
resp.RequiresReplace = append(resp.RequiresReplace, convert.PathToAttributePath(path))
|
||||
}
|
||||
|
||||
if !planResp.PlannedIdentity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
plannedIdentityVal, err := encodeDynamicValue6(planResp.PlannedIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
resp.PlannedIdentity = &tfplugin6.ResourceIdentityData{
|
||||
IdentityData: plannedIdentityVal,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider6) ApplyResourceChange(_ context.Context, req *tfplugin6.ApplyResourceChange_Request) (*tfplugin6.ApplyResourceChange_Response, error) {
|
||||
resp := &tfplugin6.ApplyResourceChange_Response{}
|
||||
ty := p.schema.ResourceTypes[req.TypeName].Body.ImpliedType()
|
||||
resSchema := p.schema.ResourceTypes[req.TypeName]
|
||||
ty := resSchema.Body.ImpliedType()
|
||||
|
||||
priorStateVal, err := decodeDynamicValue6(req.PriorState, ty)
|
||||
if err != nil {
|
||||
|
|
@ -346,13 +407,27 @@ func (p *provider6) ApplyResourceChange(_ context.Context, req *tfplugin6.ApplyR
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
var plannedIdentity cty.Value
|
||||
if req.PlannedIdentity != nil && req.PlannedIdentity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
|
||||
plannedIdentity, err = decodeDynamicValue6(req.PlannedIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
applyResp := p.provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{
|
||||
TypeName: req.TypeName,
|
||||
PriorState: priorStateVal,
|
||||
PlannedState: plannedStateVal,
|
||||
Config: configVal,
|
||||
PlannedPrivate: req.PlannedPrivate,
|
||||
ProviderMeta: metaVal,
|
||||
TypeName: req.TypeName,
|
||||
PriorState: priorStateVal,
|
||||
PlannedState: plannedStateVal,
|
||||
Config: configVal,
|
||||
PlannedPrivate: req.PlannedPrivate,
|
||||
ProviderMeta: metaVal,
|
||||
PlannedIdentity: plannedIdentity,
|
||||
})
|
||||
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, applyResp.Diagnostics)
|
||||
|
|
@ -367,31 +442,78 @@ func (p *provider6) ApplyResourceChange(_ context.Context, req *tfplugin6.ApplyR
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
if !applyResp.NewIdentity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
newIdentity, err := encodeDynamicValue6(applyResp.NewIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.NewIdentity = &tfplugin6.ResourceIdentityData{
|
||||
IdentityData: newIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider6) ImportResourceState(_ context.Context, req *tfplugin6.ImportResourceState_Request) (*tfplugin6.ImportResourceState_Response, error) {
|
||||
resp := &tfplugin6.ImportResourceState_Response{}
|
||||
|
||||
resSchema := p.schema.ResourceTypes[req.TypeName]
|
||||
|
||||
var identity cty.Value
|
||||
var err error
|
||||
if req.Identity != nil && req.Identity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", req.TypeName)
|
||||
}
|
||||
identity, err = decodeDynamicValue6(req.Identity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
importResp := p.provider.ImportResourceState(providers.ImportResourceStateRequest{
|
||||
TypeName: req.TypeName,
|
||||
ID: req.Id,
|
||||
Identity: identity,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, importResp.Diagnostics)
|
||||
|
||||
for _, res := range importResp.ImportedResources {
|
||||
ty := p.schema.ResourceTypes[res.TypeName].Body.ImpliedType()
|
||||
importSchema := p.schema.ResourceTypes[res.TypeName]
|
||||
ty := importSchema.Body.ImpliedType()
|
||||
state, err := encodeDynamicValue6(res.State, ty)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
continue
|
||||
}
|
||||
|
||||
resp.ImportedResources = append(resp.ImportedResources, &tfplugin6.ImportResourceState_ImportedResource{
|
||||
importedResource := &tfplugin6.ImportResourceState_ImportedResource{
|
||||
TypeName: res.TypeName,
|
||||
State: state,
|
||||
Private: res.Private,
|
||||
})
|
||||
}
|
||||
if !res.Identity.IsNull() {
|
||||
if importSchema.Identity == nil {
|
||||
return nil, fmt.Errorf("identity schema not found for type %s", res.TypeName)
|
||||
}
|
||||
|
||||
identity, err := encodeDynamicValue6(res.Identity, importSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
continue
|
||||
}
|
||||
|
||||
importedResource.Identity = &tfplugin6.ResourceIdentityData{
|
||||
IdentityData: identity,
|
||||
}
|
||||
}
|
||||
|
||||
resp.ImportedResources = append(resp.ImportedResources, importedResource)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
|
@ -400,6 +522,12 @@ func (p *provider6) ImportResourceState(_ context.Context, req *tfplugin6.Import
|
|||
func (p *provider6) MoveResourceState(_ context.Context, request *tfplugin6.MoveResourceState_Request) (*tfplugin6.MoveResourceState_Response, error) {
|
||||
resp := &tfplugin6.MoveResourceState_Response{}
|
||||
|
||||
var sourceIdentity []byte
|
||||
var err error
|
||||
if request.SourceIdentity != nil && len(request.SourceIdentity.Json) > 0 {
|
||||
sourceIdentity = request.SourceIdentity.Json
|
||||
}
|
||||
|
||||
moveResp := p.provider.MoveResourceState(providers.MoveResourceStateRequest{
|
||||
SourceProviderAddress: request.SourceProviderAddress,
|
||||
SourceTypeName: request.SourceTypeName,
|
||||
|
|
@ -407,18 +535,37 @@ func (p *provider6) MoveResourceState(_ context.Context, request *tfplugin6.Move
|
|||
SourceStateJSON: request.SourceState.Json,
|
||||
SourcePrivate: request.SourcePrivate,
|
||||
TargetTypeName: request.TargetTypeName,
|
||||
SourceIdentity: sourceIdentity,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, moveResp.Diagnostics)
|
||||
if moveResp.Diagnostics.HasErrors() {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
targetType := p.schema.ResourceTypes[request.TargetTypeName].Body.ImpliedType()
|
||||
targetSchema := p.schema.ResourceTypes[request.TargetTypeName]
|
||||
targetType := targetSchema.Body.ImpliedType()
|
||||
targetState, err := encodeDynamicValue6(moveResp.TargetState, targetType)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if !moveResp.TargetIdentity.IsNull() {
|
||||
if targetSchema.Identity == nil {
|
||||
return resp, fmt.Errorf("identity schema not found for type %s", request.TargetTypeName)
|
||||
}
|
||||
|
||||
targetIdentity, err := encodeDynamicValue6(moveResp.TargetIdentity, targetSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
resp.TargetIdentity = &tfplugin6.ResourceIdentityData{
|
||||
IdentityData: targetIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
resp.TargetState = targetState
|
||||
resp.TargetPrivate = moveResp.TargetPrivate
|
||||
return resp, nil
|
||||
|
|
|
|||
|
|
@ -63,8 +63,7 @@ func (c *Changes) Encode(schemas *schemarepo.Schemas) (*ChangesSrc, error) {
|
|||
if schema.Body == nil {
|
||||
return changesSrc, fmt.Errorf("Changes.Encode: missing schema for %s", rc.Addr)
|
||||
}
|
||||
|
||||
rcs, err := rc.Encode(schema.Body.ImpliedType())
|
||||
rcs, err := rc.Encode(schema)
|
||||
if err != nil {
|
||||
return changesSrc, fmt.Errorf("Changes.Encode: %w", err)
|
||||
}
|
||||
|
|
@ -280,8 +279,8 @@ type ResourceInstanceChange struct {
|
|||
// Encode produces a variant of the reciever that has its change values
|
||||
// serialized so it can be written to a plan file. Pass the implied type of the
|
||||
// corresponding resource type schema for correct operation.
|
||||
func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) {
|
||||
cs, err := rc.Change.Encode(ty)
|
||||
func (rc *ResourceInstanceChange) Encode(schema providers.Schema) (*ResourceInstanceChangeSrc, error) {
|
||||
cs, err := rc.Change.Encode(&schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -292,6 +291,7 @@ func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSr
|
|||
prevRunAddr = rc.Addr
|
||||
}
|
||||
return &ResourceInstanceChangeSrc{
|
||||
|
||||
Addr: rc.Addr,
|
||||
PrevRunAddr: prevRunAddr,
|
||||
DeposedKey: rc.DeposedKey,
|
||||
|
|
@ -347,7 +347,9 @@ func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceCha
|
|||
Change: Change{
|
||||
Action: Delete,
|
||||
Before: rc.Before,
|
||||
BeforeIdentity: rc.BeforeIdentity,
|
||||
After: cty.NullVal(rc.Before.Type()),
|
||||
AfterIdentity: cty.NullVal(rc.BeforeIdentity.Type()),
|
||||
Importing: rc.Importing,
|
||||
GeneratedConfig: rc.GeneratedConfig,
|
||||
},
|
||||
|
|
@ -361,7 +363,9 @@ func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceCha
|
|||
Change: Change{
|
||||
Action: NoOp,
|
||||
Before: rc.Before,
|
||||
BeforeIdentity: rc.BeforeIdentity,
|
||||
After: rc.Before,
|
||||
AfterIdentity: rc.BeforeIdentity,
|
||||
Importing: rc.Importing,
|
||||
GeneratedConfig: rc.GeneratedConfig,
|
||||
},
|
||||
|
|
@ -378,7 +382,9 @@ func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceCha
|
|||
Change: Change{
|
||||
Action: NoOp,
|
||||
Before: rc.Before,
|
||||
BeforeIdentity: rc.BeforeIdentity,
|
||||
After: rc.Before,
|
||||
AfterIdentity: rc.BeforeIdentity,
|
||||
Importing: rc.Importing,
|
||||
GeneratedConfig: rc.GeneratedConfig,
|
||||
},
|
||||
|
|
@ -392,7 +398,9 @@ func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceCha
|
|||
Change: Change{
|
||||
Action: Create,
|
||||
Before: cty.NullVal(rc.After.Type()),
|
||||
BeforeIdentity: cty.NullVal(rc.AfterIdentity.Type()),
|
||||
After: rc.After,
|
||||
AfterIdentity: rc.AfterIdentity,
|
||||
Importing: rc.Importing,
|
||||
GeneratedConfig: rc.GeneratedConfig,
|
||||
},
|
||||
|
|
@ -526,7 +534,7 @@ type OutputChange struct {
|
|||
// Encode produces a variant of the reciever that has its change values
|
||||
// serialized so it can be written to a plan file.
|
||||
func (oc *OutputChange) Encode() (*OutputChangeSrc, error) {
|
||||
cs, err := oc.Change.Encode(cty.DynamicPseudoType)
|
||||
cs, err := oc.Change.Encode(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -584,6 +592,9 @@ type Change struct {
|
|||
// collections/structures.
|
||||
Before, After cty.Value
|
||||
|
||||
// Keeping track of how the identity of the resource has changed.
|
||||
BeforeIdentity, AfterIdentity cty.Value
|
||||
|
||||
// Importing is present if the resource is being imported as part of this
|
||||
// change.
|
||||
//
|
||||
|
|
@ -606,7 +617,7 @@ type Change struct {
|
|||
// Where a Change is embedded in some other struct, it's generally better
|
||||
// to call the corresponding Encode method of that struct rather than working
|
||||
// directly with its embedded Change.
|
||||
func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) {
|
||||
func (c *Change) Encode(schema *providers.Schema) (*ChangeSrc, error) {
|
||||
// We can't serialize value marks directly so we'll need to extract the
|
||||
// sensitive marks and store them in a separate field.
|
||||
//
|
||||
|
|
@ -632,6 +643,11 @@ func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) {
|
|||
)
|
||||
}
|
||||
|
||||
ty := cty.DynamicPseudoType
|
||||
if schema != nil {
|
||||
ty = schema.Body.ImpliedType()
|
||||
}
|
||||
|
||||
beforeDV, err := NewDynamicValue(unmarkedBefore, ty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -641,12 +657,35 @@ func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var beforeIdentityDV DynamicValue
|
||||
var afterIdentityDV DynamicValue
|
||||
|
||||
identityTy := cty.DynamicPseudoType
|
||||
if schema != nil {
|
||||
identityTy = schema.Identity.ImpliedType()
|
||||
}
|
||||
|
||||
if !c.BeforeIdentity.IsNull() {
|
||||
beforeIdentityDV, err = NewDynamicValue(c.BeforeIdentity, identityTy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !c.AfterIdentity.IsNull() {
|
||||
afterIdentityDV, err = NewDynamicValue(c.AfterIdentity, identityTy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &ChangeSrc{
|
||||
Action: c.Action,
|
||||
Before: beforeDV,
|
||||
After: afterDV,
|
||||
BeforeSensitivePaths: sensitiveAttrsBefore,
|
||||
AfterSensitivePaths: sensitiveAttrsAfter,
|
||||
BeforeIdentity: beforeIdentityDV,
|
||||
AfterIdentity: afterIdentityDV,
|
||||
Importing: c.Importing.Encode(),
|
||||
GeneratedConfig: c.GeneratedConfig,
|
||||
}, nil
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ func (c *ChangesSrc) Decode(schemas *schemarepo.Schemas) (*Changes, error) {
|
|||
return nil, fmt.Errorf("ChangesSrc.Decode: missing schema for %s", rcs.Addr)
|
||||
}
|
||||
|
||||
rc, err := rcs.Decode(schema.Body.ImpliedType())
|
||||
rc, err := rcs.Decode(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -232,8 +232,8 @@ func (rcs *ResourceInstanceChangeSrc) ObjectAddr() addrs.AbsResourceInstanceObje
|
|||
// Decode unmarshals the raw representation of the instance object being
|
||||
// changed. Pass the implied type of the corresponding resource type schema
|
||||
// for correct operation.
|
||||
func (rcs *ResourceInstanceChangeSrc) Decode(ty cty.Type) (*ResourceInstanceChange, error) {
|
||||
change, err := rcs.ChangeSrc.Decode(ty)
|
||||
func (rcs *ResourceInstanceChangeSrc) Decode(schema providers.Schema) (*ResourceInstanceChange, error) {
|
||||
change, err := rcs.ChangeSrc.Decode(&schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -309,7 +309,7 @@ type OutputChangeSrc struct {
|
|||
// Decode unmarshals the raw representation of the output value being
|
||||
// changed.
|
||||
func (ocs *OutputChangeSrc) Decode() (*OutputChange, error) {
|
||||
change, err := ocs.ChangeSrc.Decode(cty.DynamicPseudoType)
|
||||
change, err := ocs.ChangeSrc.Decode(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -380,6 +380,11 @@ type ChangeSrc struct {
|
|||
// storage.
|
||||
Before, After DynamicValue
|
||||
|
||||
// BeforeIdentity and AfterIdentity correspond to the fields of the same name in Change,
|
||||
// but have not yet been decoded from the serialized value used for
|
||||
// storage.
|
||||
BeforeIdentity, AfterIdentity DynamicValue
|
||||
|
||||
// BeforeSensitivePaths and AfterSensitivePaths are the paths for any
|
||||
// values in Before or After (respectively) that are considered to be
|
||||
// sensitive. The sensitive marks are removed from the in-memory values
|
||||
|
|
@ -409,10 +414,20 @@ type ChangeSrc struct {
|
|||
// Where a ChangeSrc is embedded in some other struct, it's generally better
|
||||
// to call the corresponding Decode method of that struct rather than working
|
||||
// directly with its embedded Change.
|
||||
func (cs *ChangeSrc) Decode(ty cty.Type) (*Change, error) {
|
||||
func (cs *ChangeSrc) Decode(schema *providers.Schema) (*Change, error) {
|
||||
var err error
|
||||
|
||||
ty := cty.DynamicPseudoType
|
||||
identityType := cty.DynamicPseudoType
|
||||
if schema != nil {
|
||||
ty = schema.Body.ImpliedType()
|
||||
identityType = schema.Identity.ImpliedType()
|
||||
}
|
||||
|
||||
before := cty.NullVal(ty)
|
||||
beforeIdentity := cty.NullVal(identityType)
|
||||
after := cty.NullVal(ty)
|
||||
afterIdentity := cty.NullVal(identityType)
|
||||
|
||||
if len(cs.Before) > 0 {
|
||||
before, err = cs.Before.Decode(ty)
|
||||
|
|
@ -420,17 +435,31 @@ func (cs *ChangeSrc) Decode(ty cty.Type) (*Change, error) {
|
|||
return nil, fmt.Errorf("error decoding 'before' value: %s", err)
|
||||
}
|
||||
}
|
||||
if len(cs.BeforeIdentity) > 0 {
|
||||
beforeIdentity, err = cs.BeforeIdentity.Decode(identityType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding 'beforeIdentity' value: %s", err)
|
||||
}
|
||||
}
|
||||
if len(cs.After) > 0 {
|
||||
after, err = cs.After.Decode(ty)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding 'after' value: %s", err)
|
||||
}
|
||||
}
|
||||
if len(cs.AfterIdentity) > 0 {
|
||||
afterIdentity, err = cs.AfterIdentity.Decode(identityType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding 'afterIdentity' value: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &Change{
|
||||
Action: cs.Action,
|
||||
Before: marks.MarkPaths(before, marks.Sensitive, cs.BeforeSensitivePaths),
|
||||
BeforeIdentity: beforeIdentity,
|
||||
After: marks.MarkPaths(after, marks.Sensitive, cs.AfterSensitivePaths),
|
||||
AfterIdentity: afterIdentity,
|
||||
Importing: cs.Importing.Decode(),
|
||||
GeneratedConfig: cs.GeneratedConfig,
|
||||
}, nil
|
||||
|
|
|
|||
|
|
@ -7,37 +7,82 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestChangeEncodeSensitive(t *testing.T) {
|
||||
testVals := []cty.Value{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"ding": cty.StringVal("dong").Mark(marks.Sensitive),
|
||||
}),
|
||||
cty.StringVal("bleep").Mark(marks.Sensitive),
|
||||
cty.ListVal([]cty.Value{cty.UnknownVal(cty.String).Mark(marks.Sensitive)}),
|
||||
testVals := []struct {
|
||||
val cty.Value
|
||||
schema providers.Schema
|
||||
}{
|
||||
{
|
||||
val: cty.ObjectVal(map[string]cty.Value{
|
||||
"ding": cty.StringVal("dong").Mark(marks.Sensitive),
|
||||
}),
|
||||
schema: providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"ding": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
val: cty.ObjectVal(map[string]cty.Value{
|
||||
"ding": cty.StringVal("bleep").Mark(marks.Sensitive),
|
||||
}),
|
||||
schema: providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"ding": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
val: cty.ObjectVal(map[string]cty.Value{
|
||||
"ding": cty.ListVal([]cty.Value{cty.UnknownVal(cty.String).Mark(marks.Sensitive)}),
|
||||
}),
|
||||
schema: providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"ding": {
|
||||
Type: cty.List(cty.String),
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range testVals {
|
||||
t.Run(fmt.Sprintf("%#v", v), func(t *testing.T) {
|
||||
change := Change{
|
||||
Before: cty.NullVal(v.Type()),
|
||||
After: v,
|
||||
Before: cty.NullVal(v.val.Type()),
|
||||
After: v.val,
|
||||
}
|
||||
|
||||
encoded, err := change.Encode(v.Type())
|
||||
encoded, err := change.Encode(&v.schema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
decoded, err := encoded.Decode(v.Type())
|
||||
decoded, err := encoded.Decode(&v.schema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !v.RawEquals(decoded.After) {
|
||||
if !v.val.RawEquals(decoded.After) {
|
||||
t.Fatalf("%#v != %#v\n", decoded.After, v)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ package plans
|
|||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// DeferredResourceInstanceChangeSrc tracks information about a resource that
|
||||
|
|
@ -19,8 +18,8 @@ type DeferredResourceInstanceChangeSrc struct {
|
|||
ChangeSrc *ResourceInstanceChangeSrc
|
||||
}
|
||||
|
||||
func (rcs *DeferredResourceInstanceChangeSrc) Decode(ty cty.Type) (*DeferredResourceInstanceChange, error) {
|
||||
change, err := rcs.ChangeSrc.Decode(ty)
|
||||
func (rcs *DeferredResourceInstanceChangeSrc) Decode(schema providers.Schema) (*DeferredResourceInstanceChange, error) {
|
||||
change, err := rcs.ChangeSrc.Decode(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -42,8 +41,8 @@ type DeferredResourceInstanceChange struct {
|
|||
Change *ResourceInstanceChange
|
||||
}
|
||||
|
||||
func (rcs *DeferredResourceInstanceChange) Encode(ty cty.Type) (*DeferredResourceInstanceChangeSrc, error) {
|
||||
change, err := rcs.Change.Encode(ty)
|
||||
func (rcs *DeferredResourceInstanceChange) Encode(schema providers.Schema) (*DeferredResourceInstanceChangeSrc, error) {
|
||||
change, err := rcs.Change.Encode(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/checks"
|
||||
"github.com/hashicorp/terraform/internal/collections"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/globalref"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
|
|
@ -381,6 +382,17 @@ func TestTFPlanRoundTripDestroy(t *testing.T) {
|
|||
"id": cty.String,
|
||||
})
|
||||
|
||||
objSchema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
plan := &plans.Plan{
|
||||
Changes: &plans.ChangesSrc{
|
||||
Outputs: []*plans.OutputChangeSrc{
|
||||
|
|
@ -453,7 +465,7 @@ func TestTFPlanRoundTripDestroy(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, rics := range newPlan.Changes.Resources {
|
||||
ric, err := rics.Decode(objTy)
|
||||
ric, err := rics.Decode(objSchema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -510,6 +510,21 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
|
|||
protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
|
||||
}
|
||||
|
||||
if !r.CurrentIdentity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("identity type not found for resource type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
currentIdentityMP, err := msgpack.Marshal(r.CurrentIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
protoReq.CurrentIdentity = &proto.ResourceIdentityData{
|
||||
IdentityData: &proto.DynamicValue{Msgpack: currentIdentityMP},
|
||||
}
|
||||
}
|
||||
|
||||
protoResp, err := p.client.ReadResource(p.ctx, protoReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
|
|
@ -526,6 +541,18 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
|
|||
resp.NewState = state
|
||||
resp.Private = protoResp.Private
|
||||
|
||||
if protoResp.NewIdentity != nil && protoResp.NewIdentity.IdentityData != nil {
|
||||
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %q", r.TypeName))
|
||||
}
|
||||
|
||||
resp.Identity, err = decodeDynamicValue(protoResp.NewIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -596,6 +623,21 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
|
|||
protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
|
||||
}
|
||||
|
||||
if !r.PriorIdentity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("identity type not found for resource type %q", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
priorIdentityMP, err := msgpack.Marshal(r.PriorIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
protoReq.PriorIdentity = &proto.ResourceIdentityData{
|
||||
IdentityData: &proto.DynamicValue{Msgpack: priorIdentityMP},
|
||||
}
|
||||
}
|
||||
|
||||
protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
|
|
@ -620,6 +662,19 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
|
|||
|
||||
resp.Deferred = convert.ProtoToDeferred(protoResp.Deferred)
|
||||
|
||||
if protoResp.PlannedIdentity != nil && protoResp.PlannedIdentity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
resp.PlannedIdentity, err = decodeDynamicValue(protoResp.PlannedIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -678,6 +733,21 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
|
|||
protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
|
||||
}
|
||||
|
||||
if !r.PlannedIdentity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("identity type not found for resource type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
identityMP, err := msgpack.Marshal(r.PlannedIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
protoReq.PlannedIdentity = &proto.ResourceIdentityData{
|
||||
IdentityData: &proto.DynamicValue{Msgpack: identityMP},
|
||||
}
|
||||
}
|
||||
|
||||
protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
|
|
@ -696,6 +766,19 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
|
|||
|
||||
resp.LegacyTypeSystem = protoResp.LegacyTypeSystem
|
||||
|
||||
if protoResp.NewIdentity != nil && protoResp.NewIdentity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("identity type not found for resource type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
newIdentity, err := decodeDynamicValue(protoResp.NewIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.NewIdentity = newIdentity
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -714,6 +797,26 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques
|
|||
ClientCapabilities: clientCapabilitiesToProto(r.ClientCapabilities),
|
||||
}
|
||||
|
||||
if !r.Identity.IsNull() {
|
||||
resSchema := schema.ResourceTypes[r.TypeName]
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %q", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
mp, err := msgpack.Marshal(r.Identity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
|
||||
protoReq.Identity = &proto.ResourceIdentityData{
|
||||
IdentityData: &proto.DynamicValue{
|
||||
Msgpack: mp,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
protoResp, err := p.client.ImportResourceState(p.ctx, protoReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
|
|
@ -740,6 +843,21 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques
|
|||
return resp
|
||||
}
|
||||
resource.State = state
|
||||
|
||||
if imported.Identity != nil && imported.Identity.IdentityData != nil {
|
||||
importedIdentitySchema, ok := schema.ResourceTypes[imported.TypeName]
|
||||
if !ok {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", imported.TypeName))
|
||||
continue
|
||||
}
|
||||
importedIdentity, err := decodeDynamicValue(imported.Identity.IdentityData, importedIdentitySchema.Body.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resource.Identity = importedIdentity
|
||||
}
|
||||
|
||||
resp.ImportedResources = append(resp.ImportedResources, resource)
|
||||
}
|
||||
|
||||
|
|
@ -760,6 +878,18 @@ func (p *GRPCProvider) MoveResourceState(r providers.MoveResourceStateRequest) (
|
|||
TargetTypeName: r.TargetTypeName,
|
||||
}
|
||||
|
||||
schema := p.GetProviderSchema()
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
resp.Diagnostics = schema.Diagnostics
|
||||
return resp
|
||||
}
|
||||
|
||||
if len(r.SourceIdentity) > 0 {
|
||||
protoReq.SourceIdentity = &proto.RawState{
|
||||
Json: r.SourceIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
protoResp, err := p.client.MoveResourceState(p.ctx, protoReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
|
|
@ -770,12 +900,6 @@ func (p *GRPCProvider) MoveResourceState(r providers.MoveResourceStateRequest) (
|
|||
return resp
|
||||
}
|
||||
|
||||
schema := p.GetProviderSchema()
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
resp.Diagnostics = schema.Diagnostics
|
||||
return resp
|
||||
}
|
||||
|
||||
targetType, ok := schema.ResourceTypes[r.TargetTypeName]
|
||||
if !ok {
|
||||
// We should have validated this earlier in the process, but we'll
|
||||
|
|
@ -792,6 +916,20 @@ func (p *GRPCProvider) MoveResourceState(r providers.MoveResourceStateRequest) (
|
|||
|
||||
resp.TargetPrivate = protoResp.TargetPrivate
|
||||
|
||||
if protoResp.TargetIdentity != nil && protoResp.TargetIdentity.IdentityData != nil {
|
||||
targetResSchema := schema.ResourceTypes[r.TargetTypeName]
|
||||
|
||||
if targetResSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %s", r.TargetTypeName))
|
||||
return resp
|
||||
}
|
||||
resp.TargetIdentity, err = decodeDynamicValue(protoResp.TargetIdentity.IdentityData, targetResSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -485,11 +485,29 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
|
|||
return resp
|
||||
}
|
||||
|
||||
var currentIdentity *proto6.ResourceIdentityData
|
||||
if !r.CurrentIdentity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
mp, err := msgpack.Marshal(r.CurrentIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
currentIdentity.IdentityData = &proto6.DynamicValue{
|
||||
Msgpack: mp,
|
||||
}
|
||||
}
|
||||
|
||||
protoReq := &proto6.ReadResource_Request{
|
||||
TypeName: r.TypeName,
|
||||
CurrentState: &proto6.DynamicValue{Msgpack: mp},
|
||||
Private: r.Private,
|
||||
ClientCapabilities: clientCapabilitiesToProto(r.ClientCapabilities),
|
||||
CurrentIdentity: currentIdentity,
|
||||
}
|
||||
|
||||
if metaSchema.Body != nil {
|
||||
|
|
@ -517,6 +535,17 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
|
|||
resp.Private = protoResp.Private
|
||||
resp.Deferred = convert.ProtoToDeferred(protoResp.Deferred)
|
||||
|
||||
if protoResp.NewIdentity != nil && protoResp.NewIdentity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %q", r.TypeName))
|
||||
}
|
||||
|
||||
resp.Identity, err = decodeDynamicValue(protoResp.NewIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -587,6 +616,25 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
|
|||
protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
|
||||
}
|
||||
|
||||
if !r.PriorIdentity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("identity type not found for resource type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
mp, err := msgpack.Marshal(r.PriorIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
|
||||
protoReq.PriorIdentity = &proto6.ResourceIdentityData{
|
||||
IdentityData: &proto6.DynamicValue{
|
||||
Msgpack: mp,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
|
|
@ -611,6 +659,20 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
|
|||
|
||||
resp.Deferred = convert.ProtoToDeferred(protoResp.Deferred)
|
||||
|
||||
if protoResp.PlannedIdentity != nil && protoResp.PlannedIdentity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
plannedIdentity, err := decodeDynamicValue(protoResp.PlannedIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.PlannedIdentity = plannedIdentity
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -669,6 +731,21 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
|
|||
protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP}
|
||||
}
|
||||
|
||||
if !r.PlannedIdentity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("identity type not found for resource type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
plannedIdentityMP, err := msgpack.Marshal(r.PlannedIdentity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
protoReq.PlannedIdentity = &proto6.ResourceIdentityData{
|
||||
IdentityData: &proto6.DynamicValue{Msgpack: plannedIdentityMP},
|
||||
}
|
||||
}
|
||||
|
||||
protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
|
|
@ -687,6 +764,20 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
|
|||
|
||||
resp.LegacyTypeSystem = protoResp.LegacyTypeSystem
|
||||
|
||||
if protoResp.NewIdentity != nil && protoResp.NewIdentity.IdentityData != nil {
|
||||
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("identity type not found for resource type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
newIdentity, err := decodeDynamicValue(protoResp.NewIdentity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.NewIdentity = newIdentity
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -699,6 +790,12 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques
|
|||
return resp
|
||||
}
|
||||
|
||||
resSchema, ok := schema.ResourceTypes[r.TypeName]
|
||||
if !ok {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("resource type not found: %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
protoReq := &proto6.ImportResourceState_Request{
|
||||
TypeName: r.TypeName,
|
||||
Id: r.ID,
|
||||
|
|
@ -713,6 +810,22 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques
|
|||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
resp.Deferred = convert.ProtoToDeferred(protoResp.Deferred)
|
||||
|
||||
if !r.Identity.IsNull() {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
identityMP, err := msgpack.Marshal(r.Identity, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
protoReq.Identity = &proto6.ResourceIdentityData{
|
||||
IdentityData: &proto6.DynamicValue{Msgpack: identityMP},
|
||||
}
|
||||
}
|
||||
|
||||
for _, imported := range protoResp.ImportedResources {
|
||||
resource := providers.ImportedResource{
|
||||
TypeName: imported.TypeName,
|
||||
|
|
@ -731,6 +844,21 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques
|
|||
return resp
|
||||
}
|
||||
resource.State = state
|
||||
|
||||
if imported.Identity != nil && imported.Identity.IdentityData != nil {
|
||||
if resSchema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %s", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
identity, err := decodeDynamicValue(imported.Identity.IdentityData, resSchema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resource.Identity = identity
|
||||
}
|
||||
|
||||
resp.ImportedResources = append(resp.ImportedResources, resource)
|
||||
}
|
||||
|
||||
|
|
@ -740,6 +868,13 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques
|
|||
func (p *GRPCProvider) MoveResourceState(r providers.MoveResourceStateRequest) (resp providers.MoveResourceStateResponse) {
|
||||
logger.Trace("GRPCProvider: MoveResourceState")
|
||||
|
||||
var sourceIdentity *proto6.RawState
|
||||
if len(r.SourceIdentity) > 0 {
|
||||
sourceIdentity = &proto6.RawState{
|
||||
Json: r.SourceIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
protoReq := &proto6.MoveResourceState_Request{
|
||||
SourceProviderAddress: r.SourceProviderAddress,
|
||||
SourceTypeName: r.SourceTypeName,
|
||||
|
|
@ -748,6 +883,7 @@ func (p *GRPCProvider) MoveResourceState(r providers.MoveResourceStateRequest) (
|
|||
Json: r.SourceStateJSON,
|
||||
},
|
||||
SourcePrivate: r.SourcePrivate,
|
||||
SourceIdentity: sourceIdentity,
|
||||
TargetTypeName: r.TargetTypeName,
|
||||
}
|
||||
|
||||
|
|
@ -783,6 +919,19 @@ func (p *GRPCProvider) MoveResourceState(r providers.MoveResourceStateRequest) (
|
|||
|
||||
resp.TargetPrivate = protoResp.TargetPrivate
|
||||
|
||||
if protoResp.TargetIdentity != nil && protoResp.TargetIdentity.IdentityData != nil {
|
||||
targetIdentitySchema := targetType.Identity
|
||||
if targetIdentitySchema == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown identity type %s", protoReq.TargetTypeName))
|
||||
return resp
|
||||
}
|
||||
resp.TargetIdentity, err = decodeDynamicValue(protoResp.TargetIdentity.IdentityData, targetIdentitySchema.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ func (s simple) Stop() error {
|
|||
func (s simple) ReadResource(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
|
||||
// just return the same state we received
|
||||
resp.NewState = req.PriorState
|
||||
resp.Identity = req.CurrentIdentity
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -171,6 +172,7 @@ func (s simple) ApplyResourceChange(req providers.ApplyResourceChangeRequest) (r
|
|||
}
|
||||
|
||||
resp.NewState = req.PlannedState
|
||||
resp.NewIdentity = req.PlannedIdentity
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ func (s simple) Stop() error {
|
|||
func (s simple) ReadResource(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
|
||||
// just return the same state we received
|
||||
resp.NewState = req.PriorState
|
||||
resp.Identity = req.CurrentIdentity
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -149,6 +150,7 @@ func (s simple) ApplyResourceChange(req providers.ApplyResourceChangeRequest) (r
|
|||
m["id"] = cty.StringVal(time.Now().String())
|
||||
}
|
||||
resp.NewState = cty.ObjectVal(m)
|
||||
resp.NewIdentity = req.PlannedIdentity
|
||||
|
||||
return resp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ func (m *Mock) ReadResource(request ReadResourceRequest) ReadResourceResponse {
|
|||
// state. So we'll return what we have.
|
||||
return ReadResourceResponse{
|
||||
NewState: request.PriorState,
|
||||
Identity: request.CurrentIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -287,13 +288,15 @@ func (m *Mock) ApplyResourceChange(request ApplyResourceChangeRequest) ApplyReso
|
|||
value, diags := mocking.ApplyComputedValuesForResource(request.PlannedState, replacement, resource.Body)
|
||||
response.Diagnostics = response.Diagnostics.Append(diags)
|
||||
response.NewState = value
|
||||
response.NewIdentity = request.PlannedIdentity
|
||||
return response
|
||||
|
||||
default:
|
||||
// For update or destroy operations, we don't have to create any values
|
||||
// so we'll just return the planned state directly.
|
||||
return ApplyResourceChangeResponse{
|
||||
NewState: request.PlannedState,
|
||||
NewState: request.PlannedState,
|
||||
NewIdentity: request.PlannedIdentity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -343,6 +343,9 @@ type ReadResourceRequest struct {
|
|||
|
||||
// ClientCapabilities contains information about the client's capabilities.
|
||||
ClientCapabilities ClientCapabilities
|
||||
|
||||
// CurrentIdentity is the current identity data of the resource.
|
||||
CurrentIdentity cty.Value
|
||||
}
|
||||
|
||||
// DeferredReason is a string that describes why a resource was deferred.
|
||||
|
|
@ -433,6 +436,9 @@ type PlanResourceChangeRequest struct {
|
|||
|
||||
// ClientCapabilities contains information about the client's capabilities.
|
||||
ClientCapabilities ClientCapabilities
|
||||
|
||||
// PriorIdentity is the current identity data of the resource.
|
||||
PriorIdentity cty.Value
|
||||
}
|
||||
|
||||
type PlanResourceChangeResponse struct {
|
||||
|
|
@ -462,6 +468,9 @@ type PlanResourceChangeResponse struct {
|
|||
// Deferred if present signals that the provider was not able to fully
|
||||
// complete this operation and a susequent run is required.
|
||||
Deferred *Deferred
|
||||
|
||||
// PlannedIdentity is the planned identity data of the resource.
|
||||
PlannedIdentity cty.Value
|
||||
}
|
||||
|
||||
type ApplyResourceChangeRequest struct {
|
||||
|
|
@ -489,6 +498,9 @@ type ApplyResourceChangeRequest struct {
|
|||
// each provider, and it should not be used without coordination with
|
||||
// HashiCorp. It is considered experimental and subject to change.
|
||||
ProviderMeta cty.Value
|
||||
|
||||
// PlannedIdentity is the planned identity data of the resource.
|
||||
PlannedIdentity cty.Value
|
||||
}
|
||||
|
||||
type ApplyResourceChangeResponse struct {
|
||||
|
|
@ -510,6 +522,9 @@ type ApplyResourceChangeResponse struct {
|
|||
// otherwise fail due to this imprecise mapping. No other provider or SDK
|
||||
// implementation is permitted to set this.
|
||||
LegacyTypeSystem bool
|
||||
|
||||
// NewIdentity is the new identity data of the resource.
|
||||
NewIdentity cty.Value
|
||||
}
|
||||
|
||||
type ImportResourceStateRequest struct {
|
||||
|
|
@ -522,6 +537,9 @@ type ImportResourceStateRequest struct {
|
|||
|
||||
// ClientCapabilities contains information about the client's capabilities.
|
||||
ClientCapabilities ClientCapabilities
|
||||
|
||||
// Identity is the identity data of the resource.
|
||||
Identity cty.Value
|
||||
}
|
||||
|
||||
type ImportResourceStateResponse struct {
|
||||
|
|
@ -556,6 +574,9 @@ type ImportedResource struct {
|
|||
// Private is an opaque blob that will be stored in state along with the
|
||||
// resource. It is intended only for interpretation by the provider itself.
|
||||
Private []byte
|
||||
|
||||
// Identity is the identity data of the resource.
|
||||
Identity cty.Value
|
||||
}
|
||||
|
||||
type MoveResourceStateRequest struct {
|
||||
|
|
@ -583,6 +604,9 @@ type MoveResourceStateRequest struct {
|
|||
// TargetTypeName is the name of the resource type that the resource is
|
||||
// being moved to.
|
||||
TargetTypeName string
|
||||
|
||||
// SourceIdentity is the identity data of the resource that is being moved.
|
||||
SourceIdentity []byte
|
||||
}
|
||||
|
||||
type MoveResourceStateResponse struct {
|
||||
|
|
@ -596,6 +620,9 @@ type MoveResourceStateResponse struct {
|
|||
|
||||
// Diagnostics contains any warnings or errors from the method call.
|
||||
Diagnostics tfdiags.Diagnostics
|
||||
|
||||
// TargetIdentity is the identity data of the resource that is being moved.
|
||||
TargetIdentity cty.Value
|
||||
}
|
||||
|
||||
type ReadDataSourceRequest struct {
|
||||
|
|
|
|||
|
|
@ -352,13 +352,14 @@ func (p *MockProvider) UpgradeResourceIdentity(r providers.UpgradeResourceIdenti
|
|||
return *p.UpgradeResourceIdentityResponse
|
||||
}
|
||||
|
||||
identitySchema, ok := p.getResourceIdentitySchemas().IdentityTypes[r.TypeName]
|
||||
if !ok {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName))
|
||||
schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName]
|
||||
|
||||
if !ok || schema.Identity == nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no identity schema found for %q", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
identityType := identitySchema.Body.ImpliedType()
|
||||
identityType := schema.Identity.ImpliedType()
|
||||
|
||||
v, err := ctyjson.Unmarshal(r.RawIdentityJSON, identityType)
|
||||
|
||||
|
|
@ -435,6 +436,10 @@ func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
|
|||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
}
|
||||
resp.NewState = newState
|
||||
if resp.Identity.IsNull() {
|
||||
resp.Identity = r.CurrentIdentity
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -470,6 +475,7 @@ func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
|
|||
resp.NewState = r.PriorState
|
||||
}
|
||||
|
||||
resp.Identity = r.CurrentIdentity
|
||||
resp.Private = r.Private
|
||||
return resp
|
||||
}
|
||||
|
|
@ -587,6 +593,7 @@ func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
|
|||
// if the value is nil, we return that directly to correspond to a delete
|
||||
if r.PlannedState.IsNull() {
|
||||
resp.NewState = r.PlannedState
|
||||
resp.NewIdentity = r.PlannedIdentity
|
||||
return resp
|
||||
}
|
||||
|
||||
|
|
@ -616,6 +623,7 @@ func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
|
|||
|
||||
resp.NewState = val
|
||||
resp.Private = r.PlannedPrivate
|
||||
resp.NewIdentity = r.PlannedIdentity
|
||||
|
||||
return resp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,11 +99,11 @@ func (m *crossTypeMover) prepareCrossTypeMove(stmt *MoveStatement, source, targe
|
|||
})
|
||||
return nil, diags
|
||||
}
|
||||
schema := targetSchema.SchemaForResourceAddr(target.Resource)
|
||||
targetResourceSchema := targetSchema.SchemaForResourceAddr(target.Resource)
|
||||
return &crossTypeMove{
|
||||
targetProvider: targetProvider,
|
||||
targetProviderAddr: *targetProviderAddr,
|
||||
targetResourceSchema: schema,
|
||||
targetResourceSchema: targetResourceSchema,
|
||||
sourceProviderAddr: sourceProviderAddr,
|
||||
}, diags
|
||||
}
|
||||
|
|
@ -128,9 +128,14 @@ func (move *crossTypeMove) applyCrossTypeMove(stmt *MoveStatement, source, targe
|
|||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// First, build the request.
|
||||
|
||||
var sourceIdentity []byte
|
||||
src := state.ResourceInstance(source).Current
|
||||
if src != nil {
|
||||
sourceIdentity = src.IdentityJSON
|
||||
}
|
||||
|
||||
// Build the request.
|
||||
|
||||
request := providers.MoveResourceStateRequest{
|
||||
SourceProviderAddress: move.sourceProviderAddr.Provider.String(),
|
||||
SourceTypeName: source.Resource.Resource.Type,
|
||||
|
|
@ -138,9 +143,10 @@ func (move *crossTypeMove) applyCrossTypeMove(stmt *MoveStatement, source, targe
|
|||
SourceStateJSON: src.AttrsJSON,
|
||||
SourcePrivate: src.Private,
|
||||
TargetTypeName: target.Resource.Resource.Type,
|
||||
SourceIdentity: sourceIdentity,
|
||||
}
|
||||
|
||||
// Second, ask the provider to transform the value into the type expected by
|
||||
// Ask the provider to transform the value into the type expected by
|
||||
// the new resource type.
|
||||
|
||||
resp := move.targetProvider.MoveResourceState(request)
|
||||
|
|
@ -167,6 +173,32 @@ func (move *crossTypeMove) applyCrossTypeMove(stmt *MoveStatement, source, targe
|
|||
return diags
|
||||
}
|
||||
|
||||
if !resp.TargetIdentity.IsNull() {
|
||||
// Identities can not contain unknown values
|
||||
if !resp.TargetIdentity.IsWhollyKnown() {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid identity",
|
||||
fmt.Sprintf(
|
||||
"Provider %q planned an identity with unknown values for the move from %s to %s. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
move.targetProviderAddr, source, target,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
// Identities can not contain marks
|
||||
if _, marks := resp.TargetIdentity.UnmarkDeep(); len(marks) > 0 {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid identity",
|
||||
fmt.Sprintf(
|
||||
"Provider %q planned an identity with marks for the move from %s to %s. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
move.targetProviderAddr, source, target,
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if resp.TargetState == cty.NilVal {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
|
|
@ -194,9 +226,9 @@ func (move *crossTypeMove) applyCrossTypeMove(stmt *MoveStatement, source, targe
|
|||
Status: src.Status,
|
||||
Dependencies: src.Dependencies,
|
||||
CreateBeforeDestroy: src.CreateBeforeDestroy,
|
||||
Identity: resp.TargetIdentity,
|
||||
}
|
||||
|
||||
// TODO: We need to handle identity data in move scenarios.
|
||||
data, err := newValue.Encode(move.targetResourceSchema)
|
||||
if err != nil {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
|
|
|
|||
|
|
@ -271,7 +271,9 @@ func TestPlanning_DestroyMode(t *testing.T) {
|
|||
|
||||
if planSrc := aPlan.Changes.ResourceInstance(aResourceInstAddr.Item); planSrc != nil {
|
||||
rAddr := aResourceInstAddr
|
||||
plan, err := planSrc.Decode(resourceTypeSchema.ImpliedType())
|
||||
plan, err := planSrc.Decode(providers.Schema{
|
||||
Body: resourceTypeSchema,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("can't decode change for %s: %s", rAddr, err)
|
||||
}
|
||||
|
|
@ -295,7 +297,9 @@ func TestPlanning_DestroyMode(t *testing.T) {
|
|||
|
||||
if planSrc := bPlan.Changes.ResourceInstance(bResourceInstAddr.Item); planSrc != nil {
|
||||
rAddr := bResourceInstAddr
|
||||
plan, err := planSrc.Decode(resourceTypeSchema.ImpliedType())
|
||||
plan, err := planSrc.Decode(providers.Schema{
|
||||
Body: resourceTypeSchema,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("can't decode change for %s: %s", rAddr, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,7 +142,8 @@ func (o *ResourceInstanceObject) Encode(schema providers.Schema) (*ResourceInsta
|
|||
}
|
||||
|
||||
var idJSON []byte
|
||||
if !o.Identity.IsNull() && schema.Identity != nil {
|
||||
// If the Identity is known and not null we can marshal it.
|
||||
if !o.Identity.IsNull() && o.Identity.IsWhollyKnown() && schema.Identity != nil {
|
||||
idJSON, err = ctyjson.Marshal(o.Identity, schema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ func (os *ResourceInstanceObjectSrc) Decode(schema providers.Schema) (*ResourceI
|
|||
} else if os.IdentityJSON != nil {
|
||||
identity, err = ctyjson.Unmarshal(os.IdentityJSON, schema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode identity schema: %s. This is most likely a bug in the Provider, providers must not change the identity schema without updating the identity schema version", err.Error())
|
||||
return nil, fmt.Errorf("failed to decode identity: %s. This is most likely a bug in the Provider, providers must not change the identity schema without updating the identity schema version", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
189
internal/terraform/context_apply_identity_test.go
Normal file
189
internal/terraform/context_apply_identity_test.go
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
)
|
||||
|
||||
func TestContext2Apply_identity(t *testing.T) {
|
||||
for name, tc := range map[string]struct {
|
||||
mode plans.Mode
|
||||
prevRunState *states.State
|
||||
requiresReplace []cty.Path
|
||||
plannedIdentity cty.Value
|
||||
|
||||
expectedIdentity cty.Value
|
||||
}{
|
||||
"create": {
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
expectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
},
|
||||
|
||||
"update": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"foo"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
expectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
},
|
||||
|
||||
"delete": {
|
||||
mode: plans.DestroyMode,
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
plannedIdentity: cty.NilVal,
|
||||
expectedIdentity: cty.NullVal(cty.Object(map[string]cty.Type{
|
||||
"id": cty.String,
|
||||
})),
|
||||
},
|
||||
"replace": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"foo"}`),
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id":"foo"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
requiresReplace: []cty.Path{cty.GetAttrPath("id")},
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
expectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
resource "test_resource" "test" {
|
||||
id = "bar"
|
||||
}
|
||||
`,
|
||||
})
|
||||
p := testProvider("test")
|
||||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"test_resource": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IdentityTypes: map[string]*configschema.Object{
|
||||
"test_resource": &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityTypeSchemaVersions: map[string]uint64{
|
||||
"test_resource": 0,
|
||||
},
|
||||
})
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
||||
return providers.PlanResourceChangeResponse{
|
||||
PlannedState: req.ProposedNewState,
|
||||
PlannedIdentity: tc.plannedIdentity,
|
||||
RequiresReplace: tc.requiresReplace,
|
||||
}
|
||||
}
|
||||
|
||||
plan, diags := ctx.Plan(m, tc.prevRunState, &PlanOpts{Mode: tc.mode})
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
state, diags := ctx.Apply(plan, m, nil)
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
if !tc.expectedIdentity.IsNull() {
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"]
|
||||
|
||||
resourceInstanceStateSrc := state.Modules[""].Resources["test_resource.test"].Instance(addrs.NoKey).Current
|
||||
|
||||
resourceInstanceState, err := resourceInstanceStateSrc.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode resource instance state: %s", err)
|
||||
}
|
||||
|
||||
if !resourceInstanceState.Identity.RawEquals(tc.expectedIdentity) {
|
||||
t.Fatalf("unexpected identity: \n expected: %s\n got: %s", tc.expectedIdentity.GoString(), resourceInstanceState.Identity.GoString())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -275,7 +275,7 @@ func TestContext2Apply_unstable(t *testing.T) {
|
|||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"]
|
||||
rds := plan.Changes.ResourceInstance(addr)
|
||||
rd, err := rds.Decode(schema.Body.ImpliedType())
|
||||
rd, err := rds.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -869,8 +869,7 @@ func (c *Context) deferredResources(config *configs.Config, deferrals []*plans.D
|
|||
deferral.Change.Addr.Resource.Resource.Mode,
|
||||
deferral.Change.Addr.Resource.Resource.Type)
|
||||
|
||||
ty := schema.Body.ImpliedType()
|
||||
deferralSrc, err := deferral.Encode(ty)
|
||||
deferralSrc, err := deferral.Encode(schema)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
|
|
@ -1084,7 +1083,7 @@ func (c *Context) driftedResources(config *configs.Config, oldState, newState *s
|
|||
},
|
||||
}
|
||||
|
||||
changeSrc, err := change.Encode(ty)
|
||||
changeSrc, err := change.Encode(schema)
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return nil, diags
|
||||
|
|
|
|||
|
|
@ -1770,6 +1770,104 @@ The -target option is not for routine use, and is provided only for exceptional
|
|||
})
|
||||
}
|
||||
|
||||
func TestContext2Plan_movedResourceWithIdentity(t *testing.T) {
|
||||
addrA := mustResourceInstanceAddr("test_object.a")
|
||||
addrB := mustResourceInstanceAddr("test_object.b")
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
resource "test_object" "b" {
|
||||
}
|
||||
|
||||
moved {
|
||||
from = test_object.a
|
||||
to = test_object.b
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
||||
state := states.BuildState(func(s *states.SyncState) {
|
||||
// The prior state tracks test_object.a, which we should treat as
|
||||
// test_object.b because of the "moved" block in the config.
|
||||
s.SetResourceInstanceCurrent(addrA, &states.ResourceInstanceObjectSrc{
|
||||
AttrsJSON: []byte(`{}`),
|
||||
Status: states.ObjectReady,
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id": "before"}`),
|
||||
}, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`))
|
||||
})
|
||||
|
||||
p := simpleMockProvider()
|
||||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"test_object": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IdentityTypes: map[string]*configschema.Object{
|
||||
"test_object": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityTypeSchemaVersions: map[string]uint64{
|
||||
"test_object": 0,
|
||||
},
|
||||
})
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
plan, diags := ctx.Plan(m, state, &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
|
||||
}
|
||||
|
||||
t.Run(addrA.String(), func(t *testing.T) {
|
||||
instPlan := plan.Changes.ResourceInstance(addrA)
|
||||
if instPlan != nil {
|
||||
t.Fatalf("unexpected plan for %s; should've moved to %s", addrA, addrB)
|
||||
}
|
||||
})
|
||||
t.Run(addrB.String(), func(t *testing.T) {
|
||||
instPlan := plan.Changes.ResourceInstance(addrB)
|
||||
if instPlan == nil {
|
||||
t.Fatalf("no plan for %s at all", addrB)
|
||||
}
|
||||
|
||||
beforeIdentity, err := instPlan.BeforeIdentity.Decode(cty.Object(map[string]cty.Type{
|
||||
"id": cty.String,
|
||||
}))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error decoding before identity: %s", err)
|
||||
}
|
||||
|
||||
if beforeIdentity.IsNull() {
|
||||
t.Fatalf("after identity is null")
|
||||
}
|
||||
expectedIdentity := cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("before"),
|
||||
})
|
||||
if !beforeIdentity.RawEquals(expectedIdentity) {
|
||||
t.Fatalf("after identity doesn't match expected: expected %s, got %s", expectedIdentity.GoString(), beforeIdentity.GoString())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestContext2Plan_crossResourceMoveBasic(t *testing.T) {
|
||||
addrA := mustResourceInstanceAddr("test_object_one.a")
|
||||
addrB := mustResourceInstanceAddr("test_object_two.a")
|
||||
|
|
@ -1973,6 +2071,156 @@ func TestContext2Plan_crossProviderMove(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestContext2Plan_crossProviderMoveWithIdentity(t *testing.T) {
|
||||
addrA := mustResourceInstanceAddr("one_object.a")
|
||||
addrB := mustResourceInstanceAddr("two_object.a")
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
resource "two_object" "a" {
|
||||
}
|
||||
|
||||
moved {
|
||||
from = one_object.a
|
||||
to = two_object.a
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
||||
state := states.BuildState(func(s *states.SyncState) {
|
||||
// The prior state tracks test_object.a, which we should treat as
|
||||
// test_object.b because of the "moved" block in the config.
|
||||
s.SetResourceInstanceCurrent(addrA, &states.ResourceInstanceObjectSrc{
|
||||
AttrsJSON: []byte(`{"value":"before"}`),
|
||||
Status: states.ObjectReady,
|
||||
IdentityJSON: []byte(`{"id":"42"}`),
|
||||
}, mustProviderConfig(`provider["registry.terraform.io/hashicorp/one"]`))
|
||||
})
|
||||
|
||||
one := &testing_provider.MockProvider{}
|
||||
one.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"one_object": {
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"value": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Identity: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
two := &testing_provider.MockProvider{}
|
||||
two.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"two_object": {
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"value": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Identity: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"arn": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
},
|
||||
ServerCapabilities: providers.ServerCapabilities{
|
||||
MoveResourceState: true,
|
||||
},
|
||||
}
|
||||
|
||||
expectedTargetIdentity := cty.ObjectVal(map[string]cty.Value{
|
||||
"arn": cty.StringVal("arn:4223"),
|
||||
})
|
||||
|
||||
var receivedSourceIdentity []byte
|
||||
two.MoveResourceStateFn = func(req providers.MoveResourceStateRequest) providers.MoveResourceStateResponse {
|
||||
receivedSourceIdentity = req.SourceIdentity
|
||||
return providers.MoveResourceStateResponse{
|
||||
TargetState: cty.ObjectVal(map[string]cty.Value{
|
||||
"value": cty.StringVal("after"),
|
||||
}),
|
||||
TargetIdentity: expectedTargetIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("one"): testProviderFuncFixed(one),
|
||||
addrs.NewDefaultProvider("two"): testProviderFuncFixed(two),
|
||||
},
|
||||
})
|
||||
|
||||
plan, diags := ctx.Plan(m, state, &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
|
||||
}
|
||||
|
||||
t.Run(addrA.String(), func(t *testing.T) {
|
||||
instPlan := plan.Changes.ResourceInstance(addrA)
|
||||
if instPlan != nil {
|
||||
t.Fatalf("unexpected plan for %s; should've moved to %s", addrA, addrB)
|
||||
}
|
||||
|
||||
expectedSourceIdentity := `{"id":"42"}`
|
||||
if string(receivedSourceIdentity) != string(expectedSourceIdentity) {
|
||||
t.Errorf("wrong source identity\ngot: %s\nwant: %s", string(receivedSourceIdentity), string(expectedSourceIdentity))
|
||||
}
|
||||
})
|
||||
t.Run(addrB.String(), func(t *testing.T) {
|
||||
instPlan := plan.Changes.ResourceInstance(addrB)
|
||||
if instPlan == nil {
|
||||
t.Fatalf("no plan for %s at all", addrB)
|
||||
}
|
||||
|
||||
if got, want := instPlan.Addr, addrB; !got.Equal(want) {
|
||||
t.Errorf("wrong current address\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
if got, want := instPlan.PrevRunAddr, addrA; !got.Equal(want) {
|
||||
t.Errorf("wrong previous run address\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
if got, want := instPlan.Action, plans.Update; got != want {
|
||||
t.Errorf("wrong planned action\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
if got, want := instPlan.ActionReason, plans.ResourceInstanceChangeNoReason; got != want {
|
||||
t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
targetIdentity, err := instPlan.BeforeIdentity.Decode(cty.Object(map[string]cty.Type{
|
||||
"arn": cty.String,
|
||||
}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode after identity: %s", err)
|
||||
}
|
||||
|
||||
if !targetIdentity.RawEquals(expectedTargetIdentity) {
|
||||
t.Errorf("wrong target identity\ngot: %s\nwant: %s", targetIdentity.GoString(), expectedTargetIdentity.GoString())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestContext2Plan_crossResourceMoveMissingConfig(t *testing.T) {
|
||||
addrA := mustResourceInstanceAddr("test_object_one.a")
|
||||
addrB := mustResourceInstanceAddr("test_object_two.a")
|
||||
|
|
@ -2066,6 +2314,138 @@ func TestContext2Plan_crossResourceMoveMissingConfig(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestContext2Plan_crossResourceMoveWithIdentity(t *testing.T) {
|
||||
addrA := mustResourceInstanceAddr("test_object_one.a")
|
||||
addrB := mustResourceInstanceAddr("test_object_two.a")
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
resource "test_object_two" "a" {
|
||||
}
|
||||
|
||||
moved {
|
||||
from = test_object_one.a
|
||||
to = test_object_two.a
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
||||
state := states.BuildState(func(s *states.SyncState) {
|
||||
// The prior state tracks test_object.a, which we should treat as
|
||||
// test_object.b because of the "moved" block in the config.
|
||||
s.SetResourceInstanceCurrent(addrA, &states.ResourceInstanceObjectSrc{
|
||||
AttrsJSON: []byte(`{"value":"before"}`),
|
||||
Status: states.ObjectReady,
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"oneId": "before"}`),
|
||||
}, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`))
|
||||
})
|
||||
|
||||
expectedSourceIdentity := `{"oneId": "before"}`
|
||||
expectedTargetIdentity := cty.ObjectVal(map[string]cty.Value{
|
||||
"twoId": cty.StringVal("after"),
|
||||
})
|
||||
|
||||
p := &testing_provider.MockProvider{}
|
||||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"test_object_one": &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"value": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"test_object_two": &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"value": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IdentityTypes: map[string]*configschema.Object{
|
||||
"test_object_one": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"oneId": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
|
||||
"test_object_two": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"twoId": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityTypeSchemaVersions: map[string]uint64{
|
||||
"test_object_one": 0,
|
||||
"test_object_two": 2,
|
||||
},
|
||||
})
|
||||
p.GetProviderSchemaResponse.ServerCapabilities = providers.ServerCapabilities{
|
||||
MoveResourceState: true,
|
||||
}
|
||||
|
||||
var sourceIdentity []byte
|
||||
p.MoveResourceStateFn = func(req providers.MoveResourceStateRequest) providers.MoveResourceStateResponse {
|
||||
sourceIdentity = req.SourceIdentity
|
||||
return providers.MoveResourceStateResponse{
|
||||
TargetState: cty.ObjectVal(map[string]cty.Value{
|
||||
"value": cty.StringVal("after"),
|
||||
}),
|
||||
TargetIdentity: expectedTargetIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
plan, diags := ctx.Plan(m, state, &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
})
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
if string(expectedSourceIdentity) != string(sourceIdentity) {
|
||||
t.Fatalf("unexpected source identity; expected %s, got %s", string(expectedSourceIdentity), string(sourceIdentity))
|
||||
}
|
||||
|
||||
t.Run(addrA.String(), func(t *testing.T) {
|
||||
instPlan := plan.Changes.ResourceInstance(addrA)
|
||||
if instPlan != nil {
|
||||
t.Fatalf("unexpected plan for %s; should've moved to %s", addrA, addrB)
|
||||
}
|
||||
})
|
||||
t.Run(addrB.String(), func(t *testing.T) {
|
||||
instPlan := plan.Changes.ResourceInstance(addrB)
|
||||
if instPlan == nil {
|
||||
t.Fatalf("no plan for %s at all", addrB)
|
||||
}
|
||||
|
||||
identity, err := instPlan.BeforeIdentity.Decode(cty.Object(map[string]cty.Type{
|
||||
"twoId": cty.String,
|
||||
}))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error decoding identity: %s", err)
|
||||
}
|
||||
|
||||
if !identity.RawEquals(expectedTargetIdentity) {
|
||||
t.Fatalf("unexpected target identity; expected %s, got %s", expectedTargetIdentity.GoString(), identity.GoString())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestContext2Plan_untargetedResourceSchemaChange(t *testing.T) {
|
||||
// an untargeted resource which requires a schema migration should not
|
||||
// block planning due external changes in the plan.
|
||||
|
|
@ -5894,9 +6274,11 @@ resource "test_object" "obj" {
|
|||
Before: cty.ObjectVal(map[string]cty.Value{
|
||||
"value": cty.NullVal(cty.String),
|
||||
}),
|
||||
BeforeIdentity: cty.NullVal(cty.EmptyObject),
|
||||
After: cty.ObjectVal(map[string]cty.Value{
|
||||
"value": cty.NullVal(cty.String),
|
||||
}),
|
||||
AfterIdentity: cty.NullVal(cty.EmptyObject),
|
||||
},
|
||||
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
||||
RequiredReplace: cty.NewPathSet(cty.GetAttrPath("value")),
|
||||
|
|
@ -5911,6 +6293,7 @@ resource "test_object" "obj" {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(expectedChanges, changes, ctydebug.CmpOptions); diff != "" {
|
||||
t.Fatalf("unexpected changes: %s", diff)
|
||||
}
|
||||
|
|
@ -5989,9 +6372,11 @@ resource "test_object" "obj" {
|
|||
Before: cty.ObjectVal(map[string]cty.Value{
|
||||
"value": cty.NullVal(cty.String),
|
||||
}),
|
||||
BeforeIdentity: cty.NullVal(cty.EmptyObject),
|
||||
After: cty.ObjectVal(map[string]cty.Value{
|
||||
"value": cty.NullVal(cty.String),
|
||||
}),
|
||||
AfterIdentity: cty.NullVal(cty.EmptyObject),
|
||||
},
|
||||
ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
|
||||
RequiredReplace: cty.NewPathSet(cty.GetAttrPath("value")),
|
||||
|
|
@ -6163,17 +6548,21 @@ output "staying" {
|
|||
{
|
||||
Addr: mustAbsOutputValue("output.old"),
|
||||
Change: plans.Change{
|
||||
Action: plans.Delete,
|
||||
Before: cty.StringVal("old_value"),
|
||||
After: cty.NullVal(cty.DynamicPseudoType),
|
||||
Action: plans.Delete,
|
||||
Before: cty.StringVal("old_value"),
|
||||
BeforeIdentity: cty.NullVal(cty.DynamicPseudoType),
|
||||
After: cty.NullVal(cty.DynamicPseudoType),
|
||||
AfterIdentity: cty.NullVal(cty.DynamicPseudoType),
|
||||
},
|
||||
},
|
||||
{
|
||||
Addr: mustAbsOutputValue("output.staying"),
|
||||
Change: plans.Change{
|
||||
Action: plans.NoOp,
|
||||
Before: cty.StringVal("foo"),
|
||||
After: cty.StringVal("foo"),
|
||||
Action: plans.NoOp,
|
||||
Before: cty.StringVal("foo"),
|
||||
BeforeIdentity: cty.NullVal(cty.DynamicPseudoType),
|
||||
After: cty.StringVal("foo"),
|
||||
AfterIdentity: cty.NullVal(cty.DynamicPseudoType),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -6343,7 +6732,7 @@ resource "test_object" "a" {
|
|||
plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
|
||||
assertNoErrors(t, diags)
|
||||
|
||||
resourceType := p.GetProviderSchemaResponse.ResourceTypes["test_object"].Body.ImpliedType()
|
||||
resourceType := p.GetProviderSchemaResponse.ResourceTypes["test_object"]
|
||||
change, err := plan.Changes.ResourceInstance(mustResourceInstanceAddr(`test_object.a["old"]`)).Decode(resourceType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ func TestContext2Plan_resource_identity_refresh(t *testing.T) {
|
|||
ExpectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
ExpectedError: fmt.Errorf("failed to decode identity schema: unsupported attribute \"arn\". This is most likely a bug in the Provider, providers must not change the identity schema without updating the identity schema version"),
|
||||
ExpectedError: fmt.Errorf("failed to decode identity: unsupported attribute \"arn\". This is most likely a bug in the Provider, providers must not change the identity schema without updating the identity schema version"),
|
||||
},
|
||||
"identity upgrade succeeds": {
|
||||
StoredIdentitySchemaVersion: 1,
|
||||
|
|
@ -173,7 +173,7 @@ func TestContext2Plan_resource_identity_refresh(t *testing.T) {
|
|||
ExpectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
ExpectedError: fmt.Errorf("Provider produced different identity: Provider \"registry.terraform.io/hashicorp/aws\" planned an different identity for aws_instance.web during refresh. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker."),
|
||||
ExpectedError: fmt.Errorf("Provider produced different identity: Provider \"registry.terraform.io/hashicorp/aws\" returned a different identity for aws_instance.web than the previously stored one. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker."),
|
||||
},
|
||||
"identity with unknowns": {
|
||||
IdentitySchema: providers.IdentitySchema{
|
||||
|
|
@ -191,7 +191,7 @@ func TestContext2Plan_resource_identity_refresh(t *testing.T) {
|
|||
IdentityData: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
}),
|
||||
ExpectedError: fmt.Errorf("Provider produced invalid identity: Provider \"registry.terraform.io/hashicorp/aws\" planned an identity with unknown values for aws_instance.web during refresh. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker."),
|
||||
ExpectedError: fmt.Errorf("Provider produced invalid identity: Provider \"registry.terraform.io/hashicorp/aws\" returned an identity with unknown values for aws_instance.web. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker."),
|
||||
},
|
||||
|
||||
"identity with marks": {
|
||||
|
|
@ -210,7 +210,7 @@ func TestContext2Plan_resource_identity_refresh(t *testing.T) {
|
|||
IdentityData: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("marked value").Mark(marks.Sensitive),
|
||||
}),
|
||||
ExpectedError: fmt.Errorf("Provider produced invalid identity: Provider \"registry.terraform.io/hashicorp/aws\" planned an identity with marks for aws_instance.web during refresh. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker."),
|
||||
ExpectedError: fmt.Errorf("Provider produced invalid identity: Provider \"registry.terraform.io/hashicorp/aws\" returned an identity with marks for aws_instance.web. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker."),
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
|
@ -261,17 +261,13 @@ func TestContext2Plan_resource_identity_refresh(t *testing.T) {
|
|||
})
|
||||
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"]
|
||||
ty := schema.Body.ImpliedType()
|
||||
readState, err := hcl2shim.HCL2ValueFromFlatmap(map[string]string{"id": "foo", "foo": "baz"}, ty)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p.ReadResourceResponse = &providers.ReadResourceResponse{
|
||||
NewState: readState,
|
||||
Identity: tc.IdentityData,
|
||||
p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
|
||||
return providers.ReadResourceResponse{
|
||||
NewState: req.PriorState,
|
||||
Identity: tc.IdentityData,
|
||||
}
|
||||
}
|
||||
|
||||
p.UpgradeResourceIdentityResponse = &tc.UpgradeResourceIdentityResponse
|
||||
|
||||
s, diags := ctx.Plan(m, state, &PlanOpts{Mode: plans.RefreshOnlyMode})
|
||||
|
|
@ -306,15 +302,6 @@ func TestContext2Plan_resource_identity_refresh(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newState, err := schema.Body.CoerceValue(fromState.Value)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !cmp.Equal(readState, newState, valueComparer) {
|
||||
t.Fatal(cmp.Diff(readState, newState, valueComparer, equateEmpty))
|
||||
}
|
||||
|
||||
if tc.ExpectedIdentity.Equals(fromState.Identity).False() {
|
||||
t.Fatalf("wrong identity\nwant: %s\ngot: %s", tc.ExpectedIdentity.GoString(), fromState.Identity.GoString())
|
||||
}
|
||||
|
|
@ -425,3 +412,399 @@ func TestContext2Plan_resource_identity_refresh_destroy_deposed(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestContext2Plan_resource_identity_plan(t *testing.T) {
|
||||
for name, tc := range map[string]struct {
|
||||
mode plans.Mode
|
||||
prevRunState *states.State
|
||||
requiresReplace []cty.Path
|
||||
identitySchemaVersion int64
|
||||
|
||||
readResourceIdentity cty.Value
|
||||
upgradedIdentity cty.Value
|
||||
|
||||
plannedIdentity cty.Value
|
||||
expectedIdentity cty.Value
|
||||
expectedPriorIdentity cty.Value
|
||||
|
||||
expectDiagnostics tfdiags.Diagnostics
|
||||
}{
|
||||
"create": {
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
expectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
},
|
||||
"update": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
expectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
},
|
||||
"delete": {
|
||||
mode: plans.DestroyMode,
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
plannedIdentity: cty.NilVal,
|
||||
expectedIdentity: cty.NullVal(cty.Object(map[string]cty.Type{
|
||||
"id": cty.String,
|
||||
})),
|
||||
},
|
||||
"replace": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"foo"}`),
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id":"foo"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
requiresReplace: []cty.Path{cty.GetAttrPath("id")},
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
expectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
},
|
||||
|
||||
"update - changing identity": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
|
||||
expectDiagnostics: tfdiags.Diagnostics{
|
||||
tfdiags.Sourceless(tfdiags.Error, "Provider produced different identity", "Provider \"registry.terraform.io/hashicorp/test\" returned a different identity for test_resource.test than the previously stored one. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker."),
|
||||
},
|
||||
},
|
||||
|
||||
"update - updating identity schema version": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id":"foo"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
upgradedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
identitySchemaVersion: 1,
|
||||
expectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
},
|
||||
|
||||
"update - downgrading identity schema version": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
IdentitySchemaVersion: 2,
|
||||
IdentityJSON: []byte(`{"id":"foo"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"arn": cty.StringVal("arn:foo"),
|
||||
}),
|
||||
identitySchemaVersion: 1,
|
||||
expectDiagnostics: tfdiags.Diagnostics{
|
||||
tfdiags.Sourceless(tfdiags.Error, "Resource instance managed by newer provider version", "The current state of test_resource.test was created by a newer provider version than is currently selected. Upgrade the test provider to work with this state."),
|
||||
},
|
||||
},
|
||||
|
||||
"read and update": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
readResourceIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
plannedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
|
||||
expectedPriorIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
expectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
},
|
||||
"create with unknown identity": {
|
||||
plannedIdentity: cty.UnknownVal(cty.Object(map[string]cty.Type{
|
||||
"id": cty.String,
|
||||
})),
|
||||
expectedIdentity: cty.UnknownVal(cty.Object(map[string]cty.Type{
|
||||
"id": cty.String,
|
||||
})),
|
||||
},
|
||||
"update with unknown identity": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
plannedIdentity: cty.UnknownVal(cty.Object(map[string]cty.Type{
|
||||
"id": cty.String,
|
||||
})),
|
||||
|
||||
expectDiagnostics: tfdiags.Diagnostics{
|
||||
tfdiags.Sourceless(tfdiags.Error, "Provider produced invalid identity", "Provider \"registry.terraform.io/hashicorp/test\" returned an identity with unknown values for test_resource.test. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker."),
|
||||
},
|
||||
},
|
||||
"replace with unknown identity": {
|
||||
prevRunState: states.BuildState(func(s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "test",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"foo"}`),
|
||||
IdentitySchemaVersion: 0,
|
||||
IdentityJSON: []byte(`{"id":"foo"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
}),
|
||||
requiresReplace: []cty.Path{cty.GetAttrPath("id")},
|
||||
plannedIdentity: cty.UnknownVal(cty.Object(map[string]cty.Type{
|
||||
"id": cty.String,
|
||||
})),
|
||||
|
||||
expectedIdentity: cty.UnknownVal(cty.Object(map[string]cty.Type{
|
||||
"id": cty.String,
|
||||
})),
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
resource "test_resource" "test" {
|
||||
id = "newValue"
|
||||
}
|
||||
`,
|
||||
})
|
||||
p := testProvider("test")
|
||||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"test_resource": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IdentityTypes: map[string]*configschema.Object{
|
||||
"test_resource": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityTypeSchemaVersions: map[string]uint64{
|
||||
"test_resource": uint64(tc.identitySchemaVersion),
|
||||
},
|
||||
})
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
|
||||
identity := req.CurrentIdentity
|
||||
if !tc.readResourceIdentity.IsNull() {
|
||||
identity = tc.readResourceIdentity
|
||||
}
|
||||
|
||||
return providers.ReadResourceResponse{
|
||||
NewState: req.PriorState,
|
||||
Identity: identity,
|
||||
}
|
||||
}
|
||||
var plannedPriorIdentity cty.Value
|
||||
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
||||
plannedPriorIdentity = req.PriorIdentity
|
||||
return providers.PlanResourceChangeResponse{
|
||||
PlannedState: req.ProposedNewState,
|
||||
PlannedIdentity: tc.plannedIdentity,
|
||||
RequiresReplace: tc.requiresReplace,
|
||||
}
|
||||
}
|
||||
|
||||
p.UpgradeResourceIdentityFn = func(req providers.UpgradeResourceIdentityRequest) providers.UpgradeResourceIdentityResponse {
|
||||
|
||||
return providers.UpgradeResourceIdentityResponse{
|
||||
UpgradedIdentity: tc.upgradedIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
plan, diags := ctx.Plan(m, tc.prevRunState, &PlanOpts{Mode: plans.NormalMode})
|
||||
|
||||
if tc.expectDiagnostics != nil {
|
||||
tfdiags.AssertDiagnosticsMatch(t, diags, tc.expectDiagnostics)
|
||||
} else {
|
||||
assertNoDiagnostics(t, diags)
|
||||
|
||||
if !tc.expectedPriorIdentity.IsNull() {
|
||||
if !p.PlanResourceChangeCalled {
|
||||
t.Fatal("PlanResourceChangeFn was not called")
|
||||
}
|
||||
|
||||
if !plannedPriorIdentity.RawEquals(tc.expectedPriorIdentity) {
|
||||
t.Fatalf("wrong prior identity\nwant: %s\ngot: %s", tc.expectedPriorIdentity.GoString(), plannedPriorIdentity.GoString())
|
||||
}
|
||||
}
|
||||
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"]
|
||||
|
||||
change, err := plan.Changes.Resources[0].Decode(schema)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !tc.expectedIdentity.RawEquals(change.AfterIdentity) {
|
||||
t.Fatalf("wrong identity\nwant: %s\ngot: %s", tc.expectedIdentity.GoString(), change.AfterIdentity.GoString())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/dag"
|
||||
"github.com/hashicorp/terraform/internal/lang/langrefs"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
|
@ -53,8 +54,7 @@ type NodeAbstractResource struct {
|
|||
// interfaces if you're running those transforms, but also be explicitly
|
||||
// set if you already have that information.
|
||||
|
||||
Schema *configschema.Block // Schema for processing the configuration body
|
||||
SchemaVersion uint64 // Schema version of "Schema", as decided by the provider
|
||||
Schema *providers.Schema // Schema for processing the configuration body
|
||||
|
||||
// Config and RemovedConfig are mutally-exclusive, because a
|
||||
// resource can't be both declared and removed at the same time.
|
||||
|
|
@ -189,7 +189,7 @@ func (n *NodeAbstractResource) References() []*addrs.Reference {
|
|||
|
||||
// ReferencesInBlock() requires a schema
|
||||
if n.Schema != nil {
|
||||
refs, _ = langrefs.ReferencesInBlock(addrs.ParseRef, c.Config, n.Schema)
|
||||
refs, _ = langrefs.ReferencesInBlock(addrs.ParseRef, c.Config, n.Schema.Body)
|
||||
result = append(result, refs...)
|
||||
}
|
||||
|
||||
|
|
@ -388,9 +388,8 @@ func (n *NodeAbstractResource) AttachResourceConfig(c *configs.Resource, rc *con
|
|||
}
|
||||
|
||||
// GraphNodeAttachResourceSchema impl
|
||||
func (n *NodeAbstractResource) AttachResourceSchema(schema *configschema.Block, version uint64) {
|
||||
func (n *NodeAbstractResource) AttachResourceSchema(schema *providers.Schema) {
|
||||
n.Schema = schema
|
||||
n.SchemaVersion = version
|
||||
}
|
||||
|
||||
// GraphNodeAttachProviderMetaConfigs impl
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ func (n *NodeAbstractResourceInstance) planDestroy(ctx EvalContext, currentState
|
|||
PriorPrivate: currentState.Private,
|
||||
ProviderMeta: metaConfigVal,
|
||||
ClientCapabilities: ctx.ClientCapabilities(),
|
||||
PriorIdentity: currentState.Identity,
|
||||
})
|
||||
deferred = resp.Deferred
|
||||
|
||||
|
|
@ -449,6 +450,12 @@ func (n *NodeAbstractResourceInstance) planDestroy(ctx EvalContext, currentState
|
|||
diags = diags.Append(deferring.UnexpectedProviderDeferralDiagnostic(n.Addr))
|
||||
}
|
||||
|
||||
if !resp.PlannedIdentity.IsNull() {
|
||||
// Destroying is an operation where we allow identity changes.
|
||||
diags = diags.Append(n.validateIdentityKnown(resp.PlannedIdentity))
|
||||
diags = diags.Append(n.validateIdentity(resp.PlannedIdentity))
|
||||
}
|
||||
|
||||
// We may not have a config for all destroys, but we want to reference
|
||||
// it in the diagnostics if we do.
|
||||
if n.Config != nil {
|
||||
|
|
@ -480,9 +487,11 @@ func (n *NodeAbstractResourceInstance) planDestroy(ctx EvalContext, currentState
|
|||
PrevRunAddr: n.prevRunAddr(ctx),
|
||||
DeposedKey: deposedKey,
|
||||
Change: plans.Change{
|
||||
Action: plans.Delete,
|
||||
Before: currentState.Value,
|
||||
After: nullVal,
|
||||
Action: plans.Delete,
|
||||
Before: currentState.Value,
|
||||
BeforeIdentity: currentState.Identity,
|
||||
After: nullVal,
|
||||
AfterIdentity: resp.PlannedIdentity,
|
||||
},
|
||||
Private: resp.PlannedPrivate,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
|
|
@ -639,6 +648,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
Private: state.Private,
|
||||
ProviderMeta: metaConfigVal,
|
||||
ClientCapabilities: ctx.ClientCapabilities(),
|
||||
CurrentIdentity: state.Identity,
|
||||
})
|
||||
|
||||
// If we don't support deferrals, but the provider reports a deferral and does not
|
||||
|
|
@ -647,6 +657,11 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
diags = diags.Append(deferring.UnexpectedProviderDeferralDiagnostic(n.Addr))
|
||||
}
|
||||
|
||||
if !resp.Identity.IsNull() {
|
||||
diags = diags.Append(n.validateIdentityKnown(resp.Identity))
|
||||
diags = diags.Append(n.validateIdentity(resp.Identity))
|
||||
diags = diags.Append(n.validateIdentityDidNotChange(state, resp.Identity))
|
||||
}
|
||||
if resp.Deferred != nil {
|
||||
deferred = resp.Deferred
|
||||
}
|
||||
|
|
@ -711,8 +726,6 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
if writeOnlyDiags.HasErrors() {
|
||||
return state, deferred, diags
|
||||
}
|
||||
|
||||
diags = diags.Append(n.validateIdentity(state, resp.Identity, false))
|
||||
if diags.HasErrors() {
|
||||
return state, deferred, diags
|
||||
}
|
||||
|
|
@ -844,10 +857,12 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
var priorVal cty.Value
|
||||
var priorValTainted cty.Value
|
||||
var priorPrivate []byte
|
||||
var priorIdentity cty.Value
|
||||
if currentState != nil {
|
||||
if currentState.Status != states.ObjectTainted {
|
||||
priorVal = currentState.Value
|
||||
priorPrivate = currentState.Private
|
||||
priorIdentity = currentState.Identity
|
||||
} else {
|
||||
// If the prior state is tainted then we'll proceed below like
|
||||
// we're creating an entirely new object, but then turn it into
|
||||
|
|
@ -947,6 +962,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
PriorPrivate: priorPrivate,
|
||||
ProviderMeta: metaConfigVal,
|
||||
ClientCapabilities: ctx.ClientCapabilities(),
|
||||
PriorIdentity: priorIdentity,
|
||||
})
|
||||
// If we don't support deferrals, but the provider reports a deferral and does not
|
||||
// emit any error level diagnostics, we should emit an error.
|
||||
|
|
@ -966,6 +982,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
|
||||
plannedNewVal := resp.PlannedState
|
||||
plannedPrivate := resp.PlannedPrivate
|
||||
plannedIdentity := resp.PlannedIdentity
|
||||
|
||||
// These checks are only relevant if the provider is not deferring the
|
||||
// change.
|
||||
|
|
@ -1091,6 +1108,21 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
woPathSet := cty.NewPathSet(writeOnlyPaths...)
|
||||
action, actionReason := getAction(n.Addr, unmarkedPriorVal, unmarkedPlannedNewVal, createBeforeDestroy, woPathSet, forceReplace, reqRep)
|
||||
|
||||
if !plannedIdentity.IsNull() {
|
||||
if !action.IsReplace() && action != plans.Create {
|
||||
diags = diags.Append(n.validateIdentityKnown(plannedIdentity))
|
||||
// If the identity is not known we can not validate it did not change
|
||||
if !diags.HasErrors() {
|
||||
diags = diags.Append(n.validateIdentityDidNotChange(currentState, plannedIdentity))
|
||||
}
|
||||
}
|
||||
|
||||
diags = diags.Append(n.validateIdentity(plannedIdentity))
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
return nil, nil, deferred, keyData, diags
|
||||
}
|
||||
|
||||
if action.IsReplace() {
|
||||
// In this strange situation we want to produce a change object that
|
||||
// shows our real prior object but has a _new_ object that is built
|
||||
|
|
@ -1136,6 +1168,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
PriorPrivate: plannedPrivate,
|
||||
ProviderMeta: metaConfigVal,
|
||||
ClientCapabilities: ctx.ClientCapabilities(),
|
||||
PriorIdentity: plannedIdentity,
|
||||
})
|
||||
|
||||
// If we don't support deferrals, but the provider reports a deferral and does not
|
||||
|
|
@ -1143,6 +1176,11 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
if resp.Deferred != nil && !deferralAllowed && !resp.Diagnostics.HasErrors() {
|
||||
diags = diags.Append(deferring.UnexpectedProviderDeferralDiagnostic(n.Addr))
|
||||
}
|
||||
|
||||
if !resp.PlannedIdentity.IsNull() {
|
||||
// On replace the identity is allowed to change and be unknown.
|
||||
diags = diags.Append(n.validateIdentity(resp.PlannedIdentity))
|
||||
}
|
||||
}
|
||||
// We need to tread carefully here, since if there are any warnings
|
||||
// in here they probably also came out of our previous call to
|
||||
|
|
@ -1160,6 +1198,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
|
||||
plannedNewVal = resp.PlannedState
|
||||
plannedPrivate = resp.PlannedPrivate
|
||||
plannedIdentity = resp.PlannedIdentity
|
||||
|
||||
if len(unmarkedPaths) > 0 {
|
||||
plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths)
|
||||
|
|
@ -1253,12 +1292,14 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
Private: plannedPrivate,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
Change: plans.Change{
|
||||
Action: action,
|
||||
Before: priorVal,
|
||||
Action: action,
|
||||
Before: priorVal,
|
||||
BeforeIdentity: priorIdentity,
|
||||
// Pass the marked planned value through in our change
|
||||
// to propogate through evaluation.
|
||||
// Marks will be removed when encoding.
|
||||
After: plannedNewVal,
|
||||
AfterIdentity: plannedIdentity,
|
||||
GeneratedConfig: n.generatedConfigHCL,
|
||||
},
|
||||
ActionReason: actionReason,
|
||||
|
|
@ -1273,9 +1314,10 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
// must _also_ record the returned change in the active plan,
|
||||
// which the expression evaluator will use in preference to this
|
||||
// incomplete value recorded in the state.
|
||||
Status: states.ObjectPlanned,
|
||||
Value: plannedNewVal,
|
||||
Private: plannedPrivate,
|
||||
Status: states.ObjectPlanned,
|
||||
Value: plannedNewVal,
|
||||
Private: plannedPrivate,
|
||||
Identity: resp.PlannedIdentity,
|
||||
}
|
||||
|
||||
return plan, state, deferred, keyData, diags
|
||||
|
|
@ -2583,13 +2625,22 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
}
|
||||
} else {
|
||||
resp = provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{
|
||||
TypeName: n.Addr.Resource.Resource.Type,
|
||||
PriorState: unmarkedBefore,
|
||||
Config: unmarkedConfigVal,
|
||||
PlannedState: unmarkedAfter,
|
||||
PlannedPrivate: change.Private,
|
||||
ProviderMeta: metaConfigVal,
|
||||
TypeName: n.Addr.Resource.Resource.Type,
|
||||
PriorState: unmarkedBefore,
|
||||
Config: unmarkedConfigVal,
|
||||
PlannedState: unmarkedAfter,
|
||||
PlannedPrivate: change.Private,
|
||||
ProviderMeta: metaConfigVal,
|
||||
PlannedIdentity: change.AfterIdentity,
|
||||
})
|
||||
|
||||
if !resp.NewIdentity.IsNull() {
|
||||
diags = diags.Append(n.validateIdentityKnown(resp.NewIdentity))
|
||||
diags = diags.Append(n.validateIdentity(resp.NewIdentity))
|
||||
if !change.Action.IsReplace() {
|
||||
diags = diags.Append(n.validateIdentityDidNotChange(state, resp.NewIdentity))
|
||||
}
|
||||
}
|
||||
}
|
||||
applyDiags := resp.Diagnostics
|
||||
if applyConfig != nil {
|
||||
|
|
@ -2825,6 +2876,7 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
Value: newVal,
|
||||
Private: resp.Private,
|
||||
CreateBeforeDestroy: createBeforeDestroy,
|
||||
Identity: resp.NewIdentity,
|
||||
}
|
||||
return newState, diags
|
||||
|
||||
|
|
@ -2838,39 +2890,43 @@ func (n *NodeAbstractResourceInstance) prevRunAddr(ctx EvalContext) addrs.AbsRes
|
|||
return resourceInstancePrevRunAddr(ctx, n.Addr)
|
||||
}
|
||||
|
||||
func (n *NodeAbstractResourceInstance) validateIdentity(state *states.ResourceInstanceObject, newIdentity cty.Value, isAllowedToChange bool) (diags tfdiags.Diagnostics) {
|
||||
|
||||
// Identities can not contain unknown values
|
||||
func (n *NodeAbstractResourceInstance) validateIdentityKnown(newIdentity cty.Value) (diags tfdiags.Diagnostics) {
|
||||
if !newIdentity.IsWhollyKnown() {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid identity",
|
||||
fmt.Sprintf(
|
||||
"Provider %q planned an identity with unknown values for %s during refresh. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
"Provider %q returned an identity with unknown values for %s. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
n.ResolvedProvider.Provider, n.Addr,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
// Identities can not contain marks
|
||||
return diags
|
||||
}
|
||||
|
||||
func (n *NodeAbstractResourceInstance) validateIdentityDidNotChange(state *states.ResourceInstanceObject, newIdentity cty.Value) (diags tfdiags.Diagnostics) {
|
||||
if state != nil && !state.Identity.IsNull() && state.Identity.Equals(newIdentity).False() {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced different identity",
|
||||
fmt.Sprintf(
|
||||
"Provider %q returned a different identity for %s than the previously stored one. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
n.ResolvedProvider.Provider, n.Addr,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
func (n *NodeAbstractResourceInstance) validateIdentity(newIdentity cty.Value) (diags tfdiags.Diagnostics) {
|
||||
if _, marks := newIdentity.UnmarkDeep(); len(marks) > 0 {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid identity",
|
||||
fmt.Sprintf(
|
||||
"Provider %q planned an identity with marks for %s during refresh. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
n.ResolvedProvider.Provider, n.Addr,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
// Identities can not change (except if they are re-created or initially recorded)
|
||||
if !isAllowedToChange && !state.Identity.IsNull() && state.Identity.Equals(newIdentity).False() {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced different identity",
|
||||
fmt.Sprintf(
|
||||
"Provider %q planned an different identity for %s during refresh. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
"Provider %q returned an identity with marks for %s. \n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
|
||||
n.ResolvedProvider.Provider, n.Addr,
|
||||
),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -29,7 +29,12 @@ type nodeApplyableDeferredInstance struct {
|
|||
|
||||
func (n *nodeApplyableDeferredInstance) Execute(ctx EvalContext, _ walkOperation) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
change, err := n.ChangeSrc.Decode(n.Schema.ImpliedType())
|
||||
if n.Schema == nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(tfdiags.Error, "Failed to decode", "Terraform failed to decode a deferred change due to the schema not being present. This is a bug in Terraform; please report it!"))
|
||||
return diags
|
||||
}
|
||||
|
||||
change, err := n.ChangeSrc.Decode(*n.Schema)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(tfdiags.Error, "Failed to decode ", fmt.Sprintf("Terraform failed to decode a deferred change: %v\n\nThis is a bug in Terraform; please report it!", err)))
|
||||
}
|
||||
|
|
@ -55,7 +60,12 @@ type nodeApplyableDeferredPartialInstance struct {
|
|||
|
||||
func (n *nodeApplyableDeferredPartialInstance) Execute(ctx EvalContext, _ walkOperation) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
change, err := n.ChangeSrc.Decode(n.Schema.ImpliedType())
|
||||
if n.Schema == nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(tfdiags.Error, "Failed to decode", "Terraform failed to decode a deferred change due to the schema not being present. This is a bug in Terraform; please report it!"))
|
||||
return diags
|
||||
}
|
||||
|
||||
change, err := n.ChangeSrc.Decode(*n.Schema)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(tfdiags.Error, "Failed to decode ", fmt.Sprintf("Terraform failed to decode a deferred change: %v\n\nThis is a bug in Terraform; please report it!", err)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext)
|
|||
if deferred == nil {
|
||||
diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, refreshState))
|
||||
}
|
||||
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/configs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/dag"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
)
|
||||
|
||||
// GraphNodeAttachResourceSchema is an interface implemented by node types
|
||||
|
|
@ -18,7 +19,7 @@ type GraphNodeAttachResourceSchema interface {
|
|||
GraphNodeConfigResource
|
||||
GraphNodeProviderConsumer
|
||||
|
||||
AttachResourceSchema(schema *configschema.Block, version uint64)
|
||||
AttachResourceSchema(schema *providers.Schema)
|
||||
}
|
||||
|
||||
// GraphNodeAttachProviderConfigSchema is an interface implemented by node types
|
||||
|
|
@ -74,7 +75,7 @@ func (t *AttachSchemaTransformer) Transform(g *Graph) error {
|
|||
continue
|
||||
}
|
||||
log.Printf("[TRACE] AttachSchemaTransformer: attaching resource schema to %s", dag.VertexName(v))
|
||||
tv.AttachResourceSchema(schema.Body, uint64(schema.Version))
|
||||
tv.AttachResourceSchema(&schema)
|
||||
}
|
||||
|
||||
if tv, ok := v.(GraphNodeAttachProviderConfigSchema); ok {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import (
|
|||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/dag"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
)
|
||||
|
||||
// ResourceCountTransformer is a GraphTransformer that expands the count
|
||||
|
|
@ -17,7 +17,7 @@ import (
|
|||
// This assumes that the count is already interpolated.
|
||||
type ResourceCountTransformer struct {
|
||||
Concrete ConcreteResourceInstanceNodeFunc
|
||||
Schema *configschema.Block
|
||||
Schema *providers.Schema
|
||||
|
||||
Addr addrs.ConfigResource
|
||||
InstanceAddrs []addrs.AbsResourceInstance
|
||||
|
|
|
|||
Loading…
Reference in a new issue