mirror of
https://github.com/hashicorp/terraform.git
synced 2026-02-03 20:50:59 -05:00
Store resource identities in state (TF-23255) (#36464)
* Persist resource identity in Terraform state * make syncdeps * Move identity schema merging closer to the protocol * mock GetResourceIdentitySchemas * Fix identity refresh tests * Add more tests * Change grcpwrap upgrade identity * Review feedback * Remove unnecessary version conversion * Check if GetResourceIdentitySchemas RPC call is implemented * Update function signature docs * Adapt protocol changes * Check unimplemented error for identities in GetSchema
This commit is contained in:
parent
ec0ecca1a6
commit
b2b42c0fb4
79 changed files with 2654 additions and 630 deletions
|
|
@ -57,19 +57,19 @@ func TestLocalProvider(t *testing.T, b *Local, name string, schema providers.Pro
|
|||
return resp
|
||||
}
|
||||
|
||||
rSchema, _ := schema.SchemaForResourceType(addrs.ManagedResourceMode, req.TypeName)
|
||||
if rSchema == nil {
|
||||
rSchema = &configschema.Block{} // default schema is empty
|
||||
rSchema := schema.SchemaForResourceType(addrs.ManagedResourceMode, req.TypeName)
|
||||
if rSchema.Body == nil {
|
||||
rSchema.Body = &configschema.Block{} // default schema is empty
|
||||
}
|
||||
plannedVals := map[string]cty.Value{}
|
||||
for name, attrS := range rSchema.Attributes {
|
||||
for name, attrS := range rSchema.Body.Attributes {
|
||||
val := req.ProposedNewState.GetAttr(name)
|
||||
if attrS.Computed && val.IsNull() {
|
||||
val = cty.UnknownVal(attrS.Type)
|
||||
}
|
||||
plannedVals[name] = val
|
||||
}
|
||||
for name := range rSchema.BlockTypes {
|
||||
for name := range rSchema.Body.BlockTypes {
|
||||
// For simplicity's sake we just copy the block attributes over
|
||||
// verbatim, since this package's mock providers are all relatively
|
||||
// simple -- we're testing the backend, not esoteric provider features.
|
||||
|
|
@ -99,7 +99,6 @@ func TestLocalProvider(t *testing.T, b *Local, name string, schema providers.Pro
|
|||
}
|
||||
|
||||
return p
|
||||
|
||||
}
|
||||
|
||||
// TestLocalSingleState is a backend implementation that wraps Local
|
||||
|
|
|
|||
|
|
@ -77,6 +77,14 @@ func (p *Provider) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
return resp
|
||||
}
|
||||
|
||||
func (p *Provider) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
return providers.GetResourceIdentitySchemasResponse{
|
||||
IdentityTypes: map[string]providers.IdentitySchema{
|
||||
"terraform_data": dataStoreResourceIdentitySchema(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateProviderConfig is used to validate the configuration values.
|
||||
func (p *Provider) ValidateProviderConfig(req providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
|
||||
// At this moment there is nothing to configure for the terraform provider,
|
||||
|
|
@ -150,6 +158,10 @@ func (p *Provider) UpgradeResourceState(req providers.UpgradeResourceStateReques
|
|||
return upgradeDataStoreResourceState(req)
|
||||
}
|
||||
|
||||
func (p *Provider) UpgradeResourceIdentity(req providers.UpgradeResourceIdentityRequest) providers.UpgradeResourceIdentityResponse {
|
||||
return upgradeDataStoreResourceIdentity(req)
|
||||
}
|
||||
|
||||
// ReadResource refreshes a resource and returns its current state.
|
||||
func (p *Provider) ReadResource(req providers.ReadResourceRequest) providers.ReadResourceResponse {
|
||||
return readDataStoreResourceState(req)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,23 @@ func dataStoreResourceSchema() providers.Schema {
|
|||
"id": {Type: cty.String, Computed: true},
|
||||
},
|
||||
},
|
||||
Identity: dataStoreResourceIdentitySchema().Body,
|
||||
}
|
||||
}
|
||||
|
||||
func dataStoreResourceIdentitySchema() providers.IdentitySchema {
|
||||
return providers.IdentitySchema{
|
||||
Version: 0,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Description: "The unique identifier for the data store.",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,6 +72,11 @@ func upgradeDataStoreResourceState(req providers.UpgradeResourceStateRequest) (r
|
|||
return resp
|
||||
}
|
||||
|
||||
func upgradeDataStoreResourceIdentity(providers.UpgradeResourceIdentityRequest) (resp providers.UpgradeResourceIdentityResponse) {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("The builtin provider does not support provider upgrades since it has not changed the identity schema yet."))
|
||||
return resp
|
||||
}
|
||||
|
||||
func readDataStoreResourceState(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
|
||||
resp.NewState = req.PriorState
|
||||
return resp
|
||||
|
|
|
|||
|
|
@ -472,17 +472,17 @@ func marshalResources(resources map[string]*configs.Resource, schemas *terraform
|
|||
}
|
||||
}
|
||||
|
||||
schema, schemaVer := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
v.Provider,
|
||||
v.Mode,
|
||||
v.Type,
|
||||
)
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
return nil, fmt.Errorf("no schema found for %s (in provider %s)", v.Addr().String(), v.Provider)
|
||||
}
|
||||
r.SchemaVersion = schemaVer
|
||||
r.SchemaVersion = uint64(schema.Version)
|
||||
|
||||
r.Expressions = marshalExpressions(v.Config, schema)
|
||||
r.Expressions = marshalExpressions(v.Config, schema.Body)
|
||||
|
||||
// Managed is populated only for Mode = addrs.ManagedResourceMode
|
||||
if v.Managed != nil && len(v.Managed.Provisioners) > 0 {
|
||||
|
|
|
|||
|
|
@ -424,16 +424,16 @@ func marshalResourceChange(rc *plans.ResourceInstanceChangeSrc, schemas *terrafo
|
|||
r.PreviousAddress = rc.PrevRunAddr.String()
|
||||
}
|
||||
|
||||
schema, _ := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
rc.ProviderAddr.Provider,
|
||||
addr.Resource.Resource.Mode,
|
||||
addr.Resource.Resource.Type,
|
||||
)
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
return r, fmt.Errorf("no schema found for %s (in provider %s)", r.Address, rc.ProviderAddr.Provider)
|
||||
}
|
||||
|
||||
changeV, err := rc.Decode(schema.ImpliedType())
|
||||
changeV, err := rc.Decode(schema.Body.ImpliedType())
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
|
@ -452,7 +452,7 @@ func marshalResourceChange(rc *plans.ResourceInstanceChangeSrc, schemas *terrafo
|
|||
return r, err
|
||||
}
|
||||
sensitivePaths := rc.BeforeSensitivePaths
|
||||
sensitivePaths = append(sensitivePaths, schema.SensitivePaths(changeV.Before, nil)...)
|
||||
sensitivePaths = append(sensitivePaths, schema.Body.SensitivePaths(changeV.Before, nil)...)
|
||||
bs := jsonstate.SensitiveAsBool(marks.MarkPaths(changeV.Before, marks.Sensitive, sensitivePaths))
|
||||
beforeSensitive, err = ctyjson.Marshal(bs, bs.Type())
|
||||
if err != nil {
|
||||
|
|
@ -479,7 +479,7 @@ func marshalResourceChange(rc *plans.ResourceInstanceChangeSrc, schemas *terrafo
|
|||
afterUnknown = unknownAsBool(changeV.After)
|
||||
}
|
||||
sensitivePaths := rc.AfterSensitivePaths
|
||||
sensitivePaths = append(sensitivePaths, schema.SensitivePaths(changeV.After, nil)...)
|
||||
sensitivePaths = append(sensitivePaths, schema.Body.SensitivePaths(changeV.After, nil)...)
|
||||
as := jsonstate.SensitiveAsBool(marks.MarkPaths(changeV.After, marks.Sensitive, sensitivePaths))
|
||||
afterSensitive, err = ctyjson.Marshal(as, as.Type())
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -195,16 +195,16 @@ func marshalPlanResources(changes *plans.ChangesSrc, ris []addrs.AbsResourceInst
|
|||
)
|
||||
}
|
||||
|
||||
schema, schemaVer := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
r.ProviderAddr.Provider,
|
||||
r.Addr.Resource.Resource.Mode,
|
||||
resource.Type,
|
||||
)
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
return nil, fmt.Errorf("no schema found for %s", r.Addr.String())
|
||||
}
|
||||
resource.SchemaVersion = schemaVer
|
||||
changeV, err := r.Decode(schema.ImpliedType())
|
||||
resource.SchemaVersion = uint64(schema.Version)
|
||||
changeV, err := r.Decode(schema.Body.ImpliedType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -220,10 +220,10 @@ func marshalPlanResources(changes *plans.ChangesSrc, ris []addrs.AbsResourceInst
|
|||
|
||||
if changeV.After != cty.NilVal {
|
||||
if changeV.After.IsWhollyKnown() {
|
||||
resource.AttributeValues = marshalAttributeValues(changeV.After, schema)
|
||||
resource.AttributeValues = marshalAttributeValues(changeV.After, schema.Body)
|
||||
} else {
|
||||
knowns := omitUnknowns(changeV.After)
|
||||
resource.AttributeValues = marshalAttributeValues(knowns, schema)
|
||||
resource.AttributeValues = marshalAttributeValues(knowns, schema.Body)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -379,7 +379,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
|||
)
|
||||
}
|
||||
|
||||
schema, version := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
r.ProviderConfig.Provider,
|
||||
resAddr.Mode,
|
||||
resAddr.Type,
|
||||
|
|
@ -387,16 +387,16 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
|||
|
||||
// It is possible that the only instance is deposed
|
||||
if ri.Current != nil {
|
||||
if version != ri.Current.SchemaVersion {
|
||||
return nil, fmt.Errorf("schema version %d for %s in state does not match version %d from the provider", ri.Current.SchemaVersion, resAddr, version)
|
||||
if schema.Version != int64(ri.Current.SchemaVersion) {
|
||||
return nil, fmt.Errorf("schema version %d for %s in state does not match version %d from the provider", ri.Current.SchemaVersion, resAddr, schema.Version)
|
||||
}
|
||||
|
||||
current.SchemaVersion = ri.Current.SchemaVersion
|
||||
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
return nil, fmt.Errorf("no schema found for %s (in provider %s)", resAddr.String(), r.ProviderConfig.Provider)
|
||||
}
|
||||
riObj, err := ri.Current.Decode(schema.ImpliedType())
|
||||
riObj, err := ri.Current.Decode(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -407,7 +407,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("preparing attribute values for %s: %w", current.Address, err)
|
||||
}
|
||||
sensitivePaths = append(sensitivePaths, schema.SensitivePaths(value, nil)...)
|
||||
sensitivePaths = append(sensitivePaths, schema.Body.SensitivePaths(value, nil)...)
|
||||
s := SensitiveAsBool(marks.MarkPaths(value, marks.Sensitive, sensitivePaths))
|
||||
v, err := ctyjson.Marshal(s, s.Type())
|
||||
if err != nil {
|
||||
|
|
@ -448,7 +448,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
|||
Index: current.Index,
|
||||
}
|
||||
|
||||
riObj, err := rios.Decode(schema.ImpliedType())
|
||||
riObj, err := rios.Decode(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -459,7 +459,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("preparing attribute values for %s: %w", current.Address, err)
|
||||
}
|
||||
sensitivePaths = append(sensitivePaths, schema.SensitivePaths(value, nil)...)
|
||||
sensitivePaths = append(sensitivePaths, schema.Body.SensitivePaths(value, nil)...)
|
||||
s := SensitiveAsBool(marks.MarkPaths(value, marks.Sensitive, sensitivePaths))
|
||||
v, err := ctyjson.Marshal(s, s.Type())
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -118,12 +118,12 @@ func TestOperation_planNoChanges(t *testing.T) {
|
|||
Type: "test_resource",
|
||||
Name: "somewhere",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
||||
schema, _ := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
addrs.NewDefaultProvider("test"),
|
||||
addr.Resource.Resource.Mode,
|
||||
addr.Resource.Resource.Type,
|
||||
)
|
||||
ty := schema.ImpliedType()
|
||||
ty := schema.Body.ImpliedType()
|
||||
rc := &plans.ResourceInstanceChange{
|
||||
Addr: addr,
|
||||
PrevRunAddr: addr,
|
||||
|
|
@ -159,12 +159,12 @@ func TestOperation_planNoChanges(t *testing.T) {
|
|||
Type: "test_resource",
|
||||
Name: "somewhere",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
||||
schema, _ := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
addrs.NewDefaultProvider("test"),
|
||||
addr.Resource.Resource.Mode,
|
||||
addr.Resource.Resource.Type,
|
||||
)
|
||||
ty := schema.ImpliedType()
|
||||
ty := schema.Body.ImpliedType()
|
||||
rc := &plans.ResourceInstanceChange{
|
||||
Addr: addr,
|
||||
PrevRunAddr: addr,
|
||||
|
|
@ -206,12 +206,12 @@ func TestOperation_planNoChanges(t *testing.T) {
|
|||
Type: "test_resource",
|
||||
Name: "somewhere",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
||||
schema, _ := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
addrs.NewDefaultProvider("test"),
|
||||
addr.Resource.Resource.Mode,
|
||||
addr.Resource.Resource.Type,
|
||||
)
|
||||
ty := schema.ImpliedType()
|
||||
ty := schema.Body.ImpliedType()
|
||||
rc := &plans.ResourceInstanceChange{
|
||||
Addr: addr,
|
||||
PrevRunAddr: addr,
|
||||
|
|
@ -252,12 +252,12 @@ func TestOperation_planNoChanges(t *testing.T) {
|
|||
Type: "test_resource",
|
||||
Name: "anywhere",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
||||
schema, _ := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
addrs.NewDefaultProvider("test"),
|
||||
addr.Resource.Resource.Mode,
|
||||
addr.Resource.Resource.Type,
|
||||
)
|
||||
ty := schema.ImpliedType()
|
||||
ty := schema.Body.ImpliedType()
|
||||
rc := &plans.ResourceInstanceChange{
|
||||
Addr: addr,
|
||||
PrevRunAddr: addrPrev,
|
||||
|
|
|
|||
|
|
@ -171,3 +171,17 @@ func (a *Attribute) internalValidate(name, prefix string) error {
|
|||
|
||||
return err
|
||||
}
|
||||
|
||||
func (o *Object) InternalValidate() error {
|
||||
var err error
|
||||
|
||||
for name, attrS := range o.Attributes {
|
||||
if attrS == nil {
|
||||
err = errors.Join(err, fmt.Errorf("%s: attribute schema is nil", name))
|
||||
continue
|
||||
}
|
||||
err = errors.Join(err, attrS.internalValidate(name, ""))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -327,6 +327,98 @@ func TestBlockInternalValidate(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestObjectInternalValidate(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Object *Object
|
||||
Errs []string
|
||||
}{
|
||||
"empty": {
|
||||
&Object{},
|
||||
[]string{},
|
||||
},
|
||||
"valid": {
|
||||
&Object{
|
||||
Attributes: map[string]*Attribute{
|
||||
"foo": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"bar": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
Nesting: NestingSingle,
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
"nil": {
|
||||
&Object{
|
||||
Attributes: map[string]*Attribute{
|
||||
"foo": nil,
|
||||
},
|
||||
Nesting: NestingSingle,
|
||||
},
|
||||
[]string{"foo: attribute schema is nil"},
|
||||
},
|
||||
"attribute with no flags set": {
|
||||
&Object{
|
||||
Attributes: map[string]*Attribute{
|
||||
"foo": {
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Nesting: NestingSingle,
|
||||
},
|
||||
[]string{"foo: must set Optional, Required or Computed"},
|
||||
},
|
||||
"attribute required and optional": {
|
||||
&Object{
|
||||
Attributes: map[string]*Attribute{
|
||||
"foo": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
Nesting: NestingSingle,
|
||||
},
|
||||
[]string{"foo: cannot set both Optional and Required"},
|
||||
},
|
||||
"attribute with missing type": {
|
||||
&Object{
|
||||
Attributes: map[string]*Attribute{
|
||||
"foo": {
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
Nesting: NestingSingle,
|
||||
},
|
||||
[]string{"foo: either Type or NestedType must be defined"},
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
errs := joinedErrors(test.Object.InternalValidate())
|
||||
if got, want := len(errs), len(test.Errs); got != want {
|
||||
t.Errorf("wrong number of errors %d; want %d", got, want)
|
||||
for _, err := range errs {
|
||||
t.Logf("- %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
if len(errs) > 0 {
|
||||
for i := range errs {
|
||||
if errs[i].Error() != test.Errs[i] {
|
||||
t.Errorf("wrong error: got %s, want %s", errs[i].Error(), test.Errs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func joinedErrors(err error) []error {
|
||||
if err == nil {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package grpcwrap
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
|
|
@ -23,14 +24,16 @@ import (
|
|||
// implementation.
|
||||
func Provider(p providers.Interface) tfplugin5.ProviderServer {
|
||||
return &provider{
|
||||
provider: p,
|
||||
schema: p.GetProviderSchema(),
|
||||
provider: p,
|
||||
schema: p.GetProviderSchema(),
|
||||
identitySchemas: p.GetResourceIdentitySchemas(),
|
||||
}
|
||||
}
|
||||
|
||||
type provider struct {
|
||||
provider providers.Interface
|
||||
schema providers.GetProviderSchemaResponse
|
||||
provider providers.Interface
|
||||
schema providers.GetProviderSchemaResponse
|
||||
identitySchemas providers.GetResourceIdentitySchemasResponse
|
||||
}
|
||||
|
||||
func (p *provider) GetMetadata(_ context.Context, req *tfplugin5.GetMetadata_Request) (*tfplugin5.GetMetadata_Response, error) {
|
||||
|
|
@ -66,7 +69,7 @@ func (p *provider) GetSchema(_ context.Context, req *tfplugin5.GetProviderSchema
|
|||
}
|
||||
for typ, dat := range p.schema.DataSources {
|
||||
resp.DataSourceSchemas[typ] = &tfplugin5.Schema{
|
||||
Version: dat.Version,
|
||||
Version: int64(dat.Version),
|
||||
Block: convert.ConfigSchemaToProto(dat.Body),
|
||||
}
|
||||
}
|
||||
|
|
@ -540,11 +543,45 @@ func (p *provider) CallFunction(_ context.Context, req *tfplugin5.CallFunction_R
|
|||
}
|
||||
|
||||
func (p *provider) GetResourceIdentitySchemas(_ context.Context, req *tfplugin5.GetResourceIdentitySchemas_Request) (*tfplugin5.GetResourceIdentitySchemas_Response, error) {
|
||||
panic("Not implemented yet")
|
||||
resp := &tfplugin5.GetResourceIdentitySchemas_Response{
|
||||
IdentitySchemas: map[string]*tfplugin5.ResourceIdentitySchema{},
|
||||
Diagnostics: []*tfplugin5.Diagnostic{},
|
||||
}
|
||||
|
||||
for name, schema := range p.identitySchemas.IdentityTypes {
|
||||
resp.IdentitySchemas[name] = convert.ResourceIdentitySchemaToProto(schema)
|
||||
}
|
||||
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, p.identitySchemas.Diagnostics)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider) UpgradeResourceIdentity(_ context.Context, req *tfplugin5.UpgradeResourceIdentity_Request) (*tfplugin5.UpgradeResourceIdentity_Response, error) {
|
||||
panic("Not implemented yet")
|
||||
resp := &tfplugin5.UpgradeResourceIdentity_Response{}
|
||||
resource, ok := p.identitySchemas.IdentityTypes[req.TypeName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("resource identity schema not found for type %q", req.TypeName)
|
||||
}
|
||||
ty := resource.Body.ImpliedType()
|
||||
upgradeResp := p.provider.UpgradeResourceIdentity(providers.UpgradeResourceIdentityRequest{
|
||||
TypeName: req.TypeName,
|
||||
Version: req.Version,
|
||||
RawIdentityJSON: req.RawIdentity.Json,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, upgradeResp.Diagnostics)
|
||||
if upgradeResp.Diagnostics.HasErrors() {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
dv, err := encodeDynamicValue(upgradeResp.UpgradedIdentity, ty)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.UpgradedIdentity = &tfplugin5.ResourceIdentityData{
|
||||
IdentityData: dv,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider) Stop(context.Context, *tfplugin5.Stop_Request) (*tfplugin5.Stop_Response, error) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package grpcwrap
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
|
|
@ -24,14 +25,16 @@ import (
|
|||
// internal provider implementation.
|
||||
func Provider6(p providers.Interface) tfplugin6.ProviderServer {
|
||||
return &provider6{
|
||||
provider: p,
|
||||
schema: p.GetProviderSchema(),
|
||||
provider: p,
|
||||
schema: p.GetProviderSchema(),
|
||||
identitySchemas: p.GetResourceIdentitySchemas(),
|
||||
}
|
||||
}
|
||||
|
||||
type provider6 struct {
|
||||
provider providers.Interface
|
||||
schema providers.GetProviderSchemaResponse
|
||||
provider providers.Interface
|
||||
schema providers.GetProviderSchemaResponse
|
||||
identitySchemas providers.GetResourceIdentitySchemasResponse
|
||||
}
|
||||
|
||||
func (p *provider6) GetMetadata(_ context.Context, req *tfplugin6.GetMetadata_Request) (*tfplugin6.GetMetadata_Response, error) {
|
||||
|
|
@ -68,13 +71,13 @@ func (p *provider6) GetProviderSchema(_ context.Context, req *tfplugin6.GetProvi
|
|||
}
|
||||
for typ, dat := range p.schema.DataSources {
|
||||
resp.DataSourceSchemas[typ] = &tfplugin6.Schema{
|
||||
Version: dat.Version,
|
||||
Version: int64(dat.Version),
|
||||
Block: convert.ConfigSchemaToProto(dat.Body),
|
||||
}
|
||||
}
|
||||
for typ, dat := range p.schema.EphemeralResourceTypes {
|
||||
resp.EphemeralResourceSchemas[typ] = &tfplugin6.Schema{
|
||||
Version: dat.Version,
|
||||
Version: int64(dat.Version),
|
||||
Block: convert.ConfigSchemaToProto(dat.Body),
|
||||
}
|
||||
}
|
||||
|
|
@ -590,11 +593,48 @@ func (p *provider6) CallFunction(_ context.Context, req *tfplugin6.CallFunction_
|
|||
}
|
||||
|
||||
func (p *provider6) GetResourceIdentitySchemas(_ context.Context, req *tfplugin6.GetResourceIdentitySchemas_Request) (*tfplugin6.GetResourceIdentitySchemas_Response, error) {
|
||||
panic("Not implemented yet")
|
||||
resp := &tfplugin6.GetResourceIdentitySchemas_Response{
|
||||
IdentitySchemas: map[string]*tfplugin6.ResourceIdentitySchema{},
|
||||
Diagnostics: []*tfplugin6.Diagnostic{},
|
||||
}
|
||||
|
||||
for name, schema := range p.identitySchemas.IdentityTypes {
|
||||
resp.IdentitySchemas[name] = convert.ResourceIdentitySchemaToProto(schema)
|
||||
}
|
||||
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, p.identitySchemas.Diagnostics)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider6) UpgradeResourceIdentity(_ context.Context, req *tfplugin6.UpgradeResourceIdentity_Request) (*tfplugin6.UpgradeResourceIdentity_Response, error) {
|
||||
panic("Not implemented yet")
|
||||
resp := &tfplugin6.UpgradeResourceIdentity_Response{}
|
||||
resource, ok := p.identitySchemas.IdentityTypes[req.TypeName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("resource identity schema not found for type %q", req.TypeName)
|
||||
}
|
||||
ty := resource.Body.ImpliedType()
|
||||
|
||||
upgradeResp := p.provider.UpgradeResourceIdentity(providers.UpgradeResourceIdentityRequest{
|
||||
TypeName: req.TypeName,
|
||||
Version: req.Version,
|
||||
RawIdentityJSON: req.RawIdentity.Json,
|
||||
})
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, upgradeResp.Diagnostics)
|
||||
|
||||
if upgradeResp.Diagnostics.HasErrors() {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
dv, err := encodeDynamicValue6(upgradeResp.UpgradedIdentity, ty)
|
||||
if err != nil {
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
resp.UpgradedIdentity = &tfplugin6.ResourceIdentityData{
|
||||
IdentityData: dv,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *provider6) StopProvider(context.Context, *tfplugin6.StopProvider_Request) (*tfplugin6.StopProvider_Response, error) {
|
||||
|
|
|
|||
|
|
@ -205,8 +205,8 @@ func (a *Analyzer) metaReferencesResourceInstance(moduleAddr addrs.ModuleInstanc
|
|||
return nil
|
||||
}
|
||||
|
||||
resourceTypeSchema, _ := providerSchema.SchemaForResourceAddr(addr.Resource)
|
||||
if resourceTypeSchema == nil {
|
||||
resourceTypeSchema := providerSchema.SchemaForResourceAddr(addr.Resource)
|
||||
if resourceTypeSchema.Body == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -234,11 +234,11 @@ func (a *Analyzer) metaReferencesResourceInstance(moduleAddr addrs.ModuleInstanc
|
|||
// Caller must also update "schema" if necessary.
|
||||
}
|
||||
traverseInBlock := func(name string) ([]hcl.Body, []hcl.Expression) {
|
||||
if attr := schema.Attributes[name]; attr != nil {
|
||||
if attr := schema.Body.Attributes[name]; attr != nil {
|
||||
// When we reach a specific attribute we can't traverse any deeper, because attributes are the leaves of the schema.
|
||||
schema = nil
|
||||
schema.Body = nil
|
||||
return traverseAttr(bodies, name)
|
||||
} else if blockType := schema.BlockTypes[name]; blockType != nil {
|
||||
} else if blockType := schema.Body.BlockTypes[name]; blockType != nil {
|
||||
// We need to take a different action here depending on
|
||||
// the nesting mode of the block type. Some require us
|
||||
// to traverse in two steps in order to select a specific
|
||||
|
|
@ -248,7 +248,7 @@ func (a *Analyzer) metaReferencesResourceInstance(moduleAddr addrs.ModuleInstanc
|
|||
case configschema.NestingSingle, configschema.NestingGroup:
|
||||
// There should be only zero or one blocks of this
|
||||
// type, so we can traverse in only one step.
|
||||
schema = &blockType.Block
|
||||
schema.Body = &blockType.Block
|
||||
return traverseNestedBlockSingle(bodies, name)
|
||||
case configschema.NestingMap, configschema.NestingList, configschema.NestingSet:
|
||||
steppingThrough = blockType
|
||||
|
|
@ -258,14 +258,14 @@ func (a *Analyzer) metaReferencesResourceInstance(moduleAddr addrs.ModuleInstanc
|
|||
// we add something new in future we'll bail out
|
||||
// here and conservatively return everything under
|
||||
// the current traversal point.
|
||||
schema = nil
|
||||
schema.Body = nil
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// We'll get here if the given name isn't in the schema at all. If so,
|
||||
// there's nothing else to be done here.
|
||||
schema = nil
|
||||
schema.Body = nil
|
||||
return nil, nil
|
||||
}
|
||||
Steps:
|
||||
|
|
@ -281,7 +281,7 @@ Steps:
|
|||
// a specific attribute) and so we'll stop early, assuming that
|
||||
// any remaining steps are traversals into an attribute expression
|
||||
// result.
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
break
|
||||
}
|
||||
|
||||
|
|
@ -299,10 +299,10 @@ Steps:
|
|||
continue
|
||||
}
|
||||
nextStep(traverseNestedBlockMap(bodies, steppingThroughType, step.Name))
|
||||
schema = &steppingThrough.Block
|
||||
schema.Body = &steppingThrough.Block
|
||||
default:
|
||||
nextStep(traverseInBlock(step.Name))
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
// traverseInBlock determined that we've traversed as
|
||||
// deep as we can with reference to schema, so we'll
|
||||
// stop here and just process whatever's selected.
|
||||
|
|
@ -320,7 +320,7 @@ Steps:
|
|||
continue
|
||||
}
|
||||
nextStep(traverseNestedBlockMap(bodies, steppingThroughType, keyVal.AsString()))
|
||||
schema = &steppingThrough.Block
|
||||
schema.Body = &steppingThrough.Block
|
||||
case configschema.NestingList:
|
||||
idxVal, err := convert.Convert(step.Key, cty.Number)
|
||||
if err != nil { // Invalid traversal, so can't have any refs
|
||||
|
|
@ -334,7 +334,7 @@ Steps:
|
|||
continue
|
||||
}
|
||||
nextStep(traverseNestedBlockList(bodies, steppingThroughType, idx))
|
||||
schema = &steppingThrough.Block
|
||||
schema.Body = &steppingThrough.Block
|
||||
default:
|
||||
// Note that NestingSet ends up in here because we don't
|
||||
// actually allow traversing into set-backed block types,
|
||||
|
|
@ -352,7 +352,7 @@ Steps:
|
|||
continue
|
||||
}
|
||||
nextStep(traverseInBlock(nameVal.AsString()))
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
// traverseInBlock determined that we've traversed as
|
||||
// deep as we can with reference to schema, so we'll
|
||||
// stop here and just process whatever's selected.
|
||||
|
|
@ -392,9 +392,9 @@ Steps:
|
|||
moreRefs, _ := langrefs.ReferencesInExpr(addrs.ParseRef, expr)
|
||||
refs = append(refs, moreRefs...)
|
||||
}
|
||||
if schema != nil {
|
||||
if schema.Body != nil {
|
||||
for _, body := range bodies {
|
||||
moreRefs, _ := langrefs.ReferencesInBlock(addrs.ParseRef, body, schema)
|
||||
moreRefs, _ := langrefs.ReferencesInBlock(addrs.ParseRef, body, schema.Body)
|
||||
refs = append(refs, moreRefs...)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,20 +17,14 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
|
|||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o=
|
||||
cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
|
||||
cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
|
|
@ -41,8 +35,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
|||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
|
||||
cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
|
|
@ -52,10 +44,6 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew
|
|||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/apparentlymart/go-versions v1.0.2 h1:n5Gg9YvSLK8Zzpy743J7abh2jt7z7ammOQ0oTd/5oA4=
|
||||
github.com/apparentlymart/go-versions v1.0.2/go.mod h1:YF5j7IQtrOAOnsGkniupEA5bfCjzd7i14yu0shZavyM=
|
||||
github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo=
|
||||
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
|
|
@ -85,8 +73,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
|||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
|
|
@ -108,8 +94,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
|||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
|
@ -137,28 +121,12 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
|
|||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
|
||||
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-getter v1.7.8 h1:mshVHx1Fto0/MydBekWan5zUipGq7jO0novchgMmSiY=
|
||||
github.com/hashicorp/go-getter v1.7.8/go.mod h1:2c6CboOEb9jG6YvmC9xdD+tyAFsrUaJPedwXDGr0TM4=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||
github.com/hashicorp/go-slug v0.16.3 h1:pe0PMwz2UWN1168QksdW/d7u057itB2gY568iF0E2Ns=
|
||||
github.com/hashicorp/go-slug v0.16.3/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
|
|
@ -173,13 +141,9 @@ github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S
|
|||
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
|
||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
|
@ -192,10 +156,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
|
|
@ -213,8 +173,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
|
@ -229,8 +187,6 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
|
@ -238,8 +194,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
@ -318,8 +272,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
|||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -436,8 +388,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
|
|
@ -457,8 +407,6 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
|
|||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
|
||||
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
|
@ -466,8 +414,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
|||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
|
@ -504,12 +450,6 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
|||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
|
|
@ -526,8 +466,6 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
|||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
|
@ -538,8 +476,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ var ignoreUnexported = cmpopts.IgnoreUnexported(
|
|||
proto.Schema_Block{},
|
||||
proto.Schema_NestedBlock{},
|
||||
proto.Schema_Attribute{},
|
||||
proto.ResourceIdentitySchema{},
|
||||
proto.ResourceIdentitySchema_IdentityAttribute{},
|
||||
)
|
||||
|
||||
func TestProtoDiagnostics(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -90,11 +90,19 @@ func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Sch
|
|||
}
|
||||
|
||||
// ProtoToProviderSchema takes a proto.Schema and converts it to a providers.Schema.
|
||||
func ProtoToProviderSchema(s *proto.Schema) providers.Schema {
|
||||
return providers.Schema{
|
||||
// It takes an optional resource identity schema for resources that support identity.
|
||||
func ProtoToProviderSchema(s *proto.Schema, id *proto.ResourceIdentitySchema) providers.Schema {
|
||||
schema := providers.Schema{
|
||||
Version: s.Version,
|
||||
Body: ProtoToConfigSchema(s.Block),
|
||||
}
|
||||
|
||||
if id != nil {
|
||||
schema.IdentityVersion = id.Version
|
||||
schema.Identity = ProtoToIdentitySchema(id.IdentityAttributes)
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
// ProtoToConfigSchema takes the GetSchcema_Block from a grpc response and converts it
|
||||
|
|
@ -188,3 +196,54 @@ func sortedKeys(m interface{}) []string {
|
|||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func ProtoToIdentitySchema(attributes []*proto.ResourceIdentitySchema_IdentityAttribute) *configschema.Object {
|
||||
obj := &configschema.Object{
|
||||
Attributes: make(map[string]*configschema.Attribute),
|
||||
Nesting: configschema.NestingSingle,
|
||||
}
|
||||
|
||||
for _, a := range attributes {
|
||||
attr := &configschema.Attribute{
|
||||
Description: a.Description,
|
||||
Required: a.RequiredForImport,
|
||||
Optional: a.OptionalForImport,
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(a.Type, &attr.Type); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
obj.Attributes[a.Name] = attr
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
func ResourceIdentitySchemaToProto(b providers.IdentitySchema) *proto.ResourceIdentitySchema {
|
||||
attrs := []*proto.ResourceIdentitySchema_IdentityAttribute{}
|
||||
for _, name := range sortedKeys(b.Body.Attributes) {
|
||||
a := b.Body.Attributes[name]
|
||||
|
||||
attr := &proto.ResourceIdentitySchema_IdentityAttribute{
|
||||
Name: name,
|
||||
Description: a.Description,
|
||||
RequiredForImport: a.Required,
|
||||
OptionalForImport: a.Optional,
|
||||
}
|
||||
|
||||
ty, err := json.Marshal(a.Type)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
attr.Type = ty
|
||||
|
||||
attrs = append(attrs, attr)
|
||||
}
|
||||
|
||||
return &proto.ResourceIdentitySchema{
|
||||
Version: b.Version,
|
||||
IdentityAttributes: attrs,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
proto "github.com/hashicorp/terraform/internal/tfplugin5"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
|
@ -362,3 +363,90 @@ func TestConvertProtoSchemaBlocks(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoToResourceIdentitySchema(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Attributes []*proto.ResourceIdentitySchema_IdentityAttribute
|
||||
Want *configschema.Object
|
||||
}{
|
||||
"simple": {
|
||||
[]*proto.ResourceIdentitySchema_IdentityAttribute{
|
||||
{
|
||||
Name: "id",
|
||||
Type: []byte(`"string"`),
|
||||
RequiredForImport: true,
|
||||
OptionalForImport: false,
|
||||
Description: "Something",
|
||||
},
|
||||
},
|
||||
&configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Description: "Something",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
converted := ProtoToIdentitySchema(tc.Attributes)
|
||||
if !cmp.Equal(converted, tc.Want, typeComparer, valueComparer, equateEmpty) {
|
||||
t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, valueComparer, equateEmpty))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceIdentitySchemaToProto(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Want *proto.ResourceIdentitySchema
|
||||
Schema providers.IdentitySchema
|
||||
}{
|
||||
"attributes": {
|
||||
&proto.ResourceIdentitySchema{
|
||||
Version: 1,
|
||||
IdentityAttributes: []*proto.ResourceIdentitySchema_IdentityAttribute{
|
||||
{
|
||||
Name: "optional",
|
||||
Type: []byte(`"string"`),
|
||||
OptionalForImport: true,
|
||||
},
|
||||
{
|
||||
Name: "required",
|
||||
Type: []byte(`"number"`),
|
||||
RequiredForImport: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
providers.IdentitySchema{
|
||||
Version: 1,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"optional": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
"required": {
|
||||
Type: cty.Number,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
converted := ResourceIdentitySchemaToProto(tc.Schema)
|
||||
if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported) {
|
||||
t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import (
|
|||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
"github.com/zclconf/go-cty/cty/msgpack"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/logging"
|
||||
|
|
@ -81,9 +83,6 @@ func (p *GRPCProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
defer p.mu.Unlock()
|
||||
|
||||
// check the global cache if we can
|
||||
// FIXME: A global cache is inappropriate when Terraform Core is being
|
||||
// used in a non-Terraform-CLI mode where we shouldn't assume that all
|
||||
// calls share the same provider implementations.
|
||||
if !p.Addr.IsZero() {
|
||||
if resp, ok := providers.SchemaCache.Get(p.Addr); ok && resp.ServerCapabilities.GetProviderSchemaOptional {
|
||||
logger.Trace("GRPCProvider: returning cached schema", p.Addr.String())
|
||||
|
|
@ -129,23 +128,38 @@ func (p *GRPCProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
return resp
|
||||
}
|
||||
|
||||
resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider)
|
||||
identResp, err := p.client.GetResourceIdentitySchemas(p.ctx, new(proto.GetResourceIdentitySchemas_Request))
|
||||
if err != nil {
|
||||
if status.Code(err) == codes.Unimplemented {
|
||||
// We don't treat this as an error if older providers don't implement this method,
|
||||
// so we create an empty map for identity schemas
|
||||
identResp = &proto.GetResourceIdentitySchemas_Response{
|
||||
IdentitySchemas: map[string]*proto.ResourceIdentitySchema{},
|
||||
}
|
||||
} else {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
return resp
|
||||
}
|
||||
}
|
||||
|
||||
resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider, nil)
|
||||
if protoResp.ProviderMeta == nil {
|
||||
logger.Debug("No provider meta schema returned")
|
||||
} else {
|
||||
resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta)
|
||||
resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta, nil)
|
||||
}
|
||||
|
||||
for name, res := range protoResp.ResourceSchemas {
|
||||
resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res)
|
||||
id := identResp.IdentitySchemas[name] // We're fine if the id is not found
|
||||
resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res, id)
|
||||
}
|
||||
|
||||
for name, data := range protoResp.DataSourceSchemas {
|
||||
resp.DataSources[name] = convert.ProtoToProviderSchema(data)
|
||||
resp.DataSources[name] = convert.ProtoToProviderSchema(data, nil)
|
||||
}
|
||||
|
||||
for name, ephem := range protoResp.EphemeralResourceSchemas {
|
||||
resp.EphemeralResourceTypes[name] = convert.ProtoToProviderSchema(ephem)
|
||||
resp.EphemeralResourceTypes[name] = convert.ProtoToProviderSchema(ephem, nil)
|
||||
}
|
||||
|
||||
if decls, err := convert.FunctionDeclsFromProto(protoResp.Functions); err == nil {
|
||||
|
|
@ -162,9 +176,6 @@ func (p *GRPCProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
}
|
||||
|
||||
// set the global cache if we can
|
||||
// FIXME: A global cache is inappropriate when Terraform Core is being
|
||||
// used in a non-Terraform-CLI mode where we shouldn't assume that all
|
||||
// calls share the same provider implementations.
|
||||
if !p.Addr.IsZero() {
|
||||
providers.SchemaCache.Set(p.Addr, resp)
|
||||
}
|
||||
|
|
@ -176,6 +187,38 @@ func (p *GRPCProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
return resp
|
||||
}
|
||||
|
||||
func (p *GRPCProvider) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
var resp providers.GetResourceIdentitySchemasResponse
|
||||
|
||||
resp.IdentityTypes = make(map[string]providers.IdentitySchema)
|
||||
|
||||
protoResp, err := p.client.GetResourceIdentitySchemas(p.ctx, new(proto.GetResourceIdentitySchemas_Request))
|
||||
if err != nil {
|
||||
if status.Code(err) == codes.Unimplemented {
|
||||
// We expect no error here if older providers don't implement this method
|
||||
return resp
|
||||
}
|
||||
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
return resp
|
||||
}
|
||||
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
return resp
|
||||
}
|
||||
|
||||
for name, res := range protoResp.IdentitySchemas {
|
||||
resp.IdentityTypes[name] = providers.IdentitySchema{
|
||||
Version: res.Version,
|
||||
Body: convert.ProtoToIdentitySchema(res.IdentityAttributes),
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
||||
logger.Trace("GRPCProvider: ValidateProviderConfig")
|
||||
|
||||
|
|
@ -333,6 +376,52 @@ func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ
|
|||
return resp
|
||||
}
|
||||
|
||||
func (p *GRPCProvider) UpgradeResourceIdentity(r providers.UpgradeResourceIdentityRequest) (resp providers.UpgradeResourceIdentityResponse) {
|
||||
logger.Trace("GRPCProvider: UpgradeResourceIdentity")
|
||||
|
||||
schema := p.GetProviderSchema()
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
resp.Diagnostics = schema.Diagnostics
|
||||
return resp
|
||||
}
|
||||
|
||||
resSchema, ok := schema.ResourceTypes[r.TypeName]
|
||||
if !ok {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource identity type %q", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
protoReq := &proto.UpgradeResourceIdentity_Request{
|
||||
TypeName: r.TypeName,
|
||||
Version: int64(r.Version),
|
||||
RawIdentity: &proto.RawState{
|
||||
Json: r.RawIdentityJSON,
|
||||
},
|
||||
}
|
||||
|
||||
protoResp, err := p.client.UpgradeResourceIdentity(p.ctx, protoReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
return resp
|
||||
}
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
|
||||
ty := resSchema.Identity.ImpliedType()
|
||||
resp.UpgradedIdentity = cty.NullVal(ty)
|
||||
if protoResp.UpgradedIdentity == nil {
|
||||
return resp
|
||||
}
|
||||
|
||||
identity, err := decodeDynamicValue(protoResp.UpgradedIdentity.IdentityData, ty)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.UpgradedIdentity = identity
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (p *GRPCProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
||||
logger.Trace("GRPCProvider: ConfigureProvider")
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"go.uber.org/mock/gomock"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
mockproto "github.com/hashicorp/terraform/internal/plugin/mock_proto"
|
||||
|
|
@ -35,6 +37,13 @@ func mockProviderClient(t *testing.T) *mockproto.MockProviderClient {
|
|||
gomock.Any(),
|
||||
).Return(providerProtoSchema(), nil)
|
||||
|
||||
// GetResourceIdentitySchemas is called as part of GetSchema
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(providerResourceIdentitySchemas(), nil)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +123,23 @@ func providerProtoSchema() *proto.GetProviderSchema_Response {
|
|||
}
|
||||
}
|
||||
|
||||
func providerResourceIdentitySchemas() *proto.GetResourceIdentitySchemas_Response {
|
||||
return &proto.GetResourceIdentitySchemas_Response{
|
||||
IdentitySchemas: map[string]*proto.ResourceIdentitySchema{
|
||||
"resource": {
|
||||
Version: 1,
|
||||
IdentityAttributes: []*proto.ResourceIdentitySchema_IdentityAttribute{
|
||||
{
|
||||
Name: "attr",
|
||||
Type: []byte(`"string"`),
|
||||
RequiredForImport: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetSchema(t *testing.T) {
|
||||
p := &GRPCProvider{
|
||||
client: mockProviderClient(t),
|
||||
|
|
@ -196,6 +222,94 @@ func TestGRPCProvider_GetSchema_ResponseErrorDiagnostic(t *testing.T) {
|
|||
checkDiagsHasError(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetSchema_IdentityError(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
client := mockproto.NewMockProviderClient(ctrl)
|
||||
|
||||
client.EXPECT().GetSchema(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(providerProtoSchema(), nil)
|
||||
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(&proto.GetResourceIdentitySchemas_Response{}, fmt.Errorf("test error"))
|
||||
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
resp := p.GetProviderSchema()
|
||||
|
||||
checkDiagsHasError(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetSchema_IdentityUnimplemented(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
client := mockproto.NewMockProviderClient(ctrl)
|
||||
|
||||
client.EXPECT().GetSchema(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(providerProtoSchema(), nil)
|
||||
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(&proto.GetResourceIdentitySchemas_Response{}, status.Error(codes.Unimplemented, "test error"))
|
||||
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
resp := p.GetProviderSchema()
|
||||
|
||||
checkDiags(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetResourceIdentitySchemas(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
client := mockproto.NewMockProviderClient(ctrl)
|
||||
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(providerResourceIdentitySchemas(), nil)
|
||||
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
resp := p.GetResourceIdentitySchemas()
|
||||
|
||||
checkDiags(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetResourceIdentitySchemas_Unimplemented(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
client := mockproto.NewMockProviderClient(ctrl)
|
||||
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(&proto.GetResourceIdentitySchemas_Response{}, status.Error(codes.Unimplemented, "test error"))
|
||||
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
resp := p.GetResourceIdentitySchemas()
|
||||
|
||||
checkDiags(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_PrepareProviderConfig(t *testing.T) {
|
||||
client := mockProviderClient(t)
|
||||
p := &GRPCProvider{
|
||||
|
|
@ -312,6 +426,84 @@ func TestGRPCProvider_UpgradeResourceStateJSON(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGRPCProvider_UpgradeResourceIdentity(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
response *proto.UpgradeResourceIdentity_Response
|
||||
expectError bool
|
||||
expectedValue cty.Value
|
||||
}{
|
||||
{
|
||||
"successful upgrade",
|
||||
&proto.UpgradeResourceIdentity_Response{
|
||||
UpgradedIdentity: &proto.ResourceIdentityData{
|
||||
IdentityData: &proto.DynamicValue{
|
||||
Json: []byte(`{"attr":"bar"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("bar")}),
|
||||
},
|
||||
{
|
||||
"response with error diagnostic",
|
||||
&proto.UpgradeResourceIdentity_Response{
|
||||
Diagnostics: []*proto.Diagnostic{
|
||||
{
|
||||
Severity: proto.Diagnostic_ERROR,
|
||||
Summary: "test error",
|
||||
Detail: "test error detail",
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
cty.NilVal,
|
||||
},
|
||||
{
|
||||
"schema mismatch",
|
||||
&proto.UpgradeResourceIdentity_Response{
|
||||
UpgradedIdentity: &proto.ResourceIdentityData{
|
||||
IdentityData: &proto.DynamicValue{
|
||||
Json: []byte(`{"attr_new":"bar"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
cty.NilVal,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
client := mockProviderClient(t)
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
client.EXPECT().UpgradeResourceIdentity(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(tc.response, nil)
|
||||
|
||||
resp := p.UpgradeResourceIdentity(providers.UpgradeResourceIdentityRequest{
|
||||
TypeName: "resource",
|
||||
Version: 0,
|
||||
RawIdentityJSON: []byte(`{"old_attr":"bar"}`),
|
||||
})
|
||||
|
||||
if tc.expectError {
|
||||
checkDiagsHasError(t, resp.Diagnostics)
|
||||
} else {
|
||||
checkDiags(t, resp.Diagnostics)
|
||||
|
||||
if !cmp.Equal(tc.expectedValue, resp.UpgradedIdentity, typeComparer, valueComparer, equateEmpty) {
|
||||
t.Fatal(cmp.Diff(tc.expectedValue, resp.UpgradedIdentity, typeComparer, valueComparer, equateEmpty))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCProvider_Configure(t *testing.T) {
|
||||
client := mockProviderClient(t)
|
||||
p := &GRPCProvider{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ var ignoreUnexported = cmpopts.IgnoreUnexported(
|
|||
proto.Schema_NestedBlock{},
|
||||
proto.Schema_Attribute{},
|
||||
proto.Schema_Object{},
|
||||
proto.ResourceIdentitySchema{},
|
||||
proto.ResourceIdentitySchema_IdentityAttribute{},
|
||||
)
|
||||
|
||||
func TestProtoDiagnostics(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -96,11 +96,44 @@ func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Sch
|
|||
}
|
||||
|
||||
// ProtoToProviderSchema takes a proto.Schema and converts it to a providers.Schema.
|
||||
func ProtoToProviderSchema(s *proto.Schema) providers.Schema {
|
||||
return providers.Schema{
|
||||
// It takes an optional resource identity schema for resources that support identity.
|
||||
func ProtoToProviderSchema(s *proto.Schema, id *proto.ResourceIdentitySchema) providers.Schema {
|
||||
schema := providers.Schema{
|
||||
Version: s.Version,
|
||||
Body: ProtoToConfigSchema(s.Block),
|
||||
}
|
||||
|
||||
if id != nil {
|
||||
schema.IdentityVersion = id.Version
|
||||
schema.Identity = ProtoToIdentitySchema(id.IdentityAttributes)
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
func ProtoToIdentitySchema(attributes []*proto.ResourceIdentitySchema_IdentityAttribute) *configschema.Object {
|
||||
obj := &configschema.Object{
|
||||
Attributes: make(map[string]*configschema.Attribute),
|
||||
Nesting: configschema.NestingSingle,
|
||||
}
|
||||
|
||||
for _, a := range attributes {
|
||||
attr := &configschema.Attribute{
|
||||
Description: a.Description,
|
||||
Required: a.RequiredForImport,
|
||||
Optional: a.OptionalForImport,
|
||||
}
|
||||
|
||||
if a.Type != nil {
|
||||
if err := json.Unmarshal(a.Type, &attr.Type); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
obj.Attributes[a.Name] = attr
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
// ProtoToConfigSchema takes the GetSchcema_Block from a grpc response and converts it
|
||||
|
|
@ -301,3 +334,32 @@ func configschemaObjectToProto(b *configschema.Object) *proto.Schema_Object {
|
|||
Nesting: nesting,
|
||||
}
|
||||
}
|
||||
|
||||
func ResourceIdentitySchemaToProto(schema providers.IdentitySchema) *proto.ResourceIdentitySchema {
|
||||
identityAttributes := []*proto.ResourceIdentitySchema_IdentityAttribute{}
|
||||
|
||||
for _, name := range sortedKeys(schema.Body.Attributes) {
|
||||
a := schema.Body.Attributes[name]
|
||||
attr := &proto.ResourceIdentitySchema_IdentityAttribute{
|
||||
Name: name,
|
||||
Description: a.Description,
|
||||
RequiredForImport: a.Required,
|
||||
OptionalForImport: a.Optional,
|
||||
}
|
||||
|
||||
if a.Type != cty.NilType {
|
||||
ty, err := json.Marshal(a.Type)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
attr.Type = ty
|
||||
}
|
||||
|
||||
identityAttributes = append(identityAttributes, attr)
|
||||
}
|
||||
|
||||
return &proto.ResourceIdentitySchema{
|
||||
Version: schema.Version,
|
||||
IdentityAttributes: identityAttributes,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
proto "github.com/hashicorp/terraform/internal/tfplugin6"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
|
@ -624,3 +625,90 @@ func TestConvertProtoSchemaBlocks(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoToResourceIdentitySchema(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Attributes []*proto.ResourceIdentitySchema_IdentityAttribute
|
||||
Want *configschema.Object
|
||||
}{
|
||||
"simple": {
|
||||
[]*proto.ResourceIdentitySchema_IdentityAttribute{
|
||||
{
|
||||
Name: "id",
|
||||
Type: []byte(`"string"`),
|
||||
RequiredForImport: true,
|
||||
OptionalForImport: false,
|
||||
Description: "Something",
|
||||
},
|
||||
},
|
||||
&configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Description: "Something",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
converted := ProtoToIdentitySchema(tc.Attributes)
|
||||
if !cmp.Equal(converted, tc.Want, typeComparer, valueComparer, equateEmpty) {
|
||||
t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, valueComparer, equateEmpty))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceIdentitySchemaToProto(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Want *proto.ResourceIdentitySchema
|
||||
Schema providers.IdentitySchema
|
||||
}{
|
||||
"attributes": {
|
||||
&proto.ResourceIdentitySchema{
|
||||
Version: 1,
|
||||
IdentityAttributes: []*proto.ResourceIdentitySchema_IdentityAttribute{
|
||||
{
|
||||
Name: "optional",
|
||||
Type: []byte(`"string"`),
|
||||
OptionalForImport: true,
|
||||
},
|
||||
{
|
||||
Name: "required",
|
||||
Type: []byte(`"number"`),
|
||||
RequiredForImport: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
providers.IdentitySchema{
|
||||
Version: 1,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"optional": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
"required": {
|
||||
Type: cty.Number,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
converted := ResourceIdentitySchemaToProto(tc.Schema)
|
||||
if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported) {
|
||||
t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import (
|
|||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
"github.com/zclconf/go-cty/cty/msgpack"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/logging"
|
||||
|
|
@ -80,9 +82,6 @@ func (p *GRPCProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
defer p.mu.Unlock()
|
||||
|
||||
// check the global cache if we can
|
||||
// FIXME: A global cache is inappropriate when Terraform Core is being
|
||||
// used in a non-Terraform-CLI mode where we shouldn't assume that all
|
||||
// calls share the same provider implementations.
|
||||
if !p.Addr.IsZero() {
|
||||
if resp, ok := providers.SchemaCache.Get(p.Addr); ok && resp.ServerCapabilities.GetProviderSchemaOptional {
|
||||
logger.Trace("GRPCProvider.v6: returning cached schema", p.Addr.String())
|
||||
|
|
@ -129,23 +128,38 @@ func (p *GRPCProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
return resp
|
||||
}
|
||||
|
||||
resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider)
|
||||
identResp, err := p.client.GetResourceIdentitySchemas(p.ctx, new(proto6.GetResourceIdentitySchemas_Request))
|
||||
if err != nil {
|
||||
if status.Code(err) == codes.Unimplemented {
|
||||
// We don't treat this as an error if older providers don't implement this method,
|
||||
// so we create an empty map for identity schemas
|
||||
identResp = &proto6.GetResourceIdentitySchemas_Response{
|
||||
IdentitySchemas: map[string]*proto6.ResourceIdentitySchema{},
|
||||
}
|
||||
} else {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
return resp
|
||||
}
|
||||
}
|
||||
|
||||
resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider, nil)
|
||||
if protoResp.ProviderMeta == nil {
|
||||
logger.Debug("No provider meta schema returned")
|
||||
} else {
|
||||
resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta)
|
||||
resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta, nil)
|
||||
}
|
||||
|
||||
for name, res := range protoResp.ResourceSchemas {
|
||||
resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res)
|
||||
id := identResp.IdentitySchemas[name] // We're fine if the id is not found
|
||||
resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res, id)
|
||||
}
|
||||
|
||||
for name, data := range protoResp.DataSourceSchemas {
|
||||
resp.DataSources[name] = convert.ProtoToProviderSchema(data)
|
||||
resp.DataSources[name] = convert.ProtoToProviderSchema(data, nil)
|
||||
}
|
||||
|
||||
for name, ephem := range protoResp.EphemeralResourceSchemas {
|
||||
resp.EphemeralResourceTypes[name] = convert.ProtoToProviderSchema(ephem)
|
||||
resp.EphemeralResourceTypes[name] = convert.ProtoToProviderSchema(ephem, nil)
|
||||
}
|
||||
|
||||
if decls, err := convert.FunctionDeclsFromProto(protoResp.Functions); err == nil {
|
||||
|
|
@ -162,9 +176,6 @@ func (p *GRPCProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
}
|
||||
|
||||
// set the global cache if we can
|
||||
// FIXME: A global cache is inappropriate when Terraform Core is being
|
||||
// used in a non-Terraform-CLI mode where we shouldn't assume that all
|
||||
// calls share the same provider implementations.
|
||||
if !p.Addr.IsZero() {
|
||||
providers.SchemaCache.Set(p.Addr, resp)
|
||||
}
|
||||
|
|
@ -176,6 +187,40 @@ func (p *GRPCProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
return resp
|
||||
}
|
||||
|
||||
func (p *GRPCProvider) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
logger.Trace("GRPCProvider.v6: GetResourceIdentitySchemas")
|
||||
|
||||
var resp providers.GetResourceIdentitySchemasResponse
|
||||
|
||||
resp.IdentityTypes = make(map[string]providers.IdentitySchema)
|
||||
|
||||
protoResp, err := p.client.GetResourceIdentitySchemas(p.ctx, new(proto6.GetResourceIdentitySchemas_Request))
|
||||
if err != nil {
|
||||
if status.Code(err) == codes.Unimplemented {
|
||||
// We expect no error here if older providers don't implement this method
|
||||
return resp
|
||||
}
|
||||
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
return resp
|
||||
}
|
||||
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
return resp
|
||||
}
|
||||
|
||||
for name, res := range protoResp.IdentitySchemas {
|
||||
resp.IdentityTypes[name] = providers.IdentitySchema{
|
||||
Version: res.Version,
|
||||
Body: convert.ProtoToIdentitySchema(res.IdentityAttributes),
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
||||
logger.Trace("GRPCProvider.v6: ValidateProviderConfig")
|
||||
|
||||
|
|
@ -326,6 +371,52 @@ func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ
|
|||
return resp
|
||||
}
|
||||
|
||||
func (p *GRPCProvider) UpgradeResourceIdentity(r providers.UpgradeResourceIdentityRequest) (resp providers.UpgradeResourceIdentityResponse) {
|
||||
logger.Trace("GRPCProvider.v6: UpgradeResourceIdentity")
|
||||
|
||||
schema := p.GetProviderSchema()
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
resp.Diagnostics = schema.Diagnostics
|
||||
return resp
|
||||
}
|
||||
|
||||
resSchema, ok := schema.ResourceTypes[r.TypeName]
|
||||
if !ok {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown resource type %q", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
|
||||
protoReq := &proto6.UpgradeResourceIdentity_Request{
|
||||
TypeName: r.TypeName,
|
||||
Version: int64(r.Version),
|
||||
RawIdentity: &proto6.RawState{
|
||||
Json: r.RawIdentityJSON,
|
||||
},
|
||||
}
|
||||
|
||||
protoResp, err := p.client.UpgradeResourceIdentity(p.ctx, protoReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err))
|
||||
return resp
|
||||
}
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
|
||||
ty := resSchema.Identity.ImpliedType()
|
||||
resp.UpgradedIdentity = cty.NullVal(ty)
|
||||
if protoResp.UpgradedIdentity == nil {
|
||||
return resp
|
||||
}
|
||||
|
||||
identity, err := decodeDynamicValue(protoResp.UpgradedIdentity.IdentityData, ty)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.UpgradedIdentity = identity
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (p *GRPCProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
||||
logger.Trace("GRPCProvider.v6: ConfigureProvider")
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"go.uber.org/mock/gomock"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
mockproto "github.com/hashicorp/terraform/internal/plugin6/mock_proto"
|
||||
|
|
@ -42,6 +44,13 @@ func mockProviderClient(t *testing.T) *mockproto.MockProviderClient {
|
|||
gomock.Any(),
|
||||
).Return(providerProtoSchema(), nil)
|
||||
|
||||
// GetResourceIdentitySchemas is called as part of GetSchema
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(providerResourceIdentitySchemas(), nil)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +130,23 @@ func providerProtoSchema() *proto.GetProviderSchema_Response {
|
|||
}
|
||||
}
|
||||
|
||||
func providerResourceIdentitySchemas() *proto.GetResourceIdentitySchemas_Response {
|
||||
return &proto.GetResourceIdentitySchemas_Response{
|
||||
IdentitySchemas: map[string]*proto.ResourceIdentitySchema{
|
||||
"resource": {
|
||||
Version: 1,
|
||||
IdentityAttributes: []*proto.ResourceIdentitySchema_IdentityAttribute{
|
||||
{
|
||||
Name: "attr",
|
||||
Type: []byte(`"string"`),
|
||||
RequiredForImport: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetSchema(t *testing.T) {
|
||||
p := &GRPCProvider{
|
||||
client: mockProviderClient(t),
|
||||
|
|
@ -203,6 +229,94 @@ func TestGRPCProvider_GetSchema_ResponseErrorDiagnostic(t *testing.T) {
|
|||
checkDiagsHasError(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetSchema_IdentityError(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
client := mockproto.NewMockProviderClient(ctrl)
|
||||
|
||||
client.EXPECT().GetProviderSchema(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(providerProtoSchema(), nil)
|
||||
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(&proto.GetResourceIdentitySchemas_Response{}, fmt.Errorf("test error"))
|
||||
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
resp := p.GetProviderSchema()
|
||||
|
||||
checkDiagsHasError(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetSchema_IdentityUnimplemented(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
client := mockproto.NewMockProviderClient(ctrl)
|
||||
|
||||
client.EXPECT().GetProviderSchema(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(providerProtoSchema(), nil)
|
||||
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(&proto.GetResourceIdentitySchemas_Response{}, status.Error(codes.Unimplemented, "test error"))
|
||||
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
resp := p.GetProviderSchema()
|
||||
|
||||
checkDiags(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetResourceIdentitySchemas(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
client := mockproto.NewMockProviderClient(ctrl)
|
||||
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(providerResourceIdentitySchemas(), nil)
|
||||
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
resp := p.GetResourceIdentitySchemas()
|
||||
|
||||
checkDiags(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_GetResourceIdentitySchemas_Unimplemented(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
client := mockproto.NewMockProviderClient(ctrl)
|
||||
|
||||
client.EXPECT().GetResourceIdentitySchemas(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(&proto.GetResourceIdentitySchemas_Response{}, status.Error(codes.Unimplemented, "test error"))
|
||||
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
resp := p.GetResourceIdentitySchemas()
|
||||
|
||||
checkDiags(t, resp.Diagnostics)
|
||||
}
|
||||
|
||||
func TestGRPCProvider_PrepareProviderConfig(t *testing.T) {
|
||||
client := mockProviderClient(t)
|
||||
p := &GRPCProvider{
|
||||
|
|
@ -319,6 +433,84 @@ func TestGRPCProvider_UpgradeResourceStateJSON(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGRPCProvider_UpgradeResourceIdentity(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
response *proto.UpgradeResourceIdentity_Response
|
||||
expectError bool
|
||||
expectedValue cty.Value
|
||||
}{
|
||||
{
|
||||
"successful upgrade",
|
||||
&proto.UpgradeResourceIdentity_Response{
|
||||
UpgradedIdentity: &proto.ResourceIdentityData{
|
||||
IdentityData: &proto.DynamicValue{
|
||||
Json: []byte(`{"attr":"bar"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("bar")}),
|
||||
},
|
||||
{
|
||||
"response with error diagnostic",
|
||||
&proto.UpgradeResourceIdentity_Response{
|
||||
Diagnostics: []*proto.Diagnostic{
|
||||
{
|
||||
Severity: proto.Diagnostic_ERROR,
|
||||
Summary: "test error",
|
||||
Detail: "test error detail",
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
cty.NilVal,
|
||||
},
|
||||
{
|
||||
"schema mismatch",
|
||||
&proto.UpgradeResourceIdentity_Response{
|
||||
UpgradedIdentity: &proto.ResourceIdentityData{
|
||||
IdentityData: &proto.DynamicValue{
|
||||
Json: []byte(`{"attr_new":"bar"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
cty.NilVal,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
client := mockProviderClient(t)
|
||||
p := &GRPCProvider{
|
||||
client: client,
|
||||
}
|
||||
|
||||
client.EXPECT().UpgradeResourceIdentity(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(tc.response, nil)
|
||||
|
||||
resp := p.UpgradeResourceIdentity(providers.UpgradeResourceIdentityRequest{
|
||||
TypeName: "resource",
|
||||
Version: 0,
|
||||
RawIdentityJSON: []byte(`{"old_attr":"bar"}`),
|
||||
})
|
||||
|
||||
if tc.expectError {
|
||||
checkDiagsHasError(t, resp.Diagnostics)
|
||||
} else {
|
||||
checkDiags(t, resp.Diagnostics)
|
||||
|
||||
if !cmp.Equal(tc.expectedValue, resp.UpgradedIdentity, typeComparer, valueComparer, equateEmpty) {
|
||||
t.Fatal(cmp.Diff(tc.expectedValue, resp.UpgradedIdentity, typeComparer, valueComparer, equateEmpty))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCProvider_Configure(t *testing.T) {
|
||||
client := mockProviderClient(t)
|
||||
p := &GRPCProvider{
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func Provider() providers.Interface {
|
|||
GetProviderSchemaOptional: true,
|
||||
},
|
||||
Functions: map[string]providers.FunctionDecl{
|
||||
"noop": providers.FunctionDecl{
|
||||
"noop": {
|
||||
Parameters: []providers.FunctionParam{
|
||||
{
|
||||
Name: "noop",
|
||||
|
|
@ -80,6 +80,25 @@ func (s simple) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
return s.schema
|
||||
}
|
||||
|
||||
func (s simple) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
return providers.GetResourceIdentitySchemasResponse{
|
||||
IdentityTypes: map[string]providers.IdentitySchema{
|
||||
"simple_resource": {
|
||||
Version: 0,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s simple) ValidateProviderConfig(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
||||
return resp
|
||||
}
|
||||
|
|
@ -100,6 +119,15 @@ func (p simple) UpgradeResourceState(req providers.UpgradeResourceStateRequest)
|
|||
return resp
|
||||
}
|
||||
|
||||
func (p simple) UpgradeResourceIdentity(req providers.UpgradeResourceIdentityRequest) (resp providers.UpgradeResourceIdentityResponse) {
|
||||
schema := p.GetResourceIdentitySchemas().IdentityTypes[req.TypeName].Body
|
||||
ty := schema.ImpliedType()
|
||||
val, err := ctyjson.Unmarshal(req.RawIdentityJSON, ty)
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
resp.UpgradedIdentity = val
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s simple) ConfigureProvider(providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
||||
return resp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,25 @@ func (s simple) GetProviderSchema() providers.GetProviderSchemaResponse {
|
|||
return s.schema
|
||||
}
|
||||
|
||||
func (s simple) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
return providers.GetResourceIdentitySchemasResponse{
|
||||
IdentityTypes: map[string]providers.IdentitySchema{
|
||||
"simple_resource": {
|
||||
Version: 0,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s simple) ValidateProviderConfig(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
||||
return resp
|
||||
}
|
||||
|
|
@ -77,6 +96,15 @@ func (p simple) UpgradeResourceState(req providers.UpgradeResourceStateRequest)
|
|||
return resp
|
||||
}
|
||||
|
||||
func (p simple) UpgradeResourceIdentity(req providers.UpgradeResourceIdentityRequest) (resp providers.UpgradeResourceIdentityResponse) {
|
||||
schema := p.GetResourceIdentitySchemas().IdentityTypes[req.TypeName].Body
|
||||
ty := schema.ImpliedType()
|
||||
val, err := ctyjson.Unmarshal(req.RawIdentityJSON, ty)
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
resp.UpgradedIdentity = val
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s simple) ConfigureProvider(providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
||||
return resp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ type Mock struct {
|
|||
Provider Interface
|
||||
Data *configs.MockData
|
||||
|
||||
schema *GetProviderSchemaResponse
|
||||
schema *GetProviderSchemaResponse
|
||||
identitySchema *GetResourceIdentitySchemasResponse
|
||||
}
|
||||
|
||||
func (m *Mock) GetProviderSchema() GetProviderSchemaResponse {
|
||||
|
|
@ -66,6 +67,16 @@ func (m *Mock) GetProviderSchema() GetProviderSchemaResponse {
|
|||
return *m.schema
|
||||
}
|
||||
|
||||
func (m *Mock) GetResourceIdentitySchemas() GetResourceIdentitySchemasResponse {
|
||||
if m.identitySchema == nil {
|
||||
// Cache the schema, it's not changing.
|
||||
schema := m.Provider.GetResourceIdentitySchemas()
|
||||
|
||||
m.identitySchema = &schema
|
||||
}
|
||||
return *m.identitySchema
|
||||
}
|
||||
|
||||
func (m *Mock) ValidateProviderConfig(request ValidateProviderConfigRequest) (response ValidateProviderConfigResponse) {
|
||||
// The config for the mocked providers is consistent, and validated when we
|
||||
// parse the HCL directly. So we'll just make no change here.
|
||||
|
|
@ -131,6 +142,40 @@ func (m *Mock) UpgradeResourceState(request UpgradeResourceStateRequest) (respon
|
|||
return response
|
||||
}
|
||||
|
||||
func (m *Mock) UpgradeResourceIdentity(request UpgradeResourceIdentityRequest) (response UpgradeResourceIdentityResponse) {
|
||||
// We can't do this from a mocked provider, so we just return whatever identity
|
||||
// is in the request back unchanged.
|
||||
|
||||
schema := m.GetProviderSchema()
|
||||
response.Diagnostics = response.Diagnostics.Append(schema.Diagnostics)
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
// We couldn't retrieve the schema for some reason, so the mock
|
||||
// provider can't really function.
|
||||
return response
|
||||
}
|
||||
|
||||
resource, exists := schema.ResourceTypes[request.TypeName]
|
||||
if !exists {
|
||||
// This means something has gone wrong much earlier, we should have
|
||||
// failed a validation somewhere if a resource type doesn't exist.
|
||||
panic(fmt.Errorf("failed to retrieve identity schema for resource %s", request.TypeName))
|
||||
}
|
||||
|
||||
schemaType := resource.Identity.ImpliedType()
|
||||
value, err := ctyjson.Unmarshal(request.RawIdentityJSON, schemaType)
|
||||
|
||||
if err != nil {
|
||||
// Generally, we shouldn't get an error here. The mocked providers are
|
||||
// only used in tests, and we can't use different versions of providers
|
||||
// within/between tests so the types should always match up. As such,
|
||||
// we're not gonna return a super detailed error here.
|
||||
response.Diagnostics = response.Diagnostics.Append(err)
|
||||
return response
|
||||
}
|
||||
response.UpgradedIdentity = value
|
||||
return response
|
||||
}
|
||||
|
||||
func (m *Mock) ConfigureProvider(request ConfigureProviderRequest) (response ConfigureProviderResponse) {
|
||||
// Do nothing here, we don't have anything to configure within the mocked
|
||||
// providers. We don't want to call the original providers from here as
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
|
|
@ -17,6 +16,11 @@ type Interface interface {
|
|||
// GetSchema returns the complete schema for the provider.
|
||||
GetProviderSchema() GetProviderSchemaResponse
|
||||
|
||||
// GetResourceIdentitySchemas returns the identity schemas for all managed resources
|
||||
// for the provider. Usually you don't need to call this method directly as GetProviderSchema
|
||||
// will merge the identity schemas into the provider schema.
|
||||
GetResourceIdentitySchemas() GetResourceIdentitySchemasResponse
|
||||
|
||||
// ValidateProviderConfig allows the provider to validate the configuration.
|
||||
// The ValidateProviderConfigResponse.PreparedConfig field is unused. The
|
||||
// final configuration is not stored in the state, and any modifications
|
||||
|
|
@ -41,6 +45,12 @@ type Interface interface {
|
|||
// result is used for any further processing.
|
||||
UpgradeResourceState(UpgradeResourceStateRequest) UpgradeResourceStateResponse
|
||||
|
||||
// UpgradeResourceIdentity is called when the state loader encounters an
|
||||
// instance identity whose schema version is less than the one reported by
|
||||
// the currently-used version of the corresponding provider, and the upgraded
|
||||
// result is used for any further processing.
|
||||
UpgradeResourceIdentity(UpgradeResourceIdentityRequest) UpgradeResourceIdentityResponse
|
||||
|
||||
// Configure configures and initialized the provider.
|
||||
ConfigureProvider(ConfigureProviderRequest) ConfigureProviderResponse
|
||||
|
||||
|
|
@ -98,7 +108,7 @@ type Interface interface {
|
|||
// should only be used when handling a value for that method. The handling of
|
||||
// of schemas in any other context should always use ProviderSchema, so that
|
||||
// the in-memory representation can be more easily changed separately from the
|
||||
// RCP protocol.
|
||||
// RPC protocol.
|
||||
type GetProviderSchemaResponse struct {
|
||||
// Provider is the schema for the provider itself.
|
||||
Provider Schema
|
||||
|
|
@ -127,6 +137,25 @@ type GetProviderSchemaResponse struct {
|
|||
ServerCapabilities ServerCapabilities
|
||||
}
|
||||
|
||||
// GetResourceIdentitySchemasResponse is the return type for GetResourceIdentitySchemas,
|
||||
// and should only be used when handling a value for that method. The handling of
|
||||
// of schemas in any other context should always use ResourceIdentitySchemas, so that
|
||||
// the in-memory representation can be more easily changed separately from the
|
||||
// RPC protocol.
|
||||
type GetResourceIdentitySchemasResponse struct {
|
||||
// IdentityTypes map the resource type name to that type's identity schema.
|
||||
IdentityTypes map[string]IdentitySchema
|
||||
|
||||
// Diagnostics contains any warnings or errors from the method call.
|
||||
Diagnostics tfdiags.Diagnostics
|
||||
}
|
||||
|
||||
type IdentitySchema struct {
|
||||
Version int64
|
||||
|
||||
Body *configschema.Object
|
||||
}
|
||||
|
||||
// Schema pairs a provider or resource schema with that schema's version.
|
||||
// This is used to be able to upgrade the schema in UpgradeResourceState.
|
||||
//
|
||||
|
|
@ -136,6 +165,9 @@ type GetProviderSchemaResponse struct {
|
|||
type Schema struct {
|
||||
Version int64
|
||||
Body *configschema.Block
|
||||
|
||||
IdentityVersion int64
|
||||
Identity *configschema.Object
|
||||
}
|
||||
|
||||
// ServerCapabilities allows providers to communicate extra information
|
||||
|
|
@ -254,6 +286,26 @@ type UpgradeResourceStateResponse struct {
|
|||
Diagnostics tfdiags.Diagnostics
|
||||
}
|
||||
|
||||
type UpgradeResourceIdentityRequest struct {
|
||||
// TypeName is the name of the resource type being upgraded
|
||||
TypeName string
|
||||
|
||||
// Version is version of the schema that created the current identity.
|
||||
Version int64
|
||||
|
||||
// RawIdentityJSON contains the identity that needs to be
|
||||
// upgraded to match the current schema version.
|
||||
RawIdentityJSON []byte
|
||||
}
|
||||
|
||||
type UpgradeResourceIdentityResponse struct {
|
||||
// UpgradedState is the newly upgraded resource identity.
|
||||
UpgradedIdentity cty.Value
|
||||
|
||||
// Diagnostics contains any warnings or errors from the method call.
|
||||
Diagnostics tfdiags.Diagnostics
|
||||
}
|
||||
|
||||
type ConfigureProviderRequest struct {
|
||||
// Terraform version is the version string from the running instance of
|
||||
// terraform. Providers can use TerraformVersion to verify compatibility,
|
||||
|
|
@ -344,6 +396,10 @@ type ReadResourceResponse struct {
|
|||
// Deferred if present signals that the provider was not able to fully
|
||||
// complete this operation and a susequent run is required.
|
||||
Deferred *Deferred
|
||||
|
||||
// Identity is the object-typed value representing the identity of the remote
|
||||
// object within Terraform.
|
||||
Identity cty.Value
|
||||
}
|
||||
|
||||
type PlanResourceChangeRequest struct {
|
||||
|
|
@ -484,7 +540,7 @@ type ImportResourceStateResponse struct {
|
|||
}
|
||||
|
||||
// ImportedResource represents an object being imported into Terraform with the
|
||||
// help of a provider. An ImportedObject is a RemoteObject that has been read
|
||||
// help of a provider. An ImportedResource is a RemoteObject that has been read
|
||||
// by the provider's import handler but hasn't yet been committed to state.
|
||||
type ImportedResource struct {
|
||||
// TypeName is the name of the resource type associated with the
|
||||
|
|
@ -542,24 +598,6 @@ type MoveResourceStateResponse struct {
|
|||
Diagnostics tfdiags.Diagnostics
|
||||
}
|
||||
|
||||
// AsInstanceObject converts the receiving ImportedObject into a
|
||||
// ResourceInstanceObject that has status ObjectReady.
|
||||
//
|
||||
// The returned object does not know its own resource type, so the caller must
|
||||
// retain the ResourceType value from the source object if this information is
|
||||
// needed.
|
||||
//
|
||||
// The returned object also has no dependency addresses, but the caller may
|
||||
// freely modify the direct fields of the returned object without affecting
|
||||
// the receiver.
|
||||
func (ir ImportedResource) AsInstanceObject() *states.ResourceInstanceObject {
|
||||
return &states.ResourceInstanceObject{
|
||||
Status: states.ObjectReady,
|
||||
Value: ir.State,
|
||||
Private: ir.Private,
|
||||
}
|
||||
}
|
||||
|
||||
type ReadDataSourceRequest struct {
|
||||
// TypeName is the name of the data source type to Read.
|
||||
TypeName string
|
||||
|
|
|
|||
|
|
@ -12,13 +12,6 @@ import (
|
|||
// SchemaCache is a global cache of Schemas.
|
||||
// This will be accessed by both core and the provider clients to ensure that
|
||||
// large schemas are stored in a single location.
|
||||
//
|
||||
// FIXME: A global cache is inappropriate when Terraform Core is being
|
||||
// used in a non-Terraform-CLI mode where we shouldn't assume that all
|
||||
// calls share the same provider implementations. This would be better
|
||||
// as a per-terraform.Context cache instead, or to have callers preload
|
||||
// the schemas for the providers they intend to use and pass them in
|
||||
// to terraform.NewContext so we don't need to load them at runtime.
|
||||
var SchemaCache = &schemaCache{
|
||||
m: make(map[addrs.Provider]ProviderSchema),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ package providers
|
|||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
)
|
||||
|
||||
// ProviderSchema is an overall container for all of the schemas for all
|
||||
|
|
@ -14,26 +13,25 @@ import (
|
|||
type ProviderSchema = GetProviderSchemaResponse
|
||||
|
||||
// SchemaForResourceType attempts to find a schema for the given mode and type.
|
||||
// Returns nil if no such schema is available.
|
||||
func (ss ProviderSchema) SchemaForResourceType(mode addrs.ResourceMode, typeName string) (schema *configschema.Block, version uint64) {
|
||||
// Returns an empty schema if none is available.
|
||||
func (ss ProviderSchema) SchemaForResourceType(mode addrs.ResourceMode, typeName string) (schema Schema) {
|
||||
switch mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
res := ss.ResourceTypes[typeName]
|
||||
return res.Body, uint64(res.Version)
|
||||
return ss.ResourceTypes[typeName]
|
||||
case addrs.DataResourceMode:
|
||||
// Data resources don't have schema versions right now, since state is discarded for each refresh
|
||||
return ss.DataSources[typeName].Body, 0
|
||||
return ss.DataSources[typeName]
|
||||
case addrs.EphemeralResourceMode:
|
||||
// Ephemeral resources don't have schema versions because their objects never outlive a single phase
|
||||
return ss.EphemeralResourceTypes[typeName].Body, 0
|
||||
return ss.EphemeralResourceTypes[typeName]
|
||||
default:
|
||||
// Shouldn't happen, because the above cases are comprehensive.
|
||||
return nil, 0
|
||||
return Schema{}
|
||||
}
|
||||
}
|
||||
|
||||
// SchemaForResourceAddr attempts to find a schema for the mode and type from
|
||||
// the given resource address. Returns nil if no such schema is available.
|
||||
func (ss ProviderSchema) SchemaForResourceAddr(addr addrs.Resource) (schema *configschema.Block, version uint64) {
|
||||
// the given resource address. Returns an empty schema if none is available.
|
||||
func (ss ProviderSchema) SchemaForResourceAddr(addr addrs.Resource) (schema Schema) {
|
||||
return ss.SchemaForResourceType(addr.Mode, addr.Type)
|
||||
}
|
||||
|
||||
type ResourceIdentitySchemas = GetResourceIdentitySchemasResponse
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ type MockProvider struct {
|
|||
GetProviderSchemaCalled bool
|
||||
GetProviderSchemaResponse *providers.GetProviderSchemaResponse
|
||||
|
||||
GetResourceIdentitySchemasCalled bool
|
||||
GetResourceIdentitySchemasResponse *providers.GetResourceIdentitySchemasResponse
|
||||
|
||||
ValidateProviderConfigCalled bool
|
||||
ValidateProviderConfigResponse *providers.ValidateProviderConfigResponse
|
||||
ValidateProviderConfigRequest providers.ValidateProviderConfigRequest
|
||||
|
|
@ -55,6 +58,12 @@ type MockProvider struct {
|
|||
UpgradeResourceStateRequest providers.UpgradeResourceStateRequest
|
||||
UpgradeResourceStateFn func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse
|
||||
|
||||
UpgradeResourceIdentityCalled bool
|
||||
UpgradeResourceIdentityTypeName string
|
||||
UpgradeResourceIdentityResponse *providers.UpgradeResourceIdentityResponse
|
||||
UpgradeResourceIdentityRequest providers.UpgradeResourceIdentityRequest
|
||||
UpgradeResourceIdentityFn func(providers.UpgradeResourceIdentityRequest) providers.UpgradeResourceIdentityResponse
|
||||
|
||||
ConfigureProviderCalled bool
|
||||
ConfigureProviderResponse *providers.ConfigureProviderResponse
|
||||
ConfigureProviderRequest providers.ConfigureProviderRequest
|
||||
|
|
@ -142,6 +151,24 @@ func (p *MockProvider) getProviderSchema() providers.GetProviderSchemaResponse {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *MockProvider) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
p.GetResourceIdentitySchemasCalled = true
|
||||
|
||||
return p.getResourceIdentitySchemas()
|
||||
}
|
||||
|
||||
func (p *MockProvider) getResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
if p.GetResourceIdentitySchemasResponse != nil {
|
||||
return *p.GetResourceIdentitySchemasResponse
|
||||
}
|
||||
|
||||
return providers.GetResourceIdentitySchemasResponse{
|
||||
IdentityTypes: map[string]providers.IdentitySchema{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
|
@ -306,6 +333,44 @@ func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ
|
|||
return resp
|
||||
}
|
||||
|
||||
func (p *MockProvider) UpgradeResourceIdentity(r providers.UpgradeResourceIdentityRequest) (resp providers.UpgradeResourceIdentityResponse) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if !p.ConfigureProviderCalled {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before UpgradeResourceIdentity %q", r.TypeName))
|
||||
return resp
|
||||
}
|
||||
p.UpgradeResourceIdentityCalled = true
|
||||
p.UpgradeResourceIdentityRequest = r
|
||||
|
||||
if p.UpgradeResourceIdentityFn != nil {
|
||||
return p.UpgradeResourceIdentityFn(r)
|
||||
}
|
||||
|
||||
if p.UpgradeResourceIdentityResponse != nil {
|
||||
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))
|
||||
return resp
|
||||
}
|
||||
|
||||
identityType := identitySchema.Body.ImpliedType()
|
||||
|
||||
v, err := ctyjson.Unmarshal(r.RawIdentityJSON, identityType)
|
||||
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.UpgradedIdentity = v
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (p *MockProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/ephemeral"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
|
|
@ -100,21 +99,19 @@ func (m *crossTypeMover) prepareCrossTypeMove(stmt *MoveStatement, source, targe
|
|||
})
|
||||
return nil, diags
|
||||
}
|
||||
targetResourceSchema, targetResourceSchemaVersion := targetSchema.SchemaForResourceAddr(target.Resource)
|
||||
schema := targetSchema.SchemaForResourceAddr(target.Resource)
|
||||
return &crossTypeMove{
|
||||
targetProvider: targetProvider,
|
||||
targetProviderAddr: *targetProviderAddr,
|
||||
targetResourceSchema: targetResourceSchema,
|
||||
targetResourceSchemaVersion: targetResourceSchemaVersion,
|
||||
sourceProviderAddr: sourceProviderAddr,
|
||||
targetProvider: targetProvider,
|
||||
targetProviderAddr: *targetProviderAddr,
|
||||
targetResourceSchema: schema,
|
||||
sourceProviderAddr: sourceProviderAddr,
|
||||
}, diags
|
||||
}
|
||||
|
||||
type crossTypeMove struct {
|
||||
targetProvider providers.Interface
|
||||
targetProviderAddr addrs.AbsProviderConfig
|
||||
targetResourceSchema *configschema.Block
|
||||
targetResourceSchemaVersion uint64
|
||||
targetProvider providers.Interface
|
||||
targetProviderAddr addrs.AbsProviderConfig
|
||||
targetResourceSchema providers.Schema
|
||||
|
||||
sourceProviderAddr addrs.AbsProviderConfig
|
||||
}
|
||||
|
|
@ -162,7 +159,7 @@ func (move *crossTypeMove) applyCrossTypeMove(stmt *MoveStatement, source, targe
|
|||
)
|
||||
},
|
||||
resp.TargetState,
|
||||
move.targetResourceSchema,
|
||||
move.targetResourceSchema.Body,
|
||||
)
|
||||
diags = diags.Append(writeOnlyDiags)
|
||||
|
||||
|
|
@ -199,7 +196,8 @@ func (move *crossTypeMove) applyCrossTypeMove(stmt *MoveStatement, source, targe
|
|||
CreateBeforeDestroy: src.CreateBeforeDestroy,
|
||||
}
|
||||
|
||||
data, err := newValue.Encode(move.targetResourceSchema.ImpliedType(), move.targetResourceSchemaVersion)
|
||||
// TODO: We need to handle identity data in move scenarios.
|
||||
data, err := newValue.Encode(move.targetResourceSchema)
|
||||
if err != nil {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ func (provider *mockProvider) GetProviderSchema() providers.GetProviderSchemaRes
|
|||
}
|
||||
}
|
||||
|
||||
func (provider *mockProvider) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
panic("not implemented in mock")
|
||||
}
|
||||
|
||||
func (provider *mockProvider) ValidateProviderConfig(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
|
||||
panic("not implemented in mock")
|
||||
}
|
||||
|
|
@ -45,6 +49,10 @@ func (provider *mockProvider) UpgradeResourceState(providers.UpgradeResourceStat
|
|||
panic("not implemented in mock")
|
||||
}
|
||||
|
||||
func (provider *mockProvider) UpgradeResourceIdentity(providers.UpgradeResourceIdentityRequest) providers.UpgradeResourceIdentityResponse {
|
||||
panic("not implemented in mock")
|
||||
}
|
||||
|
||||
func (provider *mockProvider) ConfigureProvider(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse {
|
||||
panic("not implemented in mock")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,10 +100,6 @@ func (cp *Plugins) ProviderSchema(addr addrs.Provider) (providers.ProviderSchema
|
|||
// We skip this if we have preloaded schemas because that suggests that
|
||||
// our caller is not Terraform CLI and therefore it's probably inappropriate
|
||||
// to assume that provider schemas are unique process-wide.
|
||||
//
|
||||
// FIXME: A global cache is inappropriate when Terraform Core is being
|
||||
// used in a non-Terraform-CLI mode where we shouldn't assume that all
|
||||
// calls share the same provider implementations.
|
||||
schemas, ok := providers.SchemaCache.Get(addr)
|
||||
if ok {
|
||||
log.Printf("[TRACE] terraform.contextPlugins: Schema for provider %q is in the global cache", addr)
|
||||
|
|
@ -141,6 +137,30 @@ func (cp *Plugins) ProviderSchema(addr addrs.Provider) (providers.ProviderSchema
|
|||
if r.Version < 0 {
|
||||
return resp, fmt.Errorf("provider %s has invalid negative schema version for managed resource type %q, which is a bug in the provider", addr, t)
|
||||
}
|
||||
|
||||
// Validate resource identity schema if the resource has one
|
||||
if r.Identity != nil {
|
||||
if err := r.Identity.InternalValidate(); err != nil {
|
||||
return resp, fmt.Errorf("provider %s has invalid identity schema for managed resource type %q, which is a bug in the provider: %q", addr, t, err)
|
||||
}
|
||||
if r.IdentityVersion < 0 {
|
||||
return resp, fmt.Errorf("provider %s has invalid negative identity schema version for managed resource type %q, which is a bug in the provider", addr, t)
|
||||
}
|
||||
|
||||
for attrName, attrTy := range r.Identity.ImpliedType().AttributeTypes() {
|
||||
if attrTy.MapElementType() != nil {
|
||||
return resp, fmt.Errorf("provider %s has invalid schema for managed resource type %q, attribute %q is a map, which is not allowed in identity schemas", addr, t, attrName)
|
||||
}
|
||||
|
||||
if attrTy.SetElementType() != nil {
|
||||
return resp, fmt.Errorf("provider %s has invalid schema for managed resource type %q, attribute %q is a set, which is not allowed in identity schemas", addr, t, attrName)
|
||||
}
|
||||
|
||||
if attrTy.IsObjectType() {
|
||||
return resp, fmt.Errorf("provider %s has invalid schema for managed resource type %q, attribute %q is an object, which is not allowed in identity schemas", addr, t, attrName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for t, d := range resp.DataSources {
|
||||
|
|
@ -208,20 +228,15 @@ func (cp *Plugins) ProviderConfigSchema(providerAddr addrs.Provider) (*configsch
|
|||
// for the resource type of the given resource mode in that provider.
|
||||
//
|
||||
// ResourceTypeSchema will return an error if the provider schema lookup
|
||||
// fails, but will return nil if the provider schema lookup succeeds but then
|
||||
// the provider doesn't have a resource of the requested type.
|
||||
//
|
||||
// Managed resource types have versioned schemas, so the second return value
|
||||
// is the current schema version number for the requested resource. The version
|
||||
// is irrelevant for other resource modes.
|
||||
func (cp *Plugins) ResourceTypeSchema(providerAddr addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (*configschema.Block, uint64, error) {
|
||||
// fails, but will return an empty schema if the provider schema lookup
|
||||
// succeeds but then the provider doesn't have a resource of the requested type.
|
||||
func (cp *Plugins) ResourceTypeSchema(providerAddr addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (providers.Schema, error) {
|
||||
providerSchema, err := cp.ProviderSchema(providerAddr)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return providers.Schema{}, err
|
||||
}
|
||||
|
||||
schema, version := providerSchema.SchemaForResourceType(resourceMode, resourceType)
|
||||
return schema, version, nil
|
||||
return providerSchema.SchemaForResourceType(resourceMode, resourceType), nil
|
||||
}
|
||||
|
||||
// ProvisionerSchema uses a temporary instance of the provisioner with the
|
||||
|
|
|
|||
|
|
@ -32,18 +32,18 @@ func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block {
|
|||
}
|
||||
|
||||
// ResourceTypeConfig returns the schema for the configuration of a given
|
||||
// resource type belonging to a given provider type, or nil of no such
|
||||
// schema is available.
|
||||
// resource type belonging to a given provider type, or an empty schema
|
||||
// if no such schema is available.
|
||||
//
|
||||
// In many cases the provider type is inferrable from the resource type name,
|
||||
// but this is not always true because users can override the provider for
|
||||
// a resource using the "provider" meta-argument. Therefore it's important to
|
||||
// always pass the correct provider name, even though it many cases it feels
|
||||
// redundant.
|
||||
func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) {
|
||||
func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) providers.Schema {
|
||||
ps := ss.ProviderSchema(provider)
|
||||
if ps.ResourceTypes == nil {
|
||||
return nil, 0
|
||||
return providers.Schema{}
|
||||
}
|
||||
return ps.SchemaForResourceType(resourceMode, resourceType)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/collections"
|
||||
"github.com/hashicorp/terraform/internal/configs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
|
|
@ -36,7 +36,7 @@ type PlanProducer interface {
|
|||
RequiredComponents(ctx context.Context) collections.Set[stackaddrs.AbsComponent]
|
||||
|
||||
// ResourceSchema returns the schema for a resource type from a provider.
|
||||
ResourceSchema(ctx context.Context, providerTypeAddr addrs.Provider, mode addrs.ResourceMode, resourceType string) (*configschema.Block, error)
|
||||
ResourceSchema(ctx context.Context, providerTypeAddr addrs.Provider, mode addrs.ResourceMode, resourceType string) (providers.Schema, error)
|
||||
}
|
||||
|
||||
func FromPlan(ctx context.Context, config *configs.Config, plan *plans.Plan, refreshPlan *plans.Plan, action plans.Action, producer PlanProducer) ([]PlannedChange, tfdiags.Diagnostics) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/collections"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/plans/planfile"
|
||||
|
|
@ -364,9 +363,9 @@ type PlannedChangeResourceInstancePlanned struct {
|
|||
// Schema MUST be the same schema that was used to encode the dynamic
|
||||
// values inside ChangeSrc and PriorStateSrc.
|
||||
//
|
||||
// Can be nil if and only if ChangeSrc and PriorStateSrc are both nil
|
||||
// Can be empty if and only if ChangeSrc and PriorStateSrc are both nil
|
||||
// themselves.
|
||||
Schema *configschema.Block
|
||||
Schema providers.Schema
|
||||
}
|
||||
|
||||
var _ PlannedChange = (*PlannedChangeResourceInstancePlanned)(nil)
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ func TestApplyDestroy(t *testing.T) {
|
|||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self.testing_resource.data"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
NewStateSrc: nil, // We should be removing this from the state file.
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
},
|
||||
&stackstate.AppliedChangeInputVariable{
|
||||
Addr: mustStackInputVariable("id"),
|
||||
|
|
@ -185,7 +185,7 @@ func TestApplyDestroy(t *testing.T) {
|
|||
&stackstate.AppliedChangeResourceInstanceObject{
|
||||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self.data.testing_data_source.data"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
NewStateSrc: nil, // deleted
|
||||
},
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ func TestApplyDestroy(t *testing.T) {
|
|||
&stackstate.AppliedChangeResourceInstanceObject{
|
||||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self.data.testing_data_source.missing"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
NewStateSrc: nil,
|
||||
},
|
||||
&stackstate.AppliedChangeInputVariable{
|
||||
|
|
@ -264,7 +264,7 @@ func TestApplyDestroy(t *testing.T) {
|
|||
&stackstate.AppliedChangeResourceInstanceObject{
|
||||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self.data.testing_data_source.missing"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
NewStateSrc: nil, // deleted
|
||||
},
|
||||
&stackstate.AppliedChangeResourceInstanceObject{
|
||||
|
|
@ -306,13 +306,13 @@ func TestApplyDestroy(t *testing.T) {
|
|||
&stackstate.AppliedChangeResourceInstanceObject{
|
||||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self.data.testing_data_source.data"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
NewStateSrc: nil, // deleted
|
||||
},
|
||||
&stackstate.AppliedChangeResourceInstanceObject{
|
||||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self.testing_resource.data"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
NewStateSrc: nil, // deleted
|
||||
},
|
||||
&stackstate.AppliedChangeInputVariable{
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ func TestApply(t *testing.T) {
|
|||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self.testing_resource.data"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
NewStateSrc: nil,
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -625,7 +625,7 @@ func TestApply(t *testing.T) {
|
|||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self[\"removed\"].testing_resource.data"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
NewStateSrc: nil,
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
},
|
||||
&stackstate.AppliedChangeInputVariable{
|
||||
Addr: mustStackInputVariable("input"),
|
||||
|
|
@ -735,7 +735,7 @@ func TestApply(t *testing.T) {
|
|||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("stack.a.component.self.testing_resource.data"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
NewStateSrc: nil,
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -846,7 +846,7 @@ After applying this plan, Terraform will no longer manage these objects. You wil
|
|||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self.testing_resource.data"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
NewStateSrc: nil,
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -3969,7 +3969,7 @@ func TestApplyManuallyRemovedResource(t *testing.T) {
|
|||
ResourceInstanceObjectAddr: mustAbsResourceInstanceObject("component.self.testing_resource.missing"),
|
||||
ProviderConfigAddr: mustDefaultRootProvider("testing"),
|
||||
NewStateSrc: nil, // We should be removing this from the state file.
|
||||
Schema: nil,
|
||||
Schema: providers.Schema{},
|
||||
},
|
||||
&stackstate.AppliedChangeInputVariable{
|
||||
Addr: mustStackInputVariable("id"),
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/collections"
|
||||
"github.com/hashicorp/terraform/internal/configs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/instances"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/promising"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackplan"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackstate"
|
||||
|
|
@ -734,7 +734,7 @@ func (c *ComponentInstance) CheckApply(ctx context.Context) ([]stackstate.Applie
|
|||
}
|
||||
|
||||
// ResourceSchema implements stackplan.PlanProducer.
|
||||
func (c *ComponentInstance) ResourceSchema(ctx context.Context, providerTypeAddr addrs.Provider, mode addrs.ResourceMode, typ string) (*configschema.Block, error) {
|
||||
func (c *ComponentInstance) ResourceSchema(ctx context.Context, providerTypeAddr addrs.Provider, mode addrs.ResourceMode, typ string) (providers.Schema, error) {
|
||||
// This should not be able to fail with an error because we should
|
||||
// be retrieving the same schema that was already used to encode
|
||||
// the object we're working with. The error handling here is for
|
||||
|
|
@ -743,11 +743,11 @@ func (c *ComponentInstance) ResourceSchema(ctx context.Context, providerTypeAddr
|
|||
providerType := c.main.ProviderType(ctx, providerTypeAddr)
|
||||
providerSchema, err := providerType.Schema(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return providers.Schema{}, err
|
||||
}
|
||||
ret, _ := providerSchema.SchemaForResourceType(mode, typ)
|
||||
if ret == nil {
|
||||
return nil, fmt.Errorf("schema does not include %v %q", mode, typ)
|
||||
ret := providerSchema.SchemaForResourceType(mode, typ)
|
||||
if ret.Body == nil {
|
||||
return providers.Schema{}, fmt.Errorf("schema does not include %v %q", mode, typ)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/collections"
|
||||
"github.com/hashicorp/terraform/internal/configs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/instances"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/promising"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackplan"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackstate"
|
||||
|
|
@ -323,7 +323,7 @@ func (r *RemovedInstance) RequiredComponents(ctx context.Context) collections.Se
|
|||
}
|
||||
|
||||
// ResourceSchema implements stackplan.PlanProducer.
|
||||
func (r *RemovedInstance) ResourceSchema(ctx context.Context, providerTypeAddr addrs.Provider, mode addrs.ResourceMode, typ string) (*configschema.Block, error) {
|
||||
func (r *RemovedInstance) ResourceSchema(ctx context.Context, providerTypeAddr addrs.Provider, mode addrs.ResourceMode, typ string) (providers.Schema, error) {
|
||||
// This should not be able to fail with an error because we should
|
||||
// be retrieving the same schema that was already used to encode
|
||||
// the object we're working with. The error handling here is for
|
||||
|
|
@ -332,11 +332,11 @@ func (r *RemovedInstance) ResourceSchema(ctx context.Context, providerTypeAddr a
|
|||
providerType := r.main.ProviderType(ctx, providerTypeAddr)
|
||||
providerSchema, err := providerType.Schema(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return providers.Schema{}, err
|
||||
}
|
||||
ret, _ := providerSchema.SchemaForResourceType(mode, typ)
|
||||
if ret == nil {
|
||||
return nil, fmt.Errorf("schema does not include %v %q", mode, typ)
|
||||
ret := providerSchema.SchemaForResourceType(mode, typ)
|
||||
if ret.Body == nil {
|
||||
return providers.Schema{}, fmt.Errorf("schema does not include %v %q", mode, typ)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@ func (p *erroredProvider) GetProviderSchema() providers.GetProviderSchemaRespons
|
|||
return providers.GetProviderSchemaResponse{}
|
||||
}
|
||||
|
||||
func (p *erroredProvider) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
return providers.GetResourceIdentitySchemasResponse{}
|
||||
}
|
||||
|
||||
// ImportResourceState implements providers.Interface.
|
||||
func (p *erroredProvider) ImportResourceState(req providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
|
@ -180,6 +184,22 @@ func (p *erroredProvider) UpgradeResourceState(req providers.UpgradeResourceStat
|
|||
}
|
||||
}
|
||||
|
||||
func (p *erroredProvider) UpgradeResourceIdentity(req providers.UpgradeResourceIdentityRequest) providers.UpgradeResourceIdentityResponse {
|
||||
// Ideally we'd just skip this altogether and echo back what the caller
|
||||
// provided, but the request is in a different serialization format than
|
||||
// the response and so only the real provider can deal with this one.
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(tfdiags.AttributeValue(
|
||||
tfdiags.Error,
|
||||
"Provider configuration is invalid",
|
||||
"Cannot decode the prior state for this resource instance because its provider configuration is invalid.",
|
||||
nil, // nil attribute path means the overall configuration block
|
||||
))
|
||||
return providers.UpgradeResourceIdentityResponse{
|
||||
Diagnostics: diags,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateDataResourceConfig implements providers.Interface.
|
||||
func (p *erroredProvider) ValidateDataResourceConfig(req providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse {
|
||||
// We'll just optimistically assume the configuration is valid, so that
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ func (o *offlineProvider) GetProviderSchema() providers.GetProviderSchemaRespons
|
|||
return o.unconfiguredClient.GetProviderSchema()
|
||||
}
|
||||
|
||||
func (o *offlineProvider) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
return o.unconfiguredClient.GetResourceIdentitySchemas()
|
||||
}
|
||||
|
||||
func (o *offlineProvider) ValidateProviderConfig(request providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(tfdiags.AttributeValue(
|
||||
|
|
@ -99,6 +103,19 @@ func (o *offlineProvider) UpgradeResourceState(request providers.UpgradeResource
|
|||
}
|
||||
}
|
||||
|
||||
func (o *offlineProvider) UpgradeResourceIdentity(request providers.UpgradeResourceIdentityRequest) providers.UpgradeResourceIdentityResponse {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(tfdiags.AttributeValue(
|
||||
tfdiags.Error,
|
||||
"Called UpgradeResourceIdentity on an unconfigured provider",
|
||||
"Cannot upgrade the state of this resource because this provider is not configured. This is a bug in Terraform - please report it.",
|
||||
nil, // nil attribute path means the overall configuration block
|
||||
))
|
||||
return providers.UpgradeResourceIdentityResponse{
|
||||
Diagnostics: diags,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *offlineProvider) ConfigureProvider(request providers.ConfigureProviderRequest) providers.ConfigureProviderResponse {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(tfdiags.AttributeValue(
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ func (u *unknownProvider) GetProviderSchema() providers.GetProviderSchemaRespons
|
|||
return u.unconfiguredClient.GetProviderSchema()
|
||||
}
|
||||
|
||||
func (u *unknownProvider) GetResourceIdentitySchemas() providers.GetResourceIdentitySchemasResponse {
|
||||
return u.unconfiguredClient.GetResourceIdentitySchemas()
|
||||
}
|
||||
|
||||
func (u *unknownProvider) ValidateProviderConfig(request providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
|
||||
// This is offline functionality, so we can hand it off to the unconfigured
|
||||
// client.
|
||||
|
|
@ -70,6 +74,12 @@ func (u *unknownProvider) UpgradeResourceState(request providers.UpgradeResource
|
|||
return u.unconfiguredClient.UpgradeResourceState(request)
|
||||
}
|
||||
|
||||
func (u *unknownProvider) UpgradeResourceIdentity(request providers.UpgradeResourceIdentityRequest) providers.UpgradeResourceIdentityResponse {
|
||||
// This is offline functionality, so we can hand it off to the unconfigured
|
||||
// client.
|
||||
return u.unconfiguredClient.UpgradeResourceIdentity(request)
|
||||
}
|
||||
|
||||
func (u *unknownProvider) ConfigureProvider(request providers.ConfigureProviderRequest) providers.ConfigureProviderResponse {
|
||||
// This shouldn't be called, we don't configure an unknown provider within
|
||||
// stacks and Terraform Core shouldn't call this method.
|
||||
|
|
|
|||
|
|
@ -1081,12 +1081,24 @@ func TestPlanWithSingleResource(t *testing.T) {
|
|||
// type from the real terraform.io/builtin/terraform provider
|
||||
// maintained elsewhere in this codebase. If that schema changes
|
||||
// in future then this should change to match it.
|
||||
Schema: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"input": {Type: cty.DynamicPseudoType, Optional: true},
|
||||
"output": {Type: cty.DynamicPseudoType, Computed: true},
|
||||
"triggers_replace": {Type: cty.DynamicPseudoType, Optional: true},
|
||||
"id": {Type: cty.String, Computed: true},
|
||||
Schema: providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"input": {Type: cty.DynamicPseudoType, Optional: true},
|
||||
"output": {Type: cty.DynamicPseudoType, Computed: true},
|
||||
"triggers_replace": {Type: cty.DynamicPseudoType, Optional: true},
|
||||
"id": {Type: cty.String, Computed: true},
|
||||
},
|
||||
},
|
||||
Identity: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Description: "The unique identifier for the data store.",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -1843,7 +1855,7 @@ func TestPlanWithSensitivePropagation(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.StringVal("secret"),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
AfterSensitivePaths: []cty.Path{
|
||||
cty.GetAttrPath("value"),
|
||||
},
|
||||
|
|
@ -2006,7 +2018,7 @@ func TestPlanWithSensitivePropagationNested(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.StringVal("secret"),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
AfterSensitivePaths: []cty.Path{
|
||||
cty.GetAttrPath("value"),
|
||||
},
|
||||
|
|
@ -2324,7 +2336,7 @@ func TestPlanWithCheckableObjects(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("test"),
|
||||
"value": cty.StringVal("bar"),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -2458,7 +2470,7 @@ func TestPlanWithDeferredResource(t *testing.T) {
|
|||
"id": cty.StringVal("62594ae3"),
|
||||
"value": cty.NullVal(cty.String),
|
||||
"deferred": cty.BoolVal(true),
|
||||
}), stacks_testing_provider.DeferredResourceSchema),
|
||||
}), stacks_testing_provider.DeferredResourceSchema.Body),
|
||||
AfterSensitivePaths: nil,
|
||||
},
|
||||
},
|
||||
|
|
@ -2623,7 +2635,7 @@ func TestPlanWithDeferredComponentForEach(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.UnknownVal(cty.String),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
AfterSensitivePaths: nil,
|
||||
},
|
||||
},
|
||||
|
|
@ -2704,7 +2716,7 @@ func TestPlanWithDeferredComponentForEach(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.UnknownVal(cty.String),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
AfterSensitivePaths: nil,
|
||||
},
|
||||
},
|
||||
|
|
@ -2865,7 +2877,7 @@ func TestPlanWithDeferredComponentReferences(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.UnknownVal(cty.String),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
AfterSensitivePaths: nil,
|
||||
},
|
||||
},
|
||||
|
|
@ -2950,7 +2962,7 @@ func TestPlanWithDeferredComponentReferences(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.StringVal("known"),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
},
|
||||
ProviderAddr: addrs.AbsProviderConfig{
|
||||
Module: addrs.RootModule,
|
||||
|
|
@ -3116,7 +3128,7 @@ func TestPlanWithDeferredEmbeddedStackForEach(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.UnknownVal(cty.String),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
AfterSensitivePaths: nil,
|
||||
},
|
||||
},
|
||||
|
|
@ -3266,7 +3278,7 @@ func TestPlanWithDeferredEmbeddedStackAndComponentForEach(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.UnknownVal(cty.String),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
AfterSensitivePaths: nil,
|
||||
},
|
||||
},
|
||||
|
|
@ -3465,7 +3477,7 @@ func TestPlanWithDeferredProviderForEach(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.StringVal("primary"),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
},
|
||||
},
|
||||
Schema: stacks_testing_provider.TestingResourceSchema,
|
||||
|
|
@ -3537,7 +3549,7 @@ func TestPlanWithDeferredProviderForEach(t *testing.T) {
|
|||
After: mustPlanDynamicValueSchema(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.UnknownVal(cty.String),
|
||||
"value": cty.StringVal("secondary"),
|
||||
}), stacks_testing_provider.TestingResourceSchema),
|
||||
}), stacks_testing_provider.TestingResourceSchema.Body),
|
||||
},
|
||||
},
|
||||
Schema: stacks_testing_provider.TestingResourceSchema,
|
||||
|
|
|
|||
|
|
@ -19,42 +19,52 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
TestingResourceSchema = &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Optional: true, Computed: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
TestingResourceSchema = providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Optional: true, Computed: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
DeferredResourceSchema = &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Optional: true, Computed: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
"deferred": {Type: cty.Bool, Required: true},
|
||||
DeferredResourceSchema = providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Optional: true, Computed: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
"deferred": {Type: cty.Bool, Required: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
FailedResourceSchema = &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Optional: true, Computed: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
"fail_plan": {Type: cty.Bool, Optional: true, Computed: true},
|
||||
"fail_apply": {Type: cty.Bool, Optional: true, Computed: true},
|
||||
FailedResourceSchema = providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Optional: true, Computed: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
"fail_plan": {Type: cty.Bool, Optional: true, Computed: true},
|
||||
"fail_apply": {Type: cty.Bool, Optional: true, Computed: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
BlockedResourceSchema = &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Optional: true, Computed: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
"required_resources": {Type: cty.Set(cty.String), Optional: true},
|
||||
BlockedResourceSchema = providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Optional: true, Computed: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
"required_resources": {Type: cty.Set(cty.String), Optional: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
TestingDataSourceSchema = &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Required: true},
|
||||
"value": {Type: cty.String, Computed: true},
|
||||
TestingDataSourceSchema = providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Required: true},
|
||||
"value": {Type: cty.String, Computed: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
@ -127,21 +137,21 @@ func NewProviderWithData(t *testing.T, store *ResourceStore) *MockProvider {
|
|||
},
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"testing_resource": {
|
||||
Body: TestingResourceSchema,
|
||||
Body: TestingResourceSchema.Body,
|
||||
},
|
||||
"testing_deferred_resource": {
|
||||
Body: DeferredResourceSchema,
|
||||
Body: DeferredResourceSchema.Body,
|
||||
},
|
||||
"testing_failed_resource": {
|
||||
Body: FailedResourceSchema,
|
||||
Body: FailedResourceSchema.Body,
|
||||
},
|
||||
"testing_blocked_resource": {
|
||||
Body: BlockedResourceSchema,
|
||||
Body: BlockedResourceSchema.Body,
|
||||
},
|
||||
},
|
||||
DataSources: map[string]providers.Schema{
|
||||
"testing_data_source": {
|
||||
Body: TestingDataSourceSchema,
|
||||
Body: TestingDataSourceSchema.Body,
|
||||
},
|
||||
},
|
||||
Functions: map[string]providers.FunctionDecl{
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func (t *testingResource) Read(request providers.ReadResourceRequest, store *Res
|
|||
var exists bool
|
||||
response.NewState, exists = store.Get(id)
|
||||
if !exists {
|
||||
response.NewState = cty.NullVal(TestingResourceSchema.ImpliedType())
|
||||
response.NewState = cty.NullVal(TestingResourceSchema.Body.ImpliedType())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -110,7 +110,7 @@ func (d *deferredResource) Read(request providers.ReadResourceRequest, store *Re
|
|||
var exists bool
|
||||
response.NewState, exists = store.Get(id)
|
||||
if !exists {
|
||||
response.NewState = cty.NullVal(DeferredResourceSchema.ImpliedType())
|
||||
response.NewState = cty.NullVal(DeferredResourceSchema.Body.ImpliedType())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -173,7 +173,7 @@ func (f *failedResource) Read(request providers.ReadResourceRequest, store *Reso
|
|||
var exists bool
|
||||
response.NewState, exists = store.Get(id)
|
||||
if !exists {
|
||||
response.NewState = cty.NullVal(FailedResourceSchema.ImpliedType())
|
||||
response.NewState = cty.NullVal(FailedResourceSchema.Body.ImpliedType())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -253,7 +253,7 @@ func (b *blockedResource) Read(request providers.ReadResourceRequest, store *Res
|
|||
var exists bool
|
||||
response.NewState, exists = store.Get(id)
|
||||
if !exists {
|
||||
response.NewState = cty.NullVal(DeferredResourceSchema.ImpliedType())
|
||||
response.NewState = cty.NullVal(DeferredResourceSchema.Body.ImpliedType())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/collections"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/rpcapi/terraform1/stacks"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackstate/statekeys"
|
||||
|
|
@ -59,9 +59,9 @@ type AppliedChangeResourceInstanceObject struct {
|
|||
PreviousResourceInstanceObjectAddr *stackaddrs.AbsResourceInstanceObject
|
||||
|
||||
// Schema MUST be the same schema that was used to encode the dynamic
|
||||
// values inside NewStateSrc. This can be left as nil if NewStateSrc
|
||||
// values inside NewStateSrc. This can be left as empty if NewStateSrc
|
||||
// is nil, which represents that the object has been deleted.
|
||||
Schema *configschema.Block
|
||||
Schema providers.Schema
|
||||
}
|
||||
|
||||
var _ AppliedChange = (*AppliedChangeResourceInstanceObject)(nil)
|
||||
|
|
@ -147,8 +147,7 @@ func (ac *AppliedChangeResourceInstanceObject) protosForObject() ([]*stacks.Appl
|
|||
// exclusively uses MessagePack encoding for dynamic
|
||||
// values, and so we will need to use the ac.Schema to
|
||||
// transcode the data.
|
||||
ty := ac.Schema.ImpliedType()
|
||||
obj, err := objSrc.Decode(ty)
|
||||
obj, err := objSrc.Decode(ac.Schema)
|
||||
if err != nil {
|
||||
// It would be _very_ strange to get here because we should just
|
||||
// be reversing the same encoding operation done earlier to
|
||||
|
|
@ -156,7 +155,7 @@ func (ac *AppliedChangeResourceInstanceObject) protosForObject() ([]*stacks.Appl
|
|||
return nil, nil, fmt.Errorf("cannot decode new state for %s in preparation for saving it: %w", addr, err)
|
||||
}
|
||||
|
||||
protoValue, err := stacks.ToDynamicValue(obj.Value, ty)
|
||||
protoValue, err := stacks.ToDynamicValue(obj.Value, ac.Schema.Body.ImpliedType())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot encode new state for %s in preparation for saving it: %w", addr, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/plans/planproto"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/rpcapi/terraform1/stacks"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
||||
"github.com/hashicorp/terraform/internal/stacks/tfstackdata1"
|
||||
|
|
@ -52,15 +53,17 @@ func TestAppliedChangeAsProto(t *testing.T) {
|
|||
Module: addrs.RootModule,
|
||||
Provider: addrs.MustParseProviderSourceString("example.com/thingers/thingy"),
|
||||
},
|
||||
Schema: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"secret": {
|
||||
Type: cty.String,
|
||||
Sensitive: true,
|
||||
Schema: providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"secret": {
|
||||
Type: cty.String,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -160,15 +163,17 @@ func TestAppliedChangeAsProto(t *testing.T) {
|
|||
Module: addrs.RootModule,
|
||||
Provider: addrs.MustParseProviderSourceString("example.com/thingers/thingy"),
|
||||
},
|
||||
Schema: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"secret": {
|
||||
Type: cty.String,
|
||||
Sensitive: true,
|
||||
Schema: providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"secret": {
|
||||
Type: cty.String,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import (
|
|||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackplan"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
|
|
@ -25,7 +25,7 @@ type StateProducer interface {
|
|||
Addr() stackaddrs.AbsComponentInstance
|
||||
|
||||
// ResourceSchema returns the schema for a resource type from a provider.
|
||||
ResourceSchema(ctx context.Context, providerTypeAddr addrs.Provider, mode addrs.ResourceMode, resourceType string) (*configschema.Block, error)
|
||||
ResourceSchema(ctx context.Context, providerTypeAddr addrs.Provider, mode addrs.ResourceMode, resourceType string) (providers.Schema, error)
|
||||
}
|
||||
|
||||
func FromState(ctx context.Context, state *states.State, plan *stackplan.Component, applyTimeInputs cty.Value, affectedResources addrs.Set[addrs.AbsResourceInstanceObject], producer StateProducer) ([]AppliedChange, tfdiags.Diagnostics) {
|
||||
|
|
@ -37,7 +37,7 @@ func FromState(ctx context.Context, state *states.State, plan *stackplan.Compone
|
|||
for _, rioAddr := range affectedResources {
|
||||
os := state.ResourceInstanceObjectSrc(rioAddr)
|
||||
var providerConfigAddr addrs.AbsProviderConfig
|
||||
var schema *configschema.Block
|
||||
var schema providers.Schema
|
||||
if os != nil {
|
||||
rAddr := rioAddr.ResourceInstance.ContainingResource()
|
||||
rs := state.Resource(rAddr)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/lang/format"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
)
|
||||
|
||||
// ResourceInstanceObject is the local representation of a specific remote
|
||||
|
|
@ -27,6 +28,10 @@ type ResourceInstanceObject struct {
|
|||
// Terraform.
|
||||
Value cty.Value
|
||||
|
||||
// Identity is the object-typed value representing the identity of the remote
|
||||
// object within Terraform.
|
||||
Identity cty.Value
|
||||
|
||||
// Private is an opaque value set by the provider when this object was
|
||||
// last created or updated. Terraform Core does not use this value in
|
||||
// any way and it is not exposed anywhere in the user interface, so
|
||||
|
|
@ -52,6 +57,24 @@ type ResourceInstanceObject struct {
|
|||
CreateBeforeDestroy bool
|
||||
}
|
||||
|
||||
// NewResourceInstanceObjectFromIR converts the receiving
|
||||
// ImportedResource into a ResourceInstanceObject that has status ObjectReady.
|
||||
//
|
||||
// The returned object does not know its own resource type, so the caller must
|
||||
// retain the ResourceType value from the source object if this information is
|
||||
// needed.
|
||||
//
|
||||
// The returned object also has no dependency addresses, but the caller may
|
||||
// freely modify the direct fields of the returned object without affecting
|
||||
// the receiver.
|
||||
func NewResourceInstanceObjectFromIR(ir providers.ImportedResource) *ResourceInstanceObject {
|
||||
return &ResourceInstanceObject{
|
||||
Status: ObjectReady,
|
||||
Value: ir.State,
|
||||
Private: ir.Private,
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectStatus represents the status of a RemoteObject.
|
||||
type ObjectStatus rune
|
||||
|
||||
|
|
@ -80,21 +103,20 @@ const (
|
|||
ObjectPlanned ObjectStatus = 'P'
|
||||
)
|
||||
|
||||
// Encode marshals the value within the receiver to produce a
|
||||
// Encode marshals values within the receiver to produce a
|
||||
// ResourceInstanceObjectSrc ready to be written to a state file.
|
||||
//
|
||||
// The given type must be the implied type of the resource type schema, and
|
||||
// the given value must conform to it. It is important to pass the schema
|
||||
// type and not the object's own type so that dynamically-typed attributes
|
||||
// will be stored correctly. The caller must also provide the version number
|
||||
// of the schema that the given type was derived from, which will be recorded
|
||||
// in the source object so it can be used to detect when schema migration is
|
||||
// required on read.
|
||||
// The schema must contain the resource type body, and the given value must
|
||||
// conform its implied type. The schema must also contain the version number
|
||||
// of the schema, which will be recorded in the source object so it can be
|
||||
// used to detect when schema migration is required on read.
|
||||
// The schema may also contain an resource identity schema and version number,
|
||||
// which will be used to encode the resource identity.
|
||||
//
|
||||
// The returned object may share internal references with the receiver and
|
||||
// so the caller must not mutate the receiver any further once once this
|
||||
// method is called.
|
||||
func (o *ResourceInstanceObject) Encode(ty cty.Type, schemaVersion uint64) (*ResourceInstanceObjectSrc, error) {
|
||||
func (o *ResourceInstanceObject) Encode(schema providers.Schema) (*ResourceInstanceObjectSrc, error) {
|
||||
// If it contains marks, remove these marks before traversing the
|
||||
// structure with UnknownAsNull, and save the PathValueMarks
|
||||
// so we can save them in state.
|
||||
|
|
@ -114,11 +136,19 @@ func (o *ResourceInstanceObject) Encode(ty cty.Type, schemaVersion uint64) (*Res
|
|||
// and raise an error about that.
|
||||
val = cty.UnknownAsNull(val)
|
||||
|
||||
src, err := ctyjson.Marshal(val, ty)
|
||||
src, err := ctyjson.Marshal(val, schema.Body.ImpliedType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var idJSON []byte
|
||||
if !o.Identity.IsNull() && schema.Identity != nil {
|
||||
idJSON, err = ctyjson.Marshal(o.Identity, schema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Dependencies are collected and merged in an unordered format (using map
|
||||
// keys as a set), then later changed to a slice (in random ordering) to be
|
||||
// stored in state as an array. To avoid pointless thrashing of state in
|
||||
|
|
@ -132,15 +162,18 @@ func (o *ResourceInstanceObject) Encode(ty cty.Type, schemaVersion uint64) (*Res
|
|||
sort.Slice(dependencies, func(i, j int) bool { return dependencies[i].String() < dependencies[j].String() })
|
||||
|
||||
return &ResourceInstanceObjectSrc{
|
||||
SchemaVersion: schemaVersion,
|
||||
AttrsJSON: src,
|
||||
AttrSensitivePaths: sensitivePaths,
|
||||
Private: o.Private,
|
||||
Status: o.Status,
|
||||
Dependencies: dependencies,
|
||||
CreateBeforeDestroy: o.CreateBeforeDestroy,
|
||||
SchemaVersion: uint64(schema.Version),
|
||||
AttrsJSON: src,
|
||||
AttrSensitivePaths: sensitivePaths,
|
||||
Private: o.Private,
|
||||
Status: o.Status,
|
||||
Dependencies: dependencies,
|
||||
CreateBeforeDestroy: o.CreateBeforeDestroy,
|
||||
IdentityJSON: idJSON,
|
||||
IdentitySchemaVersion: uint64(schema.IdentityVersion),
|
||||
// The cached value must have all its marks since it bypasses decoding.
|
||||
decodeValueCache: o.Value,
|
||||
decodeValueCache: o.Value,
|
||||
decodeIdentityCache: o.Identity,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@
|
|||
package states
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/hcl2shim"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
)
|
||||
|
||||
// ResourceInstanceObjectSrc is a not-fully-decoded version of
|
||||
|
|
@ -40,6 +43,10 @@ type ResourceInstanceObjectSrc struct {
|
|||
// schema version should be recorded in the SchemaVersion field.
|
||||
AttrsJSON []byte
|
||||
|
||||
IdentitySchemaVersion uint64
|
||||
|
||||
IdentityJSON []byte
|
||||
|
||||
// AttrsFlat is a legacy form of attributes used in older state file
|
||||
// formats, and in the new state format for objects that haven't yet been
|
||||
// upgraded. This attribute is mutually exclusive with Attrs: for any
|
||||
|
|
@ -66,21 +73,27 @@ type ResourceInstanceObjectSrc struct {
|
|||
|
||||
// decodeValueCache stored the decoded value for repeated decodings.
|
||||
decodeValueCache cty.Value
|
||||
// decodeIdentityCache stored the decoded identity for repeated decodings.
|
||||
decodeIdentityCache cty.Value
|
||||
}
|
||||
|
||||
// Decode unmarshals the raw representation of the object attributes. Pass the
|
||||
// implied type of the corresponding resource type schema for correct operation.
|
||||
// schema of the corresponding resource type for correct operation.
|
||||
//
|
||||
// Before calling Decode, the caller must check that the SchemaVersion field
|
||||
// exactly equals the version number of the schema whose implied type is being
|
||||
// passed, or else the result is undefined.
|
||||
//
|
||||
// If the object has an identity, the schema must also contain a resource
|
||||
// identity schema for the identity to be decoded.
|
||||
//
|
||||
// The returned object may share internal references with the receiver and
|
||||
// so the caller must not mutate the receiver any further once once this
|
||||
// method is called.
|
||||
func (os *ResourceInstanceObjectSrc) Decode(ty cty.Type) (*ResourceInstanceObject, error) {
|
||||
func (os *ResourceInstanceObjectSrc) Decode(schema providers.Schema) (*ResourceInstanceObject, error) {
|
||||
var val cty.Value
|
||||
var err error
|
||||
attrsTy := schema.Body.ImpliedType()
|
||||
|
||||
switch {
|
||||
case os.decodeValueCache != cty.NilVal:
|
||||
|
|
@ -88,21 +101,32 @@ func (os *ResourceInstanceObjectSrc) Decode(ty cty.Type) (*ResourceInstanceObjec
|
|||
|
||||
case os.AttrsFlat != nil:
|
||||
// Legacy mode. We'll do our best to unpick this from the flatmap.
|
||||
val, err = hcl2shim.HCL2ValueFromFlatmap(os.AttrsFlat, ty)
|
||||
val, err = hcl2shim.HCL2ValueFromFlatmap(os.AttrsFlat, attrsTy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
val, err = ctyjson.Unmarshal(os.AttrsJSON, ty)
|
||||
val, err = ctyjson.Unmarshal(os.AttrsJSON, attrsTy)
|
||||
val = marks.MarkPaths(val, marks.Sensitive, os.AttrSensitivePaths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var identity cty.Value
|
||||
if os.decodeIdentityCache != cty.NilVal {
|
||||
identity = os.decodeIdentityCache
|
||||
} 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 &ResourceInstanceObject{
|
||||
Value: val,
|
||||
Identity: identity,
|
||||
Status: os.Status,
|
||||
Dependencies: os.Dependencies,
|
||||
Private: os.Private,
|
||||
|
|
@ -131,3 +155,16 @@ func (os *ResourceInstanceObjectSrc) CompleteUpgrade(newAttrs cty.Value, newType
|
|||
new.SchemaVersion = newSchemaVersion
|
||||
return new, nil
|
||||
}
|
||||
|
||||
func (os *ResourceInstanceObjectSrc) CompleteIdentityUpgrade(newAttrs cty.Value, schema providers.Schema) (*ResourceInstanceObjectSrc, error) {
|
||||
new := os.DeepCopy()
|
||||
|
||||
src, err := ctyjson.Marshal(newAttrs, schema.Identity.ImpliedType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
new.IdentityJSON = src
|
||||
new.IdentitySchemaVersion = uint64(schema.IdentityVersion)
|
||||
return new, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import (
|
|||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
|
@ -23,6 +25,27 @@ func TestResourceInstanceObject_encode(t *testing.T) {
|
|||
"sensitive_a": cty.StringVal("secret").Mark(marks.Sensitive),
|
||||
"sensitive_b": cty.StringVal("secret").Mark(marks.Sensitive),
|
||||
})
|
||||
schema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"foo": {
|
||||
Type: cty.Bool,
|
||||
},
|
||||
"obj": {
|
||||
Type: cty.Object(map[string]cty.Type{
|
||||
"sensitive": cty.String,
|
||||
}),
|
||||
},
|
||||
"sensitive_a": {
|
||||
Type: cty.String,
|
||||
},
|
||||
"sensitive_b": {
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
},
|
||||
Version: 0,
|
||||
}
|
||||
// The in-memory order of resource dependencies is random, since they're an
|
||||
// unordered set.
|
||||
depsOne := []addrs.ConfigResource{
|
||||
|
|
@ -72,7 +95,7 @@ func TestResourceInstanceObject_encode(t *testing.T) {
|
|||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
rios, err := obj.Encode(value.Type(), 0)
|
||||
rios, err := obj.Encode(schema)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
|
@ -108,12 +131,22 @@ func TestResourceInstanceObject_encodeInvalidMarks(t *testing.T) {
|
|||
// value in the state.
|
||||
"foo": cty.True.Mark("unsupported"),
|
||||
})
|
||||
schema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"foo": {
|
||||
Type: cty.Bool,
|
||||
},
|
||||
},
|
||||
},
|
||||
Version: 0,
|
||||
}
|
||||
|
||||
obj := &ResourceInstanceObject{
|
||||
Value: value,
|
||||
Status: ObjectReady,
|
||||
}
|
||||
_, err := obj.Encode(value.Type(), 0)
|
||||
_, err := obj.Encode(schema)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected success; want error")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,8 +129,9 @@ func TestStatePersist(t *testing.T) {
|
|||
"attributes_flat": map[string]interface{}{
|
||||
"filename": "file.txt",
|
||||
},
|
||||
"schema_version": 0.0,
|
||||
"sensitive_attributes": []interface{}{},
|
||||
"identity_schema_version": 0.0,
|
||||
"schema_version": 0.0,
|
||||
"sensitive_attributes": []interface{}{},
|
||||
},
|
||||
},
|
||||
"mode": "managed",
|
||||
|
|
@ -167,8 +168,9 @@ func TestStatePersist(t *testing.T) {
|
|||
"attributes_flat": map[string]interface{}{
|
||||
"filename": "file.txt",
|
||||
},
|
||||
"schema_version": 0.0,
|
||||
"sensitive_attributes": []interface{}{},
|
||||
"identity_schema_version": 0.0,
|
||||
"schema_version": 0.0,
|
||||
"sensitive_attributes": []interface{}{},
|
||||
},
|
||||
},
|
||||
"mode": "managed",
|
||||
|
|
|
|||
|
|
@ -142,6 +142,12 @@ func (os *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc {
|
|||
copy(attrsJSON, os.AttrsJSON)
|
||||
}
|
||||
|
||||
var identityJSON []byte
|
||||
if os.IdentityJSON != nil {
|
||||
identityJSON = make([]byte, len(os.IdentityJSON))
|
||||
copy(identityJSON, os.IdentityJSON)
|
||||
}
|
||||
|
||||
var sensitiveAttrPaths []cty.Path
|
||||
if os.AttrSensitivePaths != nil {
|
||||
sensitiveAttrPaths = make([]cty.Path, len(os.AttrSensitivePaths))
|
||||
|
|
@ -163,15 +169,18 @@ func (os *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc {
|
|||
}
|
||||
|
||||
return &ResourceInstanceObjectSrc{
|
||||
Status: os.Status,
|
||||
SchemaVersion: os.SchemaVersion,
|
||||
Private: private,
|
||||
AttrsFlat: attrsFlat,
|
||||
AttrsJSON: attrsJSON,
|
||||
AttrSensitivePaths: sensitiveAttrPaths,
|
||||
Dependencies: dependencies,
|
||||
CreateBeforeDestroy: os.CreateBeforeDestroy,
|
||||
decodeValueCache: os.decodeValueCache,
|
||||
Status: os.Status,
|
||||
SchemaVersion: os.SchemaVersion,
|
||||
Private: private,
|
||||
AttrsFlat: attrsFlat,
|
||||
AttrsJSON: attrsJSON,
|
||||
AttrSensitivePaths: sensitiveAttrPaths,
|
||||
Dependencies: dependencies,
|
||||
CreateBeforeDestroy: os.CreateBeforeDestroy,
|
||||
decodeValueCache: os.decodeValueCache,
|
||||
IdentityJSON: identityJSON,
|
||||
IdentitySchemaVersion: os.IdentitySchemaVersion,
|
||||
decodeIdentityCache: os.decodeIdentityCache,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -494,6 +494,8 @@ func appendInstanceObjectStateV4(rs *states.Resource, is *states.ResourceInstanc
|
|||
PrivateRaw: privateRaw,
|
||||
Dependencies: deps,
|
||||
CreateBeforeDestroy: obj.CreateBeforeDestroy,
|
||||
IdentitySchemaVersion: obj.IdentitySchemaVersion,
|
||||
IdentityRaw: obj.IdentityJSON,
|
||||
}), diags
|
||||
}
|
||||
|
||||
|
|
@ -702,6 +704,9 @@ type instanceObjectStateV4 struct {
|
|||
AttributesFlat map[string]string `json:"attributes_flat,omitempty"`
|
||||
AttributeSensitivePaths json.RawMessage `json:"sensitive_attributes,omitempty"`
|
||||
|
||||
IdentitySchemaVersion uint64 `json:"identity_schema_version"`
|
||||
IdentityRaw json.RawMessage `json:"identity,omitempty"`
|
||||
|
||||
PrivateRaw []byte `json:"private,omitempty"`
|
||||
|
||||
Dependencies []string `json:"dependencies,omitempty"`
|
||||
|
|
|
|||
|
|
@ -841,10 +841,20 @@ resource "test_resource" "c" {
|
|||
for name, attrs := range wantResourceAttrs {
|
||||
addr := mustResourceInstanceAddr(fmt.Sprintf("test_resource.%s", name))
|
||||
r := state.ResourceInstance(addr)
|
||||
rd, err := r.Current.Decode(cty.Object(map[string]cty.Type{
|
||||
"value": cty.String,
|
||||
"output": cty.String,
|
||||
}))
|
||||
schema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"value": {
|
||||
Type: cty.String,
|
||||
},
|
||||
"output": {
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
},
|
||||
Version: 0,
|
||||
}
|
||||
rd, err := r.Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatalf("error decoding test_resource.a: %s", err)
|
||||
}
|
||||
|
|
@ -902,10 +912,20 @@ resource "test_resource" "c" {
|
|||
for name, attrs := range wantResourceAttrs {
|
||||
addr := mustResourceInstanceAddr(fmt.Sprintf("test_resource.%s", name))
|
||||
r := state.ResourceInstance(addr)
|
||||
rd, err := r.Current.Decode(cty.Object(map[string]cty.Type{
|
||||
"value": cty.String,
|
||||
"output": cty.String,
|
||||
}))
|
||||
schema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"value": {
|
||||
Type: cty.String,
|
||||
},
|
||||
"output": {
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
},
|
||||
Version: 0,
|
||||
}
|
||||
rd, err := r.Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatalf("error decoding test_resource.a: %s", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -377,24 +377,26 @@ resource "ephem_write_only" "wo" {
|
|||
`,
|
||||
})
|
||||
|
||||
schema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"normal": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"write_only": {
|
||||
Type: cty.String,
|
||||
WriteOnly: true,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Version: 0,
|
||||
}
|
||||
ephem := &testing_provider.MockProvider{
|
||||
GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"ephem_write_only": {
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"normal": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"write_only": {
|
||||
Type: cty.String,
|
||||
WriteOnly: true,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ephem_write_only": schema,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -457,10 +459,7 @@ resource "ephem_write_only" "wo" {
|
|||
t.Fatalf("Resource instance not found")
|
||||
}
|
||||
|
||||
attrs, err := resourceInstance.Current.Decode(cty.Object(map[string]cty.Type{
|
||||
"normal": cty.String,
|
||||
"write_only": cty.String,
|
||||
}))
|
||||
attrs, err := resourceInstance.Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode attributes: %v", err)
|
||||
}
|
||||
|
|
@ -489,24 +488,25 @@ resource "ephem_write_only" "wo" {
|
|||
`,
|
||||
})
|
||||
|
||||
schema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"normal": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"write_only": {
|
||||
Type: cty.String,
|
||||
WriteOnly: true,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
ephem := &testing_provider.MockProvider{
|
||||
GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"ephem_write_only": {
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"normal": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"write_only": {
|
||||
Type: cty.String,
|
||||
WriteOnly: true,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ephem_write_only": schema,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -585,10 +585,7 @@ resource "ephem_write_only" "wo" {
|
|||
t.Fatalf("Resource instance not found")
|
||||
}
|
||||
|
||||
attrs, err := resourceInstance.Current.Decode(cty.Object(map[string]cty.Type{
|
||||
"normal": cty.String,
|
||||
"write_only": cty.String,
|
||||
}))
|
||||
attrs, err := resourceInstance.Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode attributes: %v", err)
|
||||
}
|
||||
|
|
@ -617,24 +614,25 @@ resource "ephem_write_only" "wo" {
|
|||
`,
|
||||
})
|
||||
|
||||
schema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"normal": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"write_only": {
|
||||
Type: cty.String,
|
||||
WriteOnly: true,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
ephem := &testing_provider.MockProvider{
|
||||
GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"ephem_write_only": {
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"normal": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"write_only": {
|
||||
Type: cty.String,
|
||||
WriteOnly: true,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ephem_write_only": schema,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -724,10 +722,7 @@ resource "ephem_write_only" "wo" {
|
|||
t.Fatalf("Resource instance not found")
|
||||
}
|
||||
|
||||
attrs, err := resourceInstance.Current.Decode(cty.Object(map[string]cty.Type{
|
||||
"normal": cty.String,
|
||||
"write_only": cty.String,
|
||||
}))
|
||||
attrs, err := resourceInstance.Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode attributes: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -273,9 +273,9 @@ func TestContext2Apply_unstable(t *testing.T) {
|
|||
Type: "test_resource",
|
||||
Name: "foo",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"].Body
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"]
|
||||
rds := plan.Changes.ResourceInstance(addr)
|
||||
rd, err := rds.Decode(schema.ImpliedType())
|
||||
rd, err := rds.Decode(schema.Body.ImpliedType())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ func TestContext2Apply_unstable(t *testing.T) {
|
|||
t.Fatalf("wrong number of resources %d; want 1", len(mod.Resources))
|
||||
}
|
||||
|
||||
rs, err := rss.Current.Decode(schema.ImpliedType())
|
||||
rs, err := rss.Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatalf("decode error: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -864,12 +864,12 @@ func (c *Context) deferredResources(config *configs.Config, deferrals []*plans.D
|
|||
|
||||
for _, deferral := range deferrals {
|
||||
|
||||
schema, _ := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
deferral.Change.ProviderAddr.Provider,
|
||||
deferral.Change.Addr.Resource.Resource.Mode,
|
||||
deferral.Change.Addr.Resource.Resource.Type)
|
||||
|
||||
ty := schema.ImpliedType()
|
||||
ty := schema.Body.ImpliedType()
|
||||
deferralSrc, err := deferral.Encode(ty)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
|
|
@ -1001,12 +1001,12 @@ func (c *Context) driftedResources(config *configs.Config, oldState, newState *s
|
|||
|
||||
newIS := newState.ResourceInstance(addr)
|
||||
|
||||
schema, _ := schemas.ResourceTypeConfig(
|
||||
schema := schemas.ResourceTypeConfig(
|
||||
provider,
|
||||
addr.Resource.Resource.Mode,
|
||||
addr.Resource.Resource.Type,
|
||||
)
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Warning,
|
||||
"Missing resource schema from provider",
|
||||
|
|
@ -1014,9 +1014,8 @@ func (c *Context) driftedResources(config *configs.Config, oldState, newState *s
|
|||
))
|
||||
continue
|
||||
}
|
||||
ty := schema.ImpliedType()
|
||||
|
||||
oldObj, err := oldIS.Current.Decode(ty)
|
||||
oldObj, err := oldIS.Current.Decode(schema)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Warning,
|
||||
|
|
@ -1028,7 +1027,7 @@ func (c *Context) driftedResources(config *configs.Config, oldState, newState *s
|
|||
|
||||
var newObj *states.ResourceInstanceObject
|
||||
if newIS != nil && newIS.Current != nil {
|
||||
newObj, err = newIS.Current.Decode(ty)
|
||||
newObj, err = newIS.Current.Decode(schema)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Warning,
|
||||
|
|
@ -1039,6 +1038,7 @@ func (c *Context) driftedResources(config *configs.Config, oldState, newState *s
|
|||
}
|
||||
}
|
||||
|
||||
ty := schema.Body.ImpliedType()
|
||||
var oldVal, newVal cty.Value
|
||||
oldVal = oldObj.Value
|
||||
if newObj != nil {
|
||||
|
|
|
|||
427
internal/terraform/context_plan_identity_test.go
Normal file
427
internal/terraform/context_plan_identity_test.go
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/configs/hcl2shim"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
func TestContext2Plan_resource_identity_refresh(t *testing.T) {
|
||||
for name, tc := range map[string]struct {
|
||||
StoredIdentitySchemaVersion uint64
|
||||
StoredIdentityJSON []byte
|
||||
IdentitySchema providers.IdentitySchema
|
||||
IdentityData cty.Value
|
||||
ExpectedIdentity cty.Value
|
||||
ExpectedError error
|
||||
ExpectUpgradeResourceIdentityCalled bool
|
||||
UpgradeResourceIdentityResponse providers.UpgradeResourceIdentityResponse
|
||||
}{
|
||||
"no previous identity": {
|
||||
IdentitySchema: providers.IdentitySchema{
|
||||
Version: 0,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityData: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
ExpectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
},
|
||||
"identity version mismatch": {
|
||||
StoredIdentitySchemaVersion: 1,
|
||||
StoredIdentityJSON: []byte(`{"id": "foo"}`),
|
||||
IdentitySchema: providers.IdentitySchema{
|
||||
Version: 0,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityData: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
ExpectedError: fmt.Errorf("Resource instance managed by newer provider version: The current state of aws_instance.web was created by a newer provider version than is currently selected. Upgrade the aws provider to work with this state."),
|
||||
},
|
||||
"identity type mismatch": {
|
||||
StoredIdentitySchemaVersion: 0,
|
||||
StoredIdentityJSON: []byte(`{"arn": "foo"}`),
|
||||
IdentitySchema: providers.IdentitySchema{
|
||||
Version: 0,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityData: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
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"),
|
||||
},
|
||||
"identity upgrade succeeds": {
|
||||
StoredIdentitySchemaVersion: 1,
|
||||
StoredIdentityJSON: []byte(`{"arn": "foo"}`),
|
||||
IdentitySchema: providers.IdentitySchema{
|
||||
Version: 2,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityData: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
UpgradeResourceIdentityResponse: providers.UpgradeResourceIdentityResponse{
|
||||
UpgradedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
},
|
||||
ExpectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
ExpectUpgradeResourceIdentityCalled: true,
|
||||
},
|
||||
"identity upgrade failed": {
|
||||
StoredIdentitySchemaVersion: 1,
|
||||
StoredIdentityJSON: []byte(`{"id": "foo"}`),
|
||||
IdentitySchema: providers.IdentitySchema{
|
||||
Version: 2,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"arn": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityData: cty.ObjectVal(map[string]cty.Value{
|
||||
"arn": cty.StringVal("arn:foo"),
|
||||
}),
|
||||
UpgradeResourceIdentityResponse: providers.UpgradeResourceIdentityResponse{
|
||||
UpgradedIdentity: cty.NilVal,
|
||||
Diagnostics: tfdiags.Diagnostics{
|
||||
tfdiags.Sourceless(tfdiags.Error, "failed to upgrade resource identity", "provider was unable to do so"),
|
||||
},
|
||||
},
|
||||
ExpectedIdentity: cty.ObjectVal(map[string]cty.Value{
|
||||
"arn": cty.StringVal("arn:foo"),
|
||||
}),
|
||||
ExpectUpgradeResourceIdentityCalled: true,
|
||||
ExpectedError: fmt.Errorf("failed to upgrade resource identity: provider was unable to do so"),
|
||||
},
|
||||
"identity sent to provider differs from returned one": {
|
||||
StoredIdentitySchemaVersion: 0,
|
||||
StoredIdentityJSON: []byte(`{"id": "foo"}`),
|
||||
IdentitySchema: providers.IdentitySchema{
|
||||
Version: 0,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityData: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
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."),
|
||||
},
|
||||
"identity with unknowns": {
|
||||
IdentitySchema: providers.IdentitySchema{
|
||||
Version: 0,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
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."),
|
||||
},
|
||||
|
||||
"identity with marks": {
|
||||
IdentitySchema: providers.IdentitySchema{
|
||||
Version: 0,
|
||||
Body: &configschema.Object{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
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."),
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "refresh-basic")
|
||||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"aws_instance": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Computed: true,
|
||||
},
|
||||
"foo": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IdentityTypes: map[string]*configschema.Object{
|
||||
"aws_instance": tc.IdentitySchema.Body,
|
||||
},
|
||||
IdentityTypeSchemaVersions: map[string]uint64{
|
||||
"aws_instance": uint64(tc.IdentitySchema.Version),
|
||||
},
|
||||
})
|
||||
|
||||
state := states.NewState()
|
||||
root := state.EnsureModule(addrs.RootModuleInstance)
|
||||
|
||||
root.SetResourceInstanceCurrent(
|
||||
mustResourceInstanceAddr("aws_instance.web").Resource,
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"foo","foo":"bar"}`),
|
||||
IdentitySchemaVersion: tc.StoredIdentitySchemaVersion,
|
||||
IdentityJSON: tc.StoredIdentityJSON,
|
||||
},
|
||||
mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
|
||||
)
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
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.UpgradeResourceIdentityResponse = &tc.UpgradeResourceIdentityResponse
|
||||
|
||||
s, diags := ctx.Plan(m, state, &PlanOpts{Mode: plans.RefreshOnlyMode})
|
||||
|
||||
// TODO: maybe move to comparing diagnostics instead
|
||||
if tc.ExpectedError != nil {
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("expected error, got none")
|
||||
}
|
||||
if diags.Err().Error() != tc.ExpectedError.Error() {
|
||||
t.Fatalf("unexpected error\nwant: %v\ngot: %v", tc.ExpectedError, diags.Err())
|
||||
}
|
||||
|
||||
return
|
||||
} else {
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
}
|
||||
|
||||
if !p.ReadResourceCalled {
|
||||
t.Fatal("ReadResource should be called")
|
||||
}
|
||||
|
||||
if tc.ExpectUpgradeResourceIdentityCalled && !p.UpgradeResourceIdentityCalled {
|
||||
t.Fatal("UpgradeResourceIdentity should be called")
|
||||
}
|
||||
|
||||
mod := s.PriorState.RootModule()
|
||||
fromState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(schema)
|
||||
if err != nil {
|
||||
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())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This test validates if a resource identity that is deposed and will be destroyed
|
||||
// can be refreshed with an identity during the plan.
|
||||
func TestContext2Plan_resource_identity_refresh_destroy_deposed(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "refresh-basic")
|
||||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"aws_instance": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Computed: true,
|
||||
},
|
||||
"foo": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IdentityTypes: map[string]*configschema.Object{
|
||||
"aws_instance": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
IdentityTypeSchemaVersions: map[string]uint64{
|
||||
"aws_instance": 0,
|
||||
},
|
||||
})
|
||||
|
||||
state := states.NewState()
|
||||
root := state.EnsureModule(addrs.RootModuleInstance)
|
||||
|
||||
deposedKey := states.DeposedKey("00000001")
|
||||
root.SetResourceInstanceDeposed(
|
||||
mustResourceInstanceAddr("aws_instance.web").Resource,
|
||||
deposedKey,
|
||||
&states.ResourceInstanceObjectSrc{ // no identity recorded
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"foo","foo":"bar"}`),
|
||||
},
|
||||
mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
|
||||
)
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
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: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
}
|
||||
|
||||
s, diags := ctx.Plan(m, state, &PlanOpts{Mode: plans.RefreshOnlyMode})
|
||||
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
if !p.ReadResourceCalled {
|
||||
t.Fatal("ReadResource should be called")
|
||||
}
|
||||
|
||||
mod := s.PriorState.RootModule()
|
||||
fromState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Deposed[deposedKey].Decode(schema)
|
||||
if err != nil {
|
||||
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))
|
||||
}
|
||||
expectedIdentity := cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
})
|
||||
if expectedIdentity.Equals(fromState.Identity).False() {
|
||||
t.Fatalf("wrong identity\nwant: %s\ngot: %s", expectedIdentity.GoString(), fromState.Identity.GoString())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -43,8 +43,8 @@ func TestContext2Refresh(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Body
|
||||
ty := schema.ImpliedType()
|
||||
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)
|
||||
|
|
@ -64,12 +64,12 @@ func TestContext2Refresh(t *testing.T) {
|
|||
}
|
||||
|
||||
mod := s.RootModule()
|
||||
fromState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(ty)
|
||||
fromState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newState, err := schema.CoerceValue(fromState.Value)
|
||||
newState, err := schema.Body.CoerceValue(fromState.Value)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -130,9 +130,7 @@ func TestContext2Refresh_dynamicAttr(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["test_instance"].Body
|
||||
ty := schema.ImpliedType()
|
||||
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["test_instance"]
|
||||
s, diags := ctx.Refresh(m, startingState, &PlanOpts{Mode: plans.NormalMode})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
|
|
@ -143,7 +141,7 @@ func TestContext2Refresh_dynamicAttr(t *testing.T) {
|
|||
}
|
||||
|
||||
mod := s.RootModule()
|
||||
newState, err := mod.Resources["test_instance.foo"].Instances[addrs.NoKey].Current.Decode(ty)
|
||||
newState, err := mod.Resources["test_instance.foo"].Instances[addrs.NoKey].Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -807,10 +805,8 @@ func TestContext2Refresh_stateBasic(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Body
|
||||
ty := schema.ImpliedType()
|
||||
|
||||
readStateVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
||||
schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"]
|
||||
readStateVal, err := schema.Body.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}))
|
||||
if err != nil {
|
||||
|
|
@ -831,7 +827,7 @@ func TestContext2Refresh_stateBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
mod := s.RootModule()
|
||||
newState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(ty)
|
||||
newState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -889,11 +885,13 @@ func TestContext2Refresh_dataCount(t *testing.T) {
|
|||
func TestContext2Refresh_dataState(t *testing.T) {
|
||||
m := testModule(t, "refresh-data-resource-basic")
|
||||
state := states.NewState()
|
||||
schema := &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"inputs": {
|
||||
Type: cty.Map(cty.String),
|
||||
Optional: true,
|
||||
schema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"inputs": {
|
||||
Type: cty.Map(cty.String),
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -902,7 +900,7 @@ func TestContext2Refresh_dataState(t *testing.T) {
|
|||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
|
||||
Provider: &configschema.Block{},
|
||||
DataSources: map[string]*configschema.Block{
|
||||
"null_data_source": schema,
|
||||
"null_data_source": schema.Body,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -934,7 +932,7 @@ func TestContext2Refresh_dataState(t *testing.T) {
|
|||
|
||||
mod := s.RootModule()
|
||||
|
||||
newState, err := mod.Resources["data.null_data_source.testing"].Instances[addrs.NoKey].Current.Decode(schema.ImpliedType())
|
||||
newState, err := mod.Resources["data.null_data_source.testing"].Instances[addrs.NoKey].Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -1065,22 +1063,24 @@ func TestContext2Refresh_unknownProvider(t *testing.T) {
|
|||
func TestContext2Refresh_vars(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
|
||||
schema := &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"ami": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Computed: true,
|
||||
schema := providers.Schema{
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"ami": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
|
||||
Provider: &configschema.Block{},
|
||||
ResourceTypes: map[string]*configschema.Block{"aws_instance": schema},
|
||||
ResourceTypes: map[string]*configschema.Block{"aws_instance": schema.Body},
|
||||
})
|
||||
|
||||
m := testModule(t, "refresh-vars")
|
||||
|
|
@ -1094,7 +1094,7 @@ func TestContext2Refresh_vars(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
readStateVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
||||
readStateVal, err := schema.Body.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}))
|
||||
if err != nil {
|
||||
|
|
@ -1122,7 +1122,7 @@ func TestContext2Refresh_vars(t *testing.T) {
|
|||
|
||||
mod := s.RootModule()
|
||||
|
||||
newState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(schema.ImpliedType())
|
||||
newState, err := mod.Resources["aws_instance.web"].Instances[addrs.NoKey].Current.Decode(schema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,11 @@ type MockEvalContext struct {
|
|||
ProviderSchemaSchema providers.ProviderSchema
|
||||
ProviderSchemaError error
|
||||
|
||||
ResourceIdentitySchemasCalled bool
|
||||
ResourceIdentitySchemasAddr addrs.AbsProviderConfig
|
||||
ResourceIdentitySchemasSchemas providers.ResourceIdentitySchemas
|
||||
ResourceIdentitySchemasError error
|
||||
|
||||
CloseProviderCalled bool
|
||||
CloseProviderAddr addrs.AbsProviderConfig
|
||||
CloseProviderProvider providers.Interface
|
||||
|
|
@ -209,6 +214,12 @@ func (c *MockEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (provider
|
|||
return c.ProviderSchemaSchema, c.ProviderSchemaError
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) ResourceIdentitySchemas(addr addrs.AbsProviderConfig) (providers.ResourceIdentitySchemas, error) {
|
||||
c.ResourceIdentitySchemasCalled = true
|
||||
c.ResourceIdentitySchemasAddr = addr
|
||||
return c.ResourceIdentitySchemasSchemas, c.ProviderSchemaError
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error {
|
||||
c.CloseProviderCalled = true
|
||||
c.CloseProviderAddr = addr
|
||||
|
|
|
|||
|
|
@ -58,5 +58,6 @@ func getProvider(ctx EvalContext, addr addrs.AbsProviderConfig) (providers.Inter
|
|||
if err != nil {
|
||||
return nil, providers.ProviderSchema{}, fmt.Errorf("failed to read schema for provider %s: %w", addr, err)
|
||||
}
|
||||
|
||||
return provider, schema, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/didyoumean"
|
||||
"github.com/hashicorp/terraform/internal/instances"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
|
|
@ -21,6 +20,7 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/namedvals"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/plans/deferring"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/resources/ephemeral"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
|
|
@ -556,7 +556,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
|
|||
// We need to build an abs provider address, but we can use a default
|
||||
// instance since we're only interested in the schema.
|
||||
schema := d.getResourceSchema(addr, config.Provider)
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
// This shouldn't happen, since validation before we get here should've
|
||||
// taken care of it, but we'll show a reasonable error message anyway.
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
|
|
@ -567,7 +567,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
|
|||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
ty := schema.ImpliedType()
|
||||
ty := schema.Body.ImpliedType()
|
||||
|
||||
if addr.Mode == addrs.EphemeralResourceMode {
|
||||
// FIXME: This does not yet work with deferrals, and it would be nice to
|
||||
|
|
@ -695,7 +695,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
|
|||
continue
|
||||
}
|
||||
|
||||
ios, err := is.Current.Decode(ty)
|
||||
ios, err := is.Current.Decode(schema)
|
||||
if err != nil {
|
||||
// This shouldn't happen, since by the time we get here we
|
||||
// should have upgraded the state data already.
|
||||
|
|
@ -896,13 +896,13 @@ func (d *evaluationStateData) getEphemeralResource(addr addrs.Resource, rng tfdi
|
|||
}
|
||||
}
|
||||
|
||||
func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAddr addrs.Provider) *configschema.Block {
|
||||
schema, _, err := d.Evaluator.Plugins.ResourceTypeSchema(providerAddr, addr.Mode, addr.Type)
|
||||
func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAddr addrs.Provider) providers.Schema {
|
||||
schema, err := d.Evaluator.Plugins.ResourceTypeSchema(providerAddr, addr.Mode, addr.Type)
|
||||
if err != nil {
|
||||
// We have plently other codepaths that will detect and report
|
||||
// schema lookup errors before we'd reach this point, so we'll just
|
||||
// treat a failure here the same as having no schema.
|
||||
return nil
|
||||
return providers.Schema{}
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ func staticValidateResourceReference(modCfg *configs.Config, addr addrs.Resource
|
|||
}
|
||||
|
||||
providerFqn := modCfg.Module.ProviderForLocalConfig(cfg.ProviderConfigAddr())
|
||||
schema, _, err := plugins.ResourceTypeSchema(providerFqn, addr.Mode, addr.Type)
|
||||
schema, err := plugins.ResourceTypeSchema(providerFqn, addr.Mode, addr.Type)
|
||||
if err != nil {
|
||||
// Prior validation should've taken care of a schema lookup error,
|
||||
// so we should never get here but we'll handle it here anyway for
|
||||
|
|
@ -262,7 +262,7 @@ func staticValidateResourceReference(modCfg *configs.Config, addr addrs.Resource
|
|||
})
|
||||
}
|
||||
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
// Prior validation should've taken care of a resource block with an
|
||||
// unsupported type, so we should never get here but we'll handle it
|
||||
// here anyway for robustness.
|
||||
|
|
@ -298,7 +298,7 @@ func staticValidateResourceReference(modCfg *configs.Config, addr addrs.Resource
|
|||
|
||||
// If we got this far then we'll try to validate the remaining traversal
|
||||
// steps against our schema.
|
||||
moreDiags := schema.StaticValidateTraversal(remain)
|
||||
moreDiags := schema.Body.StaticValidateTraversal(remain)
|
||||
diags = diags.Append(moreDiags)
|
||||
|
||||
return diags
|
||||
|
|
|
|||
|
|
@ -491,12 +491,12 @@ func (n *NodeAbstractResource) readResourceInstanceState(ctx EvalContext, addr a
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource())
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(addr.Resource.ContainingResource())
|
||||
if schema.Body == nil {
|
||||
// Shouldn't happen since we should've failed long ago if no schema is present
|
||||
return nil, diags.Append(fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr))
|
||||
}
|
||||
src, upgradeDiags := upgradeResourceState(addr, provider, src, schema, currentVersion)
|
||||
src, upgradeDiags := upgradeResourceState(addr, provider, src, schema)
|
||||
if n.Config != nil {
|
||||
upgradeDiags = upgradeDiags.InConfigBody(n.Config.Config, addr.String())
|
||||
}
|
||||
|
|
@ -505,7 +505,13 @@ func (n *NodeAbstractResource) readResourceInstanceState(ctx EvalContext, addr a
|
|||
return nil, diags
|
||||
}
|
||||
|
||||
obj, err := src.Decode(schema.ImpliedType())
|
||||
src, upgradeDiags = upgradeResourceIdentity(addr, provider, src, schema)
|
||||
diags = diags.Append(upgradeDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
obj, err := src.Decode(schema)
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
}
|
||||
|
|
@ -536,14 +542,14 @@ func (n *NodeAbstractResource) readResourceInstanceStateDeposed(ctx EvalContext,
|
|||
return nil, diags
|
||||
}
|
||||
|
||||
schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource())
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(addr.Resource.ContainingResource())
|
||||
if schema.Body == nil {
|
||||
// Shouldn't happen since we should've failed long ago if no schema is present
|
||||
return nil, diags.Append(fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr))
|
||||
|
||||
}
|
||||
|
||||
src, upgradeDiags := upgradeResourceState(addr, provider, src, schema, currentVersion)
|
||||
src, upgradeDiags := upgradeResourceState(addr, provider, src, schema)
|
||||
if n.Config != nil {
|
||||
upgradeDiags = upgradeDiags.InConfigBody(n.Config.Config, addr.String())
|
||||
}
|
||||
|
|
@ -556,7 +562,13 @@ func (n *NodeAbstractResource) readResourceInstanceStateDeposed(ctx EvalContext,
|
|||
return nil, diags
|
||||
}
|
||||
|
||||
obj, err := src.Decode(schema.ImpliedType())
|
||||
src, upgradeDiags = upgradeResourceIdentity(addr, provider, src, schema)
|
||||
diags = diags.Append(upgradeDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
obj, err := src.Decode(schema)
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,8 +167,8 @@ func (n *NodeAbstractResourceInstance) readDiff(ctx EvalContext, providerSchema
|
|||
changes := ctx.Changes()
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(addr.Resource.Resource)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(addr.Resource.Resource)
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
return nil, fmt.Errorf("provider does not support resource type %q", addr.Resource.Resource.Type)
|
||||
}
|
||||
|
|
@ -341,15 +341,15 @@ func (n *NodeAbstractResourceInstance) writeResourceInstanceStateImpl(ctx EvalCo
|
|||
|
||||
log.Printf("[TRACE] %s: writing state object for %s", logFuncName, absAddr)
|
||||
|
||||
schema, currentVersion := providerSchema.SchemaForResourceAddr(absAddr.ContainingResource().Resource)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(absAddr.ContainingResource().Resource)
|
||||
if schema.Body == nil {
|
||||
// It shouldn't be possible to get this far in any real scenario
|
||||
// without a schema, but we might end up here in contrived tests that
|
||||
// fail to set up their world properly.
|
||||
return fmt.Errorf("failed to encode %s in state: no resource type schema available", absAddr)
|
||||
}
|
||||
|
||||
src, err := obj.Encode(schema.ImpliedType(), currentVersion)
|
||||
src, err := obj.Encode(schema)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode %s in state: %s", absAddr, err)
|
||||
}
|
||||
|
|
@ -596,8 +596,8 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
return state, deferred, diags
|
||||
}
|
||||
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.Resource.ContainingResource())
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(n.Addr.Resource.ContainingResource())
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Resource.Type))
|
||||
return state, deferred, diags
|
||||
|
|
@ -630,6 +630,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
// to the provider so we'll just return whatever was in state.
|
||||
resp = providers.ReadResourceResponse{
|
||||
NewState: priorVal,
|
||||
Identity: state.Identity,
|
||||
}
|
||||
} else {
|
||||
resp = provider.ReadResource(providers.ReadResourceRequest{
|
||||
|
|
@ -679,7 +680,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
))
|
||||
}
|
||||
|
||||
for _, err := range resp.NewState.Type().TestConformance(schema.ImpliedType()) {
|
||||
for _, err := range resp.NewState.Type().TestConformance(schema.Body.ImpliedType()) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid object",
|
||||
|
|
@ -703,7 +704,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
)
|
||||
},
|
||||
resp.NewState,
|
||||
schema,
|
||||
schema.Body,
|
||||
)
|
||||
diags = diags.Append(writeOnlyDiags)
|
||||
|
||||
|
|
@ -711,7 +712,12 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
return state, deferred, diags
|
||||
}
|
||||
|
||||
newState := objchange.NormalizeObjectFromLegacySDK(resp.NewState, schema)
|
||||
diags = diags.Append(n.validateIdentity(state, resp.Identity, false))
|
||||
if diags.HasErrors() {
|
||||
return state, deferred, diags
|
||||
}
|
||||
|
||||
newState := objchange.NormalizeObjectFromLegacySDK(resp.NewState, schema.Body)
|
||||
if !newState.RawEquals(resp.NewState) {
|
||||
// We had to fix up this object in some way, and we still need to
|
||||
// accept any changes for compatibility, so all we can do is log a
|
||||
|
|
@ -722,6 +728,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
ret := state.DeepCopy()
|
||||
ret.Value = newState
|
||||
ret.Private = resp.Private
|
||||
ret.Identity = resp.Identity
|
||||
|
||||
// Call post-refresh hook
|
||||
diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
|
|
@ -737,7 +744,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
|
|||
// the prior state. New marks may appear when the prior state was from an
|
||||
// import operation, or if the provider added new marks to the schema.
|
||||
ret.Value = ret.Value.MarkWithPaths(priorMarks)
|
||||
if moreSensitivePaths := schema.SensitivePaths(ret.Value, nil); len(moreSensitivePaths) != 0 {
|
||||
if moreSensitivePaths := schema.Body.SensitivePaths(ret.Value, nil); len(moreSensitivePaths) != 0 {
|
||||
ret.Value = marks.MarkPaths(ret.Value, marks.Sensitive, moreSensitivePaths)
|
||||
}
|
||||
|
||||
|
|
@ -763,8 +770,8 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
return nil, nil, deferred, keyData, diags.Append(err)
|
||||
}
|
||||
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(resource)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(resource)
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider does not support resource type %q", resource.Type))
|
||||
return nil, nil, deferred, keyData, diags
|
||||
|
|
@ -819,10 +826,10 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
return plannedChange, currentState.DeepCopy(), deferred, keyData, diags
|
||||
}
|
||||
|
||||
origConfigVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData)
|
||||
origConfigVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(configDiags)
|
||||
diags = diags.Append(
|
||||
validateResourceForbiddenEphemeralValues(ctx, origConfigVal, schema).InConfigBody(n.Config.Config, n.Addr.String()),
|
||||
validateResourceForbiddenEphemeralValues(ctx, origConfigVal, schema.Body).InConfigBody(n.Config.Config, n.Addr.String()),
|
||||
)
|
||||
if diags.HasErrors() {
|
||||
return nil, nil, deferred, keyData, diags
|
||||
|
|
@ -848,10 +855,10 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
// result as if the provider had marked at least one argument
|
||||
// change as "requires replacement".
|
||||
priorValTainted = currentState.Value
|
||||
priorVal = cty.NullVal(schema.ImpliedType())
|
||||
priorVal = cty.NullVal(schema.Body.ImpliedType())
|
||||
}
|
||||
} else {
|
||||
priorVal = cty.NullVal(schema.ImpliedType())
|
||||
priorVal = cty.NullVal(schema.Body.ImpliedType())
|
||||
}
|
||||
|
||||
log.Printf("[TRACE] Re-validating config for %q", n.Addr)
|
||||
|
|
@ -885,7 +892,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
// starting values.
|
||||
// Here we operate on the marked values, so as to revert any changes to the
|
||||
// marks as well as the value.
|
||||
configValIgnored, ignoreChangeDiags := n.processIgnoreChanges(priorVal, origConfigVal, schema)
|
||||
configValIgnored, ignoreChangeDiags := n.processIgnoreChanges(priorVal, origConfigVal, schema.Body)
|
||||
diags = diags.Append(ignoreChangeDiags)
|
||||
if ignoreChangeDiags.HasErrors() {
|
||||
return nil, nil, deferred, keyData, diags
|
||||
|
|
@ -897,7 +904,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
unmarkedConfigVal, unmarkedPaths := configValIgnored.UnmarkDeepWithPaths()
|
||||
unmarkedPriorVal, _ := priorVal.UnmarkDeepWithPaths()
|
||||
|
||||
proposedNewVal := objchange.ProposedNew(schema, unmarkedPriorVal, unmarkedConfigVal)
|
||||
proposedNewVal := objchange.ProposedNew(schema.Body, unmarkedPriorVal, unmarkedConfigVal)
|
||||
|
||||
// Call pre-diff hook
|
||||
diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
|
|
@ -919,7 +926,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
Value: n.override.Values,
|
||||
Range: n.override.Range,
|
||||
ComputedAsUnknown: !n.override.UseForPlan,
|
||||
}, schema)
|
||||
}, schema.Body)
|
||||
resp = providers.PlanResourceChangeResponse{
|
||||
PlannedState: override,
|
||||
Diagnostics: overrideDiags,
|
||||
|
|
@ -980,7 +987,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
)
|
||||
},
|
||||
plannedNewVal,
|
||||
schema,
|
||||
schema.Body,
|
||||
)
|
||||
diags = diags.Append(writeOnlyDiags)
|
||||
|
||||
|
|
@ -992,7 +999,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
// here, since that allows the provider to do special logic like a
|
||||
// DiffSuppressFunc, but we still require that the provider produces
|
||||
// a value whose type conforms to the schema.
|
||||
for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) {
|
||||
for _, err := range plannedNewVal.Type().TestConformance(schema.Body.ImpliedType()) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid plan",
|
||||
|
|
@ -1007,7 +1014,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
return nil, nil, deferred, keyData, diags
|
||||
}
|
||||
|
||||
if errs := objchange.AssertPlanValid(schema, unmarkedPriorVal, unmarkedConfigVal, plannedNewVal); len(errs) > 0 {
|
||||
if errs := objchange.AssertPlanValid(schema.Body, unmarkedPriorVal, unmarkedConfigVal, plannedNewVal); len(errs) > 0 {
|
||||
if resp.LegacyTypeSystem {
|
||||
// The shimming of the old type system in the legacy SDK is not precise
|
||||
// enough to pass this consistency check, so we'll give it a pass here,
|
||||
|
|
@ -1069,11 +1076,11 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
unmarkedPaths = marks.RemoveAll(unmarkedPaths, marks.Ephemeral)
|
||||
|
||||
plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths)
|
||||
if sensitivePaths := schema.SensitivePaths(plannedNewVal, nil); len(sensitivePaths) != 0 {
|
||||
if sensitivePaths := schema.Body.SensitivePaths(plannedNewVal, nil); len(sensitivePaths) != 0 {
|
||||
plannedNewVal = marks.MarkPaths(plannedNewVal, marks.Sensitive, sensitivePaths)
|
||||
}
|
||||
|
||||
writeOnlyPaths := schema.WriteOnlyPaths(plannedNewVal, nil)
|
||||
writeOnlyPaths := schema.Body.WriteOnlyPaths(plannedNewVal, nil)
|
||||
|
||||
reqRep, reqRepDiags := getRequiredReplaces(unmarkedPriorVal, unmarkedPlannedNewVal, writeOnlyPaths, resp.RequiresReplace, n.ResolvedProvider.Provider, n.Addr)
|
||||
diags = diags.Append(reqRepDiags)
|
||||
|
|
@ -1096,7 +1103,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
// The resulting change should show any computed attributes changing
|
||||
// from known prior values to unknown values, unless the provider is
|
||||
// able to predict new values for any of these computed attributes.
|
||||
nullPriorVal := cty.NullVal(schema.ImpliedType())
|
||||
nullPriorVal := cty.NullVal(schema.Body.ImpliedType())
|
||||
|
||||
// Since there is no prior state to compare after replacement, we need
|
||||
// a new unmarked config from our original with no ignored values.
|
||||
|
|
@ -1106,7 +1113,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
}
|
||||
|
||||
// create a new proposed value from the null state and the config
|
||||
proposedNewVal = objchange.ProposedNew(schema, nullPriorVal, unmarkedConfigVal)
|
||||
proposedNewVal = objchange.ProposedNew(schema.Body, nullPriorVal, unmarkedConfigVal)
|
||||
|
||||
if n.override != nil {
|
||||
// In this case, we are always creating the resource so we don't
|
||||
|
|
@ -1115,7 +1122,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
Value: n.override.Values,
|
||||
Range: n.override.Range,
|
||||
ComputedAsUnknown: !n.override.UseForPlan,
|
||||
}, schema)
|
||||
}, schema.Body)
|
||||
resp = providers.PlanResourceChangeResponse{
|
||||
PlannedState: override,
|
||||
Diagnostics: overrideDiags,
|
||||
|
|
@ -1158,7 +1165,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths)
|
||||
}
|
||||
|
||||
for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) {
|
||||
for _, err := range plannedNewVal.Type().TestConformance(schema.Body.ImpliedType()) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid plan",
|
||||
|
|
@ -1182,7 +1189,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
|||
)
|
||||
},
|
||||
plannedNewVal,
|
||||
schema,
|
||||
schema.Body,
|
||||
)
|
||||
diags = diags.Append(writeOnlyDiags)
|
||||
|
||||
|
|
@ -1544,8 +1551,8 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal
|
|||
if diags.HasErrors() {
|
||||
return newVal, deferred, diags
|
||||
}
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type))
|
||||
return newVal, deferred, diags
|
||||
|
|
@ -1590,7 +1597,7 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal
|
|||
Value: n.override.Values,
|
||||
Range: n.override.Range,
|
||||
ComputedAsUnknown: false,
|
||||
}, schema)
|
||||
}, schema.Body)
|
||||
resp = providers.ReadDataSourceResponse{
|
||||
State: override,
|
||||
Diagnostics: overrideDiags,
|
||||
|
|
@ -1621,12 +1628,12 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal
|
|||
if newVal == cty.NilVal {
|
||||
// This can happen with incompletely-configured mocks. We'll allow it
|
||||
// and treat it as an alias for a properly-typed null value.
|
||||
newVal = cty.NullVal(schema.ImpliedType())
|
||||
newVal = cty.NullVal(schema.Body.ImpliedType())
|
||||
}
|
||||
|
||||
// We don't want to run the checks if the data source read is deferred
|
||||
if deferred == nil {
|
||||
for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) {
|
||||
for _, err := range newVal.Type().TestConformance(schema.Body.ImpliedType()) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid object",
|
||||
|
|
@ -1671,7 +1678,7 @@ func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal
|
|||
}
|
||||
}
|
||||
newVal = newVal.MarkWithPaths(pvm)
|
||||
if sensitivePaths := schema.SensitivePaths(newVal, nil); len(sensitivePaths) != 0 {
|
||||
if sensitivePaths := schema.Body.SensitivePaths(newVal, nil); len(sensitivePaths) != 0 {
|
||||
newVal = marks.MarkPaths(newVal, marks.Sensitive, sensitivePaths)
|
||||
}
|
||||
|
||||
|
|
@ -1750,14 +1757,14 @@ func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, checkRule
|
|||
}
|
||||
|
||||
config := *n.Config
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type))
|
||||
return nil, nil, deferred, keyData, diags
|
||||
}
|
||||
|
||||
objTy := schema.ImpliedType()
|
||||
objTy := schema.Body.ImpliedType()
|
||||
priorVal := cty.NullVal(objTy)
|
||||
|
||||
forEach, _, _ := evaluateForEachExpression(config.ForEach, ctx, false)
|
||||
|
|
@ -1775,10 +1782,10 @@ func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, checkRule
|
|||
}
|
||||
|
||||
var configDiags tfdiags.Diagnostics
|
||||
configVal, _, configDiags = ctx.EvaluateBlock(config.Config, schema, nil, keyData)
|
||||
configVal, _, configDiags = ctx.EvaluateBlock(config.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(configDiags)
|
||||
diags = diags.Append(
|
||||
validateResourceForbiddenEphemeralValues(ctx, configVal, schema).InConfigBody(n.Config.Config, n.Addr.String()),
|
||||
validateResourceForbiddenEphemeralValues(ctx, configVal, schema.Body).InConfigBody(n.Config.Config, n.Addr.String()),
|
||||
)
|
||||
if diags.HasErrors() {
|
||||
return nil, nil, deferred, keyData, diags
|
||||
|
|
@ -1842,13 +1849,13 @@ func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, checkRule
|
|||
reason = plans.ResourceInstanceReadBecauseDependencyPending
|
||||
}
|
||||
|
||||
proposedNewVal := objchange.PlannedDataResourceObject(schema, unmarkedConfigVal)
|
||||
proposedNewVal := objchange.PlannedDataResourceObject(schema.Body, unmarkedConfigVal)
|
||||
|
||||
// even though we are only returning the config value because we can't
|
||||
// yet read the data source, we need to incorporate the schema marks so
|
||||
// that downstream consumers can detect them when planning.
|
||||
proposedNewVal = proposedNewVal.MarkWithPaths(unmarkedPaths)
|
||||
if sensitivePaths := schema.SensitivePaths(proposedNewVal, nil); len(sensitivePaths) != 0 {
|
||||
if sensitivePaths := schema.Body.SensitivePaths(proposedNewVal, nil); len(sensitivePaths) != 0 {
|
||||
proposedNewVal = marks.MarkPaths(proposedNewVal, marks.Sensitive, sensitivePaths)
|
||||
}
|
||||
|
||||
|
|
@ -1917,13 +1924,13 @@ func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, checkRule
|
|||
if readDiags.HasErrors() {
|
||||
// If we had errors, then we can cover that up by marking the new
|
||||
// state as unknown.
|
||||
newVal = objchange.PlannedDataResourceObject(schema, unmarkedConfigVal)
|
||||
newVal = objchange.PlannedDataResourceObject(schema.Body, unmarkedConfigVal)
|
||||
|
||||
// not only do we want to ensure this synthetic value has the marks,
|
||||
// but since this is the value being returned from the data source
|
||||
// we need to ensure the schema marks are added as well.
|
||||
newVal = newVal.MarkWithPaths(unmarkedPaths)
|
||||
if sensitivePaths := schema.SensitivePaths(newVal, nil); len(sensitivePaths) != 0 {
|
||||
if sensitivePaths := schema.Body.SensitivePaths(newVal, nil); len(sensitivePaths) != 0 {
|
||||
newVal = marks.MarkPaths(newVal, marks.Sensitive, sensitivePaths)
|
||||
}
|
||||
|
||||
|
|
@ -2080,8 +2087,8 @@ func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned
|
|||
}
|
||||
|
||||
config := *n.Config
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type))
|
||||
return nil, keyData, diags
|
||||
|
|
@ -2111,7 +2118,7 @@ func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned
|
|||
return nil, keyData, diags
|
||||
}
|
||||
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData)
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
return nil, keyData, diags
|
||||
|
|
@ -2477,8 +2484,8 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
if err != nil {
|
||||
return nil, diags.Append(err)
|
||||
}
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Addr.Resource.Resource.Mode, n.Addr.Resource.Resource.Type)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceType(n.Addr.Resource.Resource.Mode, n.Addr.Resource.Resource.Type)
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Resource.Type))
|
||||
return nil, diags
|
||||
|
|
@ -2489,7 +2496,7 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
configVal := cty.NullVal(cty.DynamicPseudoType)
|
||||
if applyConfig != nil {
|
||||
var configDiags tfdiags.Diagnostics
|
||||
configVal, _, configDiags = ctx.EvaluateBlock(applyConfig.Config, schema, nil, keyData)
|
||||
configVal, _, configDiags = ctx.EvaluateBlock(applyConfig.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
return nil, diags
|
||||
|
|
@ -2564,7 +2571,7 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
Value: n.override.Values,
|
||||
Range: n.override.Range,
|
||||
ComputedAsUnknown: false,
|
||||
}, schema)
|
||||
}, schema.Body)
|
||||
resp = providers.ApplyResourceChangeResponse{
|
||||
NewState: override,
|
||||
Diagnostics: overrideDiags,
|
||||
|
|
@ -2608,7 +2615,7 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
// we were trying to execute a delete, because the provider in this case
|
||||
// probably left the newVal unset intending it to be interpreted as "null".
|
||||
if change.After.IsNull() {
|
||||
newVal = cty.NullVal(schema.ImpliedType())
|
||||
newVal = cty.NullVal(schema.Body.ImpliedType())
|
||||
}
|
||||
|
||||
if !diags.HasErrors() {
|
||||
|
|
@ -2624,7 +2631,7 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
}
|
||||
|
||||
var conformDiags tfdiags.Diagnostics
|
||||
for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) {
|
||||
for _, err := range newVal.Type().TestConformance(schema.Body.ImpliedType()) {
|
||||
conformDiags = conformDiags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid object",
|
||||
|
|
@ -2652,7 +2659,7 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
)
|
||||
},
|
||||
newVal,
|
||||
schema,
|
||||
schema.Body,
|
||||
)
|
||||
diags = diags.Append(writeOnlyDiags)
|
||||
|
||||
|
|
@ -2705,7 +2712,7 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
// won't be included in afterPaths, which are only what was read from the
|
||||
// After plan value.
|
||||
newVal = newVal.MarkWithPaths(afterPaths)
|
||||
if sensitivePaths := schema.SensitivePaths(newVal, nil); len(sensitivePaths) != 0 {
|
||||
if sensitivePaths := schema.Body.SensitivePaths(newVal, nil); len(sensitivePaths) != 0 {
|
||||
newVal = marks.MarkPaths(newVal, marks.Sensitive, sensitivePaths)
|
||||
}
|
||||
|
||||
|
|
@ -2719,7 +2726,7 @@ func (n *NodeAbstractResourceInstance) apply(
|
|||
// a pass since the other errors are usually the explanation for
|
||||
// this one and so it's more helpful to let the user focus on the
|
||||
// root cause rather than distract with this extra problem.
|
||||
if errs := objchange.AssertObjectCompatible(schema, change.After, newVal); len(errs) > 0 {
|
||||
if errs := objchange.AssertObjectCompatible(schema.Body, change.After, newVal); len(errs) > 0 {
|
||||
if resp.LegacyTypeSystem {
|
||||
// The shimming of the old type system in the legacy SDK is not precise
|
||||
// enough to pass this consistency check, so we'll give it a pass here,
|
||||
|
|
@ -2831,6 +2838,47 @@ 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
|
||||
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.",
|
||||
n.ResolvedProvider.Provider, n.Addr,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
// Identities can not contain marks
|
||||
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.",
|
||||
n.ResolvedProvider.Provider, n.Addr,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
func resourceInstancePrevRunAddr(ctx EvalContext, currentAddr addrs.AbsResourceInstance) addrs.AbsResourceInstance {
|
||||
table := ctx.MoveResults()
|
||||
return table.OldAddr(currentAddr)
|
||||
|
|
|
|||
|
|
@ -407,8 +407,8 @@ func (n *NodeApplyableResourceInstance) checkPlannedChange(ctx EvalContext, plan
|
|||
var diags tfdiags.Diagnostics
|
||||
addr := n.ResourceInstanceAddr().Resource
|
||||
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(addr.ContainingResource())
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(addr.ContainingResource())
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider does not support %q", addr.Resource.Type))
|
||||
return diags
|
||||
|
|
@ -450,7 +450,7 @@ func (n *NodeApplyableResourceInstance) checkPlannedChange(ctx EvalContext, plan
|
|||
}
|
||||
}
|
||||
|
||||
errs := objchange.AssertObjectCompatible(schema, plannedChange.After, actualChange.After)
|
||||
errs := objchange.AssertObjectCompatible(schema.Body, plannedChange.After, actualChange.After)
|
||||
for _, err := range errs {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
|
|
|
|||
|
|
@ -447,14 +447,15 @@ func (n *NodeDestroyDeposedResourceInstanceObject) writeResourceInstanceState(ct
|
|||
return err
|
||||
}
|
||||
|
||||
schema, currentVersion := providerSchema.SchemaForResourceAddr(absAddr.ContainingResource().Resource)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(absAddr.ContainingResource().Resource)
|
||||
if schema.Body == nil {
|
||||
// It shouldn't be possible to get this far in any real scenario
|
||||
// without a schema, but we might end up here in contrived tests that
|
||||
// fail to set up their world properly.
|
||||
return fmt.Errorf("failed to encode %s in state: no resource type schema available", absAddr)
|
||||
}
|
||||
src, err := obj.Encode(schema.ImpliedType(), currentVersion)
|
||||
|
||||
src, err := obj.Encode(schema)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode %s in state: %s", absAddr, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ func ephemeralResourceOpen(ctx EvalContext, inp ephemeralResourceInput) (*provid
|
|||
}
|
||||
|
||||
config := inp.config
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(inp.addr.ContainingResource().Resource)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(inp.addr.ContainingResource().Resource)
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(
|
||||
fmt.Errorf("provider %q does not support ephemeral resource %q",
|
||||
|
|
@ -71,7 +71,7 @@ func ephemeralResourceOpen(ctx EvalContext, inp ephemeralResourceInput) (*provid
|
|||
return nil, diags // failed preconditions prevent further evaluation
|
||||
}
|
||||
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData)
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(configDiags)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
|
|
@ -84,7 +84,7 @@ func ephemeralResourceOpen(ctx EvalContext, inp ephemeralResourceInput) (*provid
|
|||
// We don't know what the result will be, but we need to keep the
|
||||
// configured attributes for consistent evaluation. We can use the same
|
||||
// technique we used for data sources to create the plan-time value.
|
||||
unknownResult := objchange.PlannedDataResourceObject(schema, unmarkedConfigVal)
|
||||
unknownResult := objchange.PlannedDataResourceObject(schema.Body, unmarkedConfigVal)
|
||||
// add back any configured marks
|
||||
unknownResult = unknownResult.MarkWithPaths(configMarks)
|
||||
// and mark the entire value as ephemeral, since it's coming from an ephemeral context.
|
||||
|
|
@ -135,7 +135,7 @@ func ephemeralResourceOpen(ctx EvalContext, inp ephemeralResourceInput) (*provid
|
|||
}
|
||||
resultVal := resp.Result.MarkWithPaths(configMarks)
|
||||
|
||||
errs := objchange.AssertPlanValid(schema, cty.NullVal(schema.ImpliedType()), configVal, resultVal)
|
||||
errs := objchange.AssertPlanValid(schema.Body, cty.NullVal(schema.Body.ImpliedType()), configVal, resultVal)
|
||||
for _, err := range errs {
|
||||
diags = diags.Append(tfdiags.AttributeValue(
|
||||
tfdiags.Error,
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@ func (n *graphNodeImportState) Execute(ctx EvalContext, op walkOperation) (diags
|
|||
return diags
|
||||
}
|
||||
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Addr.Resource.Resource.Mode, n.Addr.Resource.Resource.Type)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceType(n.Addr.Resource.Resource.Mode, n.Addr.Resource.Resource.Type)
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Resource.Type))
|
||||
return diags
|
||||
|
|
@ -125,7 +125,7 @@ func (n *graphNodeImportState) Execute(ctx EvalContext, op walkOperation) (diags
|
|||
)
|
||||
},
|
||||
imported.State,
|
||||
schema,
|
||||
schema.Body,
|
||||
)
|
||||
diags = diags.Append(writeOnlyDiags)
|
||||
}
|
||||
|
|
@ -250,7 +250,7 @@ func (n *graphNodeImportStateSub) Execute(ctx EvalContext, op walkOperation) (di
|
|||
return diags
|
||||
}
|
||||
|
||||
state := n.State.AsInstanceObject()
|
||||
state := states.NewResourceInstanceObjectFromIR(n.State)
|
||||
|
||||
// Refresh
|
||||
riNode := &NodeAbstractResourceInstance{
|
||||
|
|
|
|||
|
|
@ -587,8 +587,8 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
|
|||
return nil, deferred, diags
|
||||
}
|
||||
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.Resource.Resource)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(n.Addr.Resource.Resource)
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider does not support resource type for %q", n.Addr))
|
||||
return nil, deferred, diags
|
||||
|
|
@ -615,7 +615,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
|
|||
|
||||
forEach, _, _ := evaluateForEachExpression(n.Config.ForEach, ctx, false)
|
||||
keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach)
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData)
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(n.Config.Config, schema.Body, nil, keyData)
|
||||
if configDiags.HasErrors() {
|
||||
// We have an overridden resource so we're definitely in a test and
|
||||
// the users config is not valid. So give up and just report the
|
||||
|
|
@ -638,7 +638,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
|
|||
Value: n.override.Values,
|
||||
Range: n.override.Range,
|
||||
ComputedAsUnknown: false,
|
||||
}, schema)
|
||||
}, schema.Body)
|
||||
resp = providers.ImportResourceStateResponse{
|
||||
ImportedResources: []providers.ImportedResource{
|
||||
{
|
||||
|
|
@ -699,12 +699,12 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
|
|||
// state we're going to import.
|
||||
state := providers.ImportedResource{
|
||||
TypeName: addr.Resource.Resource.Type,
|
||||
State: cty.NullVal(schema.ImpliedType()),
|
||||
State: cty.NullVal(schema.Body.ImpliedType()),
|
||||
}
|
||||
|
||||
// We skip the read and further validation since we make up the state
|
||||
// of the imported resource anyways.
|
||||
return state.AsInstanceObject(), deferred, diags
|
||||
return states.NewResourceInstanceObjectFromIR(state), deferred, diags
|
||||
}
|
||||
|
||||
for _, obj := range imported {
|
||||
|
|
@ -735,7 +735,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
|
|||
)
|
||||
},
|
||||
imported[0].State,
|
||||
schema,
|
||||
schema.Body,
|
||||
)
|
||||
diags = diags.Append(writeOnlyDiags)
|
||||
|
||||
|
|
@ -743,7 +743,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
|
|||
return nil, deferred, diags
|
||||
}
|
||||
|
||||
importedState := imported[0].AsInstanceObject()
|
||||
importedState := states.NewResourceInstanceObjectFromIR(imported[0])
|
||||
if deferred == nil && importedState.Value.IsNull() {
|
||||
// It's actually okay for a deferred import to have returned a null.
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
|
|
@ -807,7 +807,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
|
|||
// First we generate the contents of the resource block for use within
|
||||
// the planning node. Then we wrap it in an enclosing resource block to
|
||||
// pass into the plan for rendering.
|
||||
generatedHCLAttributes, generatedDiags := n.generateHCLStringAttributes(n.Addr, instanceRefreshState, schema)
|
||||
generatedHCLAttributes, generatedDiags := n.generateHCLStringAttributes(n.Addr, instanceRefreshState, schema.Body)
|
||||
diags = diags.Append(generatedDiags)
|
||||
|
||||
n.generatedConfigHCL = genconfig.WrapResourceContents(n.Addr, generatedHCLAttributes)
|
||||
|
|
|
|||
|
|
@ -165,8 +165,8 @@ func (n *nodePlannablePartialExpandedResource) managedResourceExecute(ctx EvalCo
|
|||
return &change, diags
|
||||
}
|
||||
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(n.addr.Resource())
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(n.addr.Resource())
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.addr.Resource().Type))
|
||||
return &change, diags
|
||||
|
|
@ -194,7 +194,7 @@ func (n *nodePlannablePartialExpandedResource) managedResourceExecute(ctx EvalCo
|
|||
|
||||
keyData := n.keyData()
|
||||
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(n.config.Config, schema, nil, keyData)
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(n.config.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
return &change, diags
|
||||
|
|
@ -214,8 +214,8 @@ func (n *nodePlannablePartialExpandedResource) managedResourceExecute(ctx EvalCo
|
|||
}
|
||||
|
||||
unmarkedConfigVal, unmarkedPaths := configVal.UnmarkDeepWithPaths()
|
||||
priorVal := cty.NullVal(schema.ImpliedType()) // we don't have any specific prior value to use
|
||||
proposedNewVal := objchange.ProposedNew(schema, priorVal, unmarkedConfigVal)
|
||||
priorVal := cty.NullVal(schema.Body.ImpliedType()) // we don't have any specific prior value to use
|
||||
proposedNewVal := objchange.ProposedNew(schema.Body, priorVal, unmarkedConfigVal)
|
||||
|
||||
// The provider now gets to plan an imaginary substitute that represents
|
||||
// all of the possible resource instances together. Correctly-implemented
|
||||
|
|
@ -247,7 +247,7 @@ func (n *nodePlannablePartialExpandedResource) managedResourceExecute(ctx EvalCo
|
|||
panic(fmt.Sprintf("PlanResourceChange of %s produced nil value", n.addr.String()))
|
||||
}
|
||||
|
||||
for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) {
|
||||
for _, err := range plannedNewVal.Type().TestConformance(schema.Body.ImpliedType()) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Provider produced invalid plan",
|
||||
|
|
@ -261,7 +261,7 @@ func (n *nodePlannablePartialExpandedResource) managedResourceExecute(ctx EvalCo
|
|||
return &change, diags
|
||||
}
|
||||
|
||||
if errs := objchange.AssertPlanValid(schema, priorVal, unmarkedConfigVal, plannedNewVal); len(errs) > 0 {
|
||||
if errs := objchange.AssertPlanValid(schema.Body, priorVal, unmarkedConfigVal, plannedNewVal); len(errs) > 0 {
|
||||
if resp.LegacyTypeSystem {
|
||||
// The shimming of the old type system in the legacy SDK is not precise
|
||||
// enough to pass this consistency check, so we'll give it a pass here,
|
||||
|
|
@ -295,7 +295,7 @@ func (n *nodePlannablePartialExpandedResource) managedResourceExecute(ctx EvalCo
|
|||
// We need to combine the dynamic marks with the static marks implied by
|
||||
// the provider's schema.
|
||||
plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths)
|
||||
if sensitivePaths := schema.SensitivePaths(plannedNewVal, nil); len(sensitivePaths) != 0 {
|
||||
if sensitivePaths := schema.Body.SensitivePaths(plannedNewVal, nil); len(sensitivePaths) != 0 {
|
||||
plannedNewVal = marks.MarkPaths(plannedNewVal, marks.Sensitive, sensitivePaths)
|
||||
}
|
||||
|
||||
|
|
@ -339,8 +339,8 @@ func (n *nodePlannablePartialExpandedResource) dataResourceExecute(ctx EvalConte
|
|||
// This is the point where we switch to mirroring logic from
|
||||
// NodeAbstractResourceInstance's planDataSource. If you were curious.
|
||||
|
||||
schema, _ := providerSchema.SchemaForResourceAddr(n.addr.Resource())
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceAddr(n.addr.Resource())
|
||||
if schema.Body == nil {
|
||||
// Should be caught during validation, so we don't bother with a pretty error here
|
||||
diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.addr.Resource().Type))
|
||||
return &change, diags
|
||||
|
|
@ -348,7 +348,7 @@ func (n *nodePlannablePartialExpandedResource) dataResourceExecute(ctx EvalConte
|
|||
|
||||
keyData := n.keyData()
|
||||
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(n.config.Config, schema, nil, keyData)
|
||||
configVal, _, configDiags := ctx.EvaluateBlock(n.config.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
return &change, diags
|
||||
|
|
@ -369,9 +369,9 @@ func (n *nodePlannablePartialExpandedResource) dataResourceExecute(ctx EvalConte
|
|||
// logic for a data source with unknown config, which is sort of what we
|
||||
// are, after all.
|
||||
unmarkedConfigVal, unmarkedPaths := configVal.UnmarkDeepWithPaths()
|
||||
proposedNewVal := objchange.PlannedDataResourceObject(schema, unmarkedConfigVal)
|
||||
proposedNewVal := objchange.PlannedDataResourceObject(schema.Body, unmarkedConfigVal)
|
||||
proposedNewVal = proposedNewVal.MarkWithPaths(unmarkedPaths)
|
||||
if sensitivePaths := schema.SensitivePaths(proposedNewVal, nil); len(sensitivePaths) != 0 {
|
||||
if sensitivePaths := schema.Body.SensitivePaths(proposedNewVal, nil); len(sensitivePaths) != 0 {
|
||||
proposedNewVal = marks.MarkPaths(proposedNewVal, marks.Sensitive, sensitivePaths)
|
||||
}
|
||||
// yay we made it
|
||||
|
|
|
|||
|
|
@ -323,10 +323,10 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
|||
// in the provider abstraction.
|
||||
switch n.Config.Mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema.Body == nil {
|
||||
var suggestion string
|
||||
if dSchema, _ := providerSchema.SchemaForResourceType(addrs.DataResourceMode, n.Config.Type); dSchema != nil {
|
||||
if dSchema := providerSchema.SchemaForResourceType(addrs.DataResourceMode, n.Config.Type); dSchema.Body != nil {
|
||||
suggestion = fmt.Sprintf("\n\nDid you intend to use the data source %q? If so, declare this using a \"data\" block instead of a \"resource\" block.", n.Config.Type)
|
||||
} else if len(providerSchema.ResourceTypes) > 0 {
|
||||
suggestions := make([]string, 0, len(providerSchema.ResourceTypes))
|
||||
|
|
@ -347,19 +347,19 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
|||
return diags
|
||||
}
|
||||
|
||||
configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData)
|
||||
configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(valDiags)
|
||||
if valDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
diags = diags.Append(
|
||||
validateResourceForbiddenEphemeralValues(ctx, configVal, schema).InConfigBody(n.Config.Config, n.Addr.String()),
|
||||
validateResourceForbiddenEphemeralValues(ctx, configVal, schema.Body).InConfigBody(n.Config.Config, n.Addr.String()),
|
||||
)
|
||||
|
||||
if n.Config.Managed != nil { // can be nil only in tests with poorly-configured mocks
|
||||
for _, traversal := range n.Config.Managed.IgnoreChanges {
|
||||
// validate the ignore_changes traversals apply.
|
||||
moreDiags := schema.StaticValidateTraversal(traversal)
|
||||
moreDiags := schema.Body.StaticValidateTraversal(traversal)
|
||||
diags = diags.Append(moreDiags)
|
||||
|
||||
// ignore_changes cannot be used for Computed attributes,
|
||||
|
|
@ -370,7 +370,7 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
|||
if !diags.HasErrors() {
|
||||
path, _ := traversalToPath(traversal)
|
||||
|
||||
attrSchema := schema.AttributeByPath(path)
|
||||
attrSchema := schema.Body.AttributeByPath(path)
|
||||
|
||||
if attrSchema != nil && !attrSchema.Optional && attrSchema.Computed {
|
||||
// ignore_changes uses absolute traversal syntax in config despite
|
||||
|
|
@ -402,10 +402,10 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
|||
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
|
||||
case addrs.DataResourceMode:
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema.Body == nil {
|
||||
var suggestion string
|
||||
if dSchema, _ := providerSchema.SchemaForResourceType(addrs.ManagedResourceMode, n.Config.Type); dSchema != nil {
|
||||
if dSchema := providerSchema.SchemaForResourceType(addrs.ManagedResourceMode, n.Config.Type); dSchema.Body != nil {
|
||||
suggestion = fmt.Sprintf("\n\nDid you intend to use the managed resource type %q? If so, declare this using a \"resource\" block instead of a \"data\" block.", n.Config.Type)
|
||||
} else if len(providerSchema.DataSources) > 0 {
|
||||
suggestions := make([]string, 0, len(providerSchema.DataSources))
|
||||
|
|
@ -426,13 +426,13 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
|||
return diags
|
||||
}
|
||||
|
||||
configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData)
|
||||
configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(valDiags)
|
||||
if valDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
diags = diags.Append(
|
||||
validateResourceForbiddenEphemeralValues(ctx, configVal, schema).InConfigBody(n.Config.Config, n.Addr.String()),
|
||||
validateResourceForbiddenEphemeralValues(ctx, configVal, schema.Body).InConfigBody(n.Config.Config, n.Addr.String()),
|
||||
)
|
||||
|
||||
// Use unmarked value for validate request
|
||||
|
|
@ -445,8 +445,8 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
|||
resp := provider.ValidateDataResourceConfig(req)
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
case addrs.EphemeralResourceMode:
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema == nil {
|
||||
schema := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema.Body == nil {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid ephemeral resource",
|
||||
|
|
@ -456,7 +456,7 @@ func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diag
|
|||
return diags
|
||||
}
|
||||
|
||||
configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData)
|
||||
configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema.Body, nil, keyData)
|
||||
diags = diags.Append(valDiags)
|
||||
if valDiags.HasErrors() {
|
||||
return diags
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ type providerSchema struct {
|
|||
ResourceTypes map[string]*configschema.Block
|
||||
ResourceTypeSchemaVersions map[string]uint64
|
||||
DataSources map[string]*configschema.Block
|
||||
IdentityTypes map[string]*configschema.Object
|
||||
IdentityTypeSchemaVersions map[string]uint64
|
||||
}
|
||||
|
||||
// getProviderSchemaResponseFromProviderSchema is a test helper to convert a
|
||||
|
|
@ -137,10 +139,18 @@ func getProviderSchemaResponseFromProviderSchema(providerSchema *providerSchema)
|
|||
}
|
||||
|
||||
for name, schema := range providerSchema.ResourceTypes {
|
||||
resp.ResourceTypes[name] = providers.Schema{
|
||||
ps := providers.Schema{
|
||||
Body: schema,
|
||||
Version: int64(providerSchema.ResourceTypeSchemaVersions[name]),
|
||||
}
|
||||
|
||||
id, ok := providerSchema.IdentityTypes[name]
|
||||
if ok {
|
||||
ps.Identity = id
|
||||
ps.IdentityVersion = int64(providerSchema.IdentityTypeSchemaVersions[name])
|
||||
}
|
||||
|
||||
resp.ResourceTypes[name] = ps
|
||||
}
|
||||
|
||||
for name, schema := range providerSchema.DataSources {
|
||||
|
|
|
|||
|
|
@ -65,16 +65,16 @@ func (t *AttachSchemaTransformer) Transform(g *Graph) error {
|
|||
typeName := addr.Resource.Type
|
||||
providerFqn := tv.Provider()
|
||||
|
||||
schema, version, err := t.Plugins.ResourceTypeSchema(providerFqn, mode, typeName)
|
||||
schema, err := t.Plugins.ResourceTypeSchema(providerFqn, mode, typeName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read schema for %s in %s: %s", addr, providerFqn, err)
|
||||
}
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
log.Printf("[ERROR] AttachSchemaTransformer: No resource schema available for %s", addr)
|
||||
continue
|
||||
}
|
||||
log.Printf("[TRACE] AttachSchemaTransformer: attaching resource schema to %s", dag.VertexName(v))
|
||||
tv.AttachResourceSchema(schema, version)
|
||||
tv.AttachResourceSchema(schema.Body, uint64(schema.Version))
|
||||
}
|
||||
|
||||
if tv, ok := v.(GraphNodeAttachProviderConfigSchema); ok {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/ephemeral"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
|
|
@ -25,7 +24,7 @@ import (
|
|||
//
|
||||
// If any errors occur during upgrade, error diagnostics are returned. In that
|
||||
// case it is not safe to proceed with using the original state object.
|
||||
func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Interface, src *states.ResourceInstanceObjectSrc, currentSchema *configschema.Block, currentVersion uint64) (*states.ResourceInstanceObjectSrc, tfdiags.Diagnostics) {
|
||||
func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Interface, src *states.ResourceInstanceObjectSrc, currentSchema providers.Schema) (*states.ResourceInstanceObjectSrc, tfdiags.Diagnostics) {
|
||||
if addr.Resource.Resource.Mode != addrs.ManagedResourceMode {
|
||||
// We only do state upgrading for managed resources.
|
||||
// This was a part of the normal workflow in older versions and
|
||||
|
|
@ -41,16 +40,16 @@ func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Int
|
|||
// Legacy flatmap state is already taken care of during conversion.
|
||||
// If the schema version is be changed, then allow the provider to handle
|
||||
// removed attributes.
|
||||
if len(src.AttrsJSON) > 0 && src.SchemaVersion == currentVersion {
|
||||
src.AttrsJSON = stripRemovedStateAttributes(src.AttrsJSON, currentSchema.ImpliedType())
|
||||
if len(src.AttrsJSON) > 0 && src.SchemaVersion == uint64(currentSchema.Version) {
|
||||
src.AttrsJSON = stripRemovedStateAttributes(src.AttrsJSON, currentSchema.Body.ImpliedType())
|
||||
}
|
||||
|
||||
stateIsFlatmap := len(src.AttrsJSON) == 0
|
||||
|
||||
// TODO: This should eventually use a proper FQN.
|
||||
providerType := addr.Resource.Resource.ImpliedProvider()
|
||||
if src.SchemaVersion > currentVersion {
|
||||
log.Printf("[TRACE] upgradeResourceState: can't downgrade state for %s from version %d to %d", addr, src.SchemaVersion, currentVersion)
|
||||
if src.SchemaVersion > uint64(currentSchema.Version) {
|
||||
log.Printf("[TRACE] upgradeResourceState: can't downgrade state for %s from version %d to %d", addr, src.SchemaVersion, currentSchema.Version)
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
|
|
@ -69,10 +68,10 @@ func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Int
|
|||
// v0.12, this also includes translating from legacy flatmap to new-style
|
||||
// representation, since only the provider has enough information to
|
||||
// understand a flatmap built against an older schema.
|
||||
if src.SchemaVersion != currentVersion {
|
||||
log.Printf("[TRACE] upgradeResourceState: upgrading state for %s from version %d to %d using provider %q", addr, src.SchemaVersion, currentVersion, providerType)
|
||||
if src.SchemaVersion != uint64(currentSchema.Version) {
|
||||
log.Printf("[TRACE] upgradeResourceState: upgrading state for %s from version %d to %d using provider %q", addr, src.SchemaVersion, currentSchema.Version, providerType)
|
||||
} else {
|
||||
log.Printf("[TRACE] upgradeResourceState: schema version of %s is still %d; calling provider %q for any other minor fixups", addr, currentVersion, providerType)
|
||||
log.Printf("[TRACE] upgradeResourceState: schema version of %s is still %d; calling provider %q for any other minor fixups", addr, currentSchema.Version, providerType)
|
||||
}
|
||||
|
||||
req := providers.UpgradeResourceStateRequest{
|
||||
|
|
@ -111,7 +110,7 @@ func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Int
|
|||
// marshaling/unmarshaling of the new value, but we'll check it here
|
||||
// anyway for robustness, e.g. for in-process providers.
|
||||
newValue := resp.UpgradedState
|
||||
if errs := newValue.Type().TestConformance(currentSchema.ImpliedType()); len(errs) > 0 {
|
||||
if errs := newValue.Type().TestConformance(currentSchema.Body.ImpliedType()); len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
|
|
@ -132,7 +131,7 @@ func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Int
|
|||
)
|
||||
},
|
||||
newValue,
|
||||
currentSchema,
|
||||
currentSchema.Body,
|
||||
)
|
||||
diags = diags.Append(writeOnlyDiags)
|
||||
|
||||
|
|
@ -140,7 +139,7 @@ func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Int
|
|||
return nil, diags
|
||||
}
|
||||
|
||||
new, err := src.CompleteUpgrade(newValue, currentSchema.ImpliedType(), uint64(currentVersion))
|
||||
new, err := src.CompleteUpgrade(newValue, currentSchema.Body.ImpliedType(), uint64(currentSchema.Version))
|
||||
if err != nil {
|
||||
// We already checked for type conformance above, so getting into this
|
||||
// codepath should be rare and is probably a bug somewhere under CompleteUpgrade.
|
||||
|
|
@ -153,6 +152,83 @@ func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Int
|
|||
return new, diags
|
||||
}
|
||||
|
||||
func upgradeResourceIdentity(addr addrs.AbsResourceInstance, provider providers.Interface, src *states.ResourceInstanceObjectSrc, currentSchema providers.Schema) (*states.ResourceInstanceObjectSrc, tfdiags.Diagnostics) {
|
||||
// TODO: This should eventually use a proper FQN.
|
||||
providerType := addr.Resource.Resource.ImpliedProvider()
|
||||
if src.IdentitySchemaVersion > uint64(currentSchema.IdentityVersion) {
|
||||
log.Printf("[TRACE] upgradeResourceIdentity: can't downgrade identity for %s from version %d to %d", addr, src.IdentitySchemaVersion, currentSchema.IdentityVersion)
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Resource instance managed by newer provider version",
|
||||
// This is not a very good error message, but we don't retain enough
|
||||
// information in state to give good feedback on what provider
|
||||
// version might be required here. :(
|
||||
fmt.Sprintf("The current state of %s was created by a newer provider version than is currently selected. Upgrade the %s provider to work with this state.", addr, providerType),
|
||||
))
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
// We don't need to do anything if the identity schema version is already up-to-date.
|
||||
if src.IdentitySchemaVersion == uint64(currentSchema.IdentityVersion) {
|
||||
return src, nil
|
||||
}
|
||||
|
||||
req := providers.UpgradeResourceIdentityRequest{
|
||||
TypeName: addr.Resource.Resource.Type,
|
||||
|
||||
// TODO: The internal schema version representations are all using
|
||||
// uint64 instead of int64, but unsigned integers aren't friendly
|
||||
// to all protobuf target languages so in practice we use int64
|
||||
// on the wire. In future we will change all of our internal
|
||||
// representations to int64 too.
|
||||
Version: int64(src.SchemaVersion),
|
||||
RawIdentityJSON: src.IdentityJSON,
|
||||
}
|
||||
|
||||
resp := provider.UpgradeResourceIdentity(req)
|
||||
diags := resp.Diagnostics
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
if !resp.UpgradedIdentity.IsWhollyKnown() {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid resource identity upgrade",
|
||||
fmt.Sprintf("The %s provider upgraded the identity for %s from a previous version, but produced an invalid result: The returned state contains unknown values.", providerType, addr),
|
||||
))
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
newIdentity := resp.UpgradedIdentity
|
||||
newType := newIdentity.Type()
|
||||
currentType := currentSchema.Identity.ImpliedType()
|
||||
if errs := newType.TestConformance(currentType); len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid resource identity upgrade",
|
||||
fmt.Sprintf("The %s provider upgraded the identity for %s from a previous version, but produced an invalid result: %s.", providerType, addr, tfdiags.FormatError(err)),
|
||||
))
|
||||
}
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
new, err := src.CompleteIdentityUpgrade(newIdentity, currentSchema)
|
||||
if err != nil {
|
||||
// We already checked for type conformance above, so getting into this
|
||||
// codepath should be rare and is probably a bug somewhere under CompleteUpgrade.
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to encode result of resource identity upgrade",
|
||||
fmt.Sprintf("Failed to encode state for %s after resource identity schema upgrade: %s.", addr, tfdiags.FormatError(err)),
|
||||
))
|
||||
}
|
||||
|
||||
return new, diags
|
||||
}
|
||||
|
||||
// stripRemovedStateAttributes deletes any attributes no longer present in the
|
||||
// schema, so that the json can be correctly decoded.
|
||||
func stripRemovedStateAttributes(state []byte, ty cty.Type) []byte {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/hashicorp/hcl/v2"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/langrefs"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
|
|
@ -28,20 +27,20 @@ func validateSelfRef(addr addrs.Referenceable, config hcl.Body, providerSchema p
|
|||
addrStrs = append(addrStrs, tAddr.ContainingResource().String())
|
||||
}
|
||||
|
||||
var schema *configschema.Block
|
||||
var schema providers.Schema
|
||||
switch tAddr := addr.(type) {
|
||||
case addrs.Resource:
|
||||
schema, _ = providerSchema.SchemaForResourceAddr(tAddr)
|
||||
schema = providerSchema.SchemaForResourceAddr(tAddr)
|
||||
case addrs.ResourceInstance:
|
||||
schema, _ = providerSchema.SchemaForResourceAddr(tAddr.ContainingResource())
|
||||
schema = providerSchema.SchemaForResourceAddr(tAddr.ContainingResource())
|
||||
}
|
||||
|
||||
if schema == nil {
|
||||
if schema.Body == nil {
|
||||
diags = diags.Append(fmt.Errorf("no schema available for %s to validate for self-references; this is a bug in Terraform and should be reported", addr))
|
||||
return diags
|
||||
}
|
||||
|
||||
refs, _ := langrefs.ReferencesInBlock(addrs.ParseRef, config, schema)
|
||||
refs, _ := langrefs.ReferencesInBlock(addrs.ParseRef, config, schema.Body)
|
||||
for _, ref := range refs {
|
||||
for _, addrStr := range addrStrs {
|
||||
if ref.Subject.String() == addrStr {
|
||||
|
|
|
|||
Loading…
Reference in a new issue