diff --git a/docs/plugin-protocol/tfplugin5.proto b/docs/plugin-protocol/tfplugin5.proto index d3104e36f8..b96d602482 100644 --- a/docs/plugin-protocol/tfplugin5.proto +++ b/docs/plugin-protocol/tfplugin5.proto @@ -158,6 +158,7 @@ message Schema { string description = 4; StringKind description_kind = 5; bool deprecated = 6; + string deprecation_message = 7; } message Attribute { @@ -171,6 +172,7 @@ message Schema { StringKind description_kind = 8; bool deprecated = 9; bool write_only = 10; + string deprecation_message = 11; } message NestedBlock { @@ -949,4 +951,3 @@ message ValidateActionConfig { repeated Diagnostic diagnostics = 1; } } - diff --git a/docs/plugin-protocol/tfplugin6.proto b/docs/plugin-protocol/tfplugin6.proto index fba6ecc8a7..1b2b0ed08c 100644 --- a/docs/plugin-protocol/tfplugin6.proto +++ b/docs/plugin-protocol/tfplugin6.proto @@ -158,6 +158,7 @@ message Schema { string description = 4; StringKind description_kind = 5; bool deprecated = 6; + string deprecation_message = 7; } message Attribute { @@ -172,6 +173,7 @@ message Schema { StringKind description_kind = 8; bool deprecated = 9; bool write_only = 11; + string deprecation_message = 12; } message NestedBlock { @@ -1073,4 +1075,4 @@ message ValidateActionConfig { message Response { repeated Diagnostic diagnostics = 1; } -} \ No newline at end of file +} diff --git a/internal/configs/configschema/copy.go b/internal/configs/configschema/copy.go index d4052374e2..aee1ed3cb8 100644 --- a/internal/configs/configschema/copy.go +++ b/internal/configs/configschema/copy.go @@ -6,9 +6,10 @@ package configschema // DeepCopy returns a deep copy of the schema. func (b *Block) DeepCopy() *Block { block := &Block{ - Description: b.Description, - DescriptionKind: b.DescriptionKind, - Deprecated: b.Deprecated, + Description: b.Description, + DescriptionKind: b.DescriptionKind, + Deprecated: b.Deprecated, + DeprecationMessage: b.DeprecationMessage, } if b.Attributes != nil { @@ -37,14 +38,15 @@ func (b *Block) DeepCopy() *Block { // DeepCopy returns a deep copy of the schema. func (a *Attribute) DeepCopy() *Attribute { attr := &Attribute{ - Type: a.Type, - Description: a.Description, - DescriptionKind: a.DescriptionKind, - Deprecated: a.Deprecated, - Required: a.Required, - Computed: a.Computed, - Optional: a.Optional, - Sensitive: a.Sensitive, + Type: a.Type, + Description: a.Description, + DescriptionKind: a.DescriptionKind, + Deprecated: a.Deprecated, + DeprecationMessage: a.DeprecationMessage, + Required: a.Required, + Computed: a.Computed, + Optional: a.Optional, + Sensitive: a.Sensitive, // NestedType is not copied here because it will be copied // separately if it is set. diff --git a/internal/configs/configschema/filter.go b/internal/configs/configschema/filter.go index 9ec60c0592..7b01d4550c 100644 --- a/internal/configs/configschema/filter.go +++ b/internal/configs/configschema/filter.go @@ -45,9 +45,10 @@ func (b *Block) Filter(filterAttribute FilterT[*Attribute], filterBlock FilterT[ func (b *Block) filter(path cty.Path, filterAttribute FilterT[*Attribute], filterBlock FilterT[*NestedBlock]) *Block { ret := &Block{ - Description: b.Description, - DescriptionKind: b.DescriptionKind, - Deprecated: b.Deprecated, + Description: b.Description, + DescriptionKind: b.DescriptionKind, + Deprecated: b.Deprecated, + DeprecationMessage: b.DeprecationMessage, } if b.Attributes != nil { diff --git a/internal/configs/configschema/internal_validate.go b/internal/configs/configschema/internal_validate.go index e1e68c7f18..d246f1f8ac 100644 --- a/internal/configs/configschema/internal_validate.go +++ b/internal/configs/configschema/internal_validate.go @@ -29,6 +29,10 @@ func (b *Block) InternalValidate() error { func (b *Block) internalValidate(prefix string) error { var multiErr error + if prefix == "" && !b.Deprecated && b.DeprecationMessage != "" { + multiErr = errors.Join(multiErr, fmt.Errorf("top-level block: DeprecationMessage must not be set when Deprecated is false")) + } + for name, attrS := range b.Attributes { if attrS == nil { multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: attribute schema is nil", prefix, name)) @@ -48,6 +52,9 @@ func (b *Block) internalValidate(prefix string) error { } else if !validName.MatchString(name) { multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) } + if !blockS.Deprecated && blockS.DeprecationMessage != "" { + multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: DeprecationMessage must not be set when Deprecated is false", prefix, name)) + } if blockS.MinItems < 0 || blockS.MaxItems < 0 { multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name)) @@ -126,6 +133,9 @@ func (a *Attribute) internalValidate(name, prefix string) error { if a.Computed && a.Required { err = errors.Join(err, fmt.Errorf("%s%s: cannot set both Computed and Required", prefix, name)) } + if !a.Deprecated && a.DeprecationMessage != "" { + err = errors.Join(err, fmt.Errorf("%s%s: DeprecationMessage must not be set when Deprecated is false", prefix, name)) + } if a.Type == cty.NilType && a.NestedType == nil { err = errors.Join(err, fmt.Errorf("%s%s: either Type or NestedType must be defined", prefix, name)) diff --git a/internal/configs/configschema/internal_validate_test.go b/internal/configs/configschema/internal_validate_test.go index fa2bb68911..80c88e828a 100644 --- a/internal/configs/configschema/internal_validate_test.go +++ b/internal/configs/configschema/internal_validate_test.go @@ -122,6 +122,18 @@ func TestBlockInternalValidate(t *testing.T) { }, []string{}, }, + "attribute with deprecation message but not deprecated": { + &Block{ + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Optional: true, + DeprecationMessage: "use bar instead", + }, + }, + }, + []string{"foo: DeprecationMessage must not be set when Deprecated is false"}, + }, "attribute with missing type": { &Block{ Attributes: map[string]*Attribute{ @@ -209,6 +221,25 @@ func TestBlockInternalValidate(t *testing.T) { }, []string{"bad.nested_bad: cannot set both Optional and Required"}, }, + "nested block with deprecation message but not deprecated": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "bad": { + Nesting: NestingSingle, + Block: Block{ + DeprecationMessage: "use good instead", + }, + }, + }, + }, + []string{"bad: DeprecationMessage must not be set when Deprecated is false"}, + }, + "top-level block with deprecation message but not deprecated": { + &Block{ + DeprecationMessage: "use another resource instead", + }, + []string{"top-level block: DeprecationMessage must not be set when Deprecated is false"}, + }, "nested list block with dynamically-typed attribute": { &Block{ BlockTypes: map[string]*NestedBlock{ diff --git a/internal/configs/configschema/schema.go b/internal/configs/configschema/schema.go index 599d5cd278..b4788d7b7e 100644 --- a/internal/configs/configschema/schema.go +++ b/internal/configs/configschema/schema.go @@ -36,6 +36,10 @@ type Block struct { DescriptionKind StringKind Deprecated bool + + // DeprecationMessage is a human-readable message explaining the deprecation. + // This is valid only when Deprecated is true. + DeprecationMessage string } // Attribute represents a configuration attribute, within a block. @@ -79,6 +83,10 @@ type Attribute struct { Deprecated bool + // DeprecationMessage is a human-readable message explaining the deprecation. + // This is valid only when Deprecated is true. + DeprecationMessage string + // WriteOnly, if set to true, indicates that the attribute is not presisted // in the state. WriteOnly bool diff --git a/internal/deprecation/schema.go b/internal/deprecation/schema.go index d701578fde..07f85da2ea 100644 --- a/internal/deprecation/schema.go +++ b/internal/deprecation/schema.go @@ -25,7 +25,7 @@ func MarkDeprecatedValues(val cty.Value, schema *configschema.Block, origin stri // Check if the block is deprecated if schema.Deprecated { - newVal = newVal.Mark(marks.NewDeprecation("Deprecated resource used as value", origin)) + newVal = newVal.Mark(marks.NewDeprecation(schemaDeprecationMessage(schema), origin)) } if !newVal.IsKnown() { @@ -40,12 +40,12 @@ func MarkDeprecatedValues(val cty.Value, schema *configschema.Block, origin stri attr := schema.AttributeByPath(p) if attr != nil && attr.Deprecated { - v = v.Mark(marks.NewDeprecation(fmt.Sprintf("Deprecated resource attribute %q used", strings.TrimPrefix(format.CtyPath(p), ".")), fmt.Sprintf("%s%s", origin, format.CtyPath(p)))) + v = v.Mark(marks.NewDeprecation(attributeDeprecationMessage(attr, p), fmt.Sprintf("%s%s", origin, format.CtyPath(p)))) } block := schema.BlockByPath(p) if block != nil && block.Deprecated { - v = v.Mark(marks.NewDeprecation(fmt.Sprintf("Deprecated resource block %q used", strings.TrimPrefix(format.CtyPath(p), ".")), fmt.Sprintf("%s%s", origin, format.CtyPath(p)))) + v = v.Mark(marks.NewDeprecation(blockDeprecationMessage(block, p), fmt.Sprintf("%s%s", origin, format.CtyPath(p)))) } return v, nil @@ -54,3 +54,24 @@ func MarkDeprecatedValues(val cty.Value, schema *configschema.Block, origin stri return newVal } + +func schemaDeprecationMessage(schema *configschema.Block) string { + if schema.DeprecationMessage != "" { + return schema.DeprecationMessage + } + return "Deprecated resource used as value" +} + +func attributeDeprecationMessage(attr *configschema.Attribute, path cty.Path) string { + if attr.DeprecationMessage != "" { + return attr.DeprecationMessage + } + return fmt.Sprintf("Deprecated resource attribute %q used", strings.TrimPrefix(format.CtyPath(path), ".")) +} + +func blockDeprecationMessage(block *configschema.Block, path cty.Path) string { + if block.DeprecationMessage != "" { + return block.DeprecationMessage + } + return fmt.Sprintf("Deprecated resource block %q used", strings.TrimPrefix(format.CtyPath(path), ".")) +} diff --git a/internal/deprecation/schema_test.go b/internal/deprecation/schema_test.go index e4863a9865..7d833ce01d 100644 --- a/internal/deprecation/schema_test.go +++ b/internal/deprecation/schema_test.go @@ -6,11 +6,26 @@ package deprecation import ( "testing" + "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/zclconf/go-cty/cty" ) +func deprecationMarkAtPath(pathMarks []cty.PathValueMarks, path cty.Path) (marks.DeprecationMark, bool) { + for _, pvm := range pathMarks { + if !pvm.Path.Equals(path) { + continue + } + for mark := range pvm.Marks { + if depMark, ok := mark.(marks.DeprecationMark); ok { + return depMark, true + } + } + } + return marks.DeprecationMark{}, false +} + func TestMarkDeprecatedValues_NilSchema(t *testing.T) { val := cty.ObjectVal(map[string]cty.Value{ "foo": cty.StringVal("bar"), @@ -60,6 +75,245 @@ func TestMarkDeprecatedValues_NoDeprecations(t *testing.T) { } } +func TestMarkDeprecatedValues_DeprecationMessages(t *testing.T) { + tests := map[string]struct { + schema *configschema.Block + val cty.Value + path cty.Path + expectedMessage string + }{ + "resource without message": { + schema: &configschema.Block{ + Deprecated: true, + }, + val: cty.ObjectVal(map[string]cty.Value{}), + path: cty.Path{}, + expectedMessage: "Deprecated resource used as value", + }, + "resource with message": { + schema: &configschema.Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated, use test_new_resource", + }, + val: cty.ObjectVal(map[string]cty.Value{}), + path: cty.Path{}, + expectedMessage: "resource is deprecated, use test_new_resource", + }, + "attribute without message": { + schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + Deprecated: true, + }, + }, + }, + val: cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("bar")}), + path: cty.GetAttrPath("foo"), + expectedMessage: `Deprecated resource attribute "foo" used`, + }, + "attribute with message": { + schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + Deprecated: true, + DeprecationMessage: "foo is deprecated, use bar", + }, + }, + }, + val: cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("bar")}), + path: cty.GetAttrPath("foo"), + expectedMessage: "foo is deprecated, use bar", + }, + "block without message": { + schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Deprecated: true, + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + }, + }, + }, + val: cty.ObjectVal(map[string]cty.Value{ + "nested": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + }), + path: cty.GetAttrPath("nested"), + expectedMessage: `Deprecated resource block "nested" used`, + }, + "block with message": { + schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + }, + }, + }, + val: cty.ObjectVal(map[string]cty.Value{ + "nested": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + }), + path: cty.GetAttrPath("nested"), + expectedMessage: "nested block is deprecated", + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + result := MarkDeprecatedValues(test.val, test.schema, "origin") + _, pathMarks := result.UnmarkDeepWithPaths() + + depMark, ok := deprecationMarkAtPath(pathMarks, test.path) + if !ok { + t.Fatalf("expected deprecation mark at path %#v", test.path) + } + if depMark.Message != test.expectedMessage { + t.Fatalf("wrong deprecation message %q; want %q", depMark.Message, test.expectedMessage) + } + }) + } +} + +func TestMarkDeprecatedValues_DiagnosticMessages(t *testing.T) { + tests := map[string]struct { + schema *configschema.Block + val cty.Value + expectedMessage string + }{ + "resource without message": { + schema: &configschema.Block{ + Deprecated: true, + }, + val: cty.ObjectVal(map[string]cty.Value{}), + expectedMessage: "Deprecated resource used as value", + }, + "resource with message": { + schema: &configschema.Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated, use test_new_resource", + }, + val: cty.ObjectVal(map[string]cty.Value{}), + expectedMessage: "resource is deprecated, use test_new_resource", + }, + "attribute without message": { + schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + Deprecated: true, + }, + }, + }, + val: cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("bar")}), + expectedMessage: `Deprecated resource attribute "foo" used`, + }, + "attribute with message": { + schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + Deprecated: true, + DeprecationMessage: "foo is deprecated, use bar", + }, + }, + }, + val: cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("bar")}), + expectedMessage: "foo is deprecated, use bar", + }, + "block without message": { + schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Deprecated: true, + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + }, + }, + }, + val: cty.ObjectVal(map[string]cty.Value{ + "nested": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + }), + expectedMessage: `Deprecated resource block "nested" used`, + }, + "block with message": { + schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + }, + }, + }, + val: cty.ObjectVal(map[string]cty.Value{ + "nested": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + }), + expectedMessage: "nested block is deprecated", + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + marked := MarkDeprecatedValues(test.val, test.schema, "origin") + _, diags := NewDeprecations().ValidateConfig(marked, test.schema, addrs.RootModule) + warnings := diags.Warnings() + + if len(warnings) != 1 { + t.Fatalf("expected exactly one warning, got %d", len(warnings)) + } + + got := warnings[0].Description().Detail + if got != test.expectedMessage { + t.Fatalf("wrong deprecation message %q; want %q", got, test.expectedMessage) + } + }) + } +} + func TestMarkDeprecatedValues_DeprecatedBlock(t *testing.T) { schema := &configschema.Block{ Deprecated: true, diff --git a/internal/plugin/convert/schema.go b/internal/plugin/convert/schema.go index f4747ba61d..a48b0a9494 100644 --- a/internal/plugin/convert/schema.go +++ b/internal/plugin/convert/schema.go @@ -18,24 +18,26 @@ import ( // proto.Schema_Block for a grpc response. func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block { block := &proto.Schema_Block{ - Description: b.Description, - DescriptionKind: protoStringKind(b.DescriptionKind), - Deprecated: b.Deprecated, + Description: b.Description, + DescriptionKind: protoStringKind(b.DescriptionKind), + Deprecated: b.Deprecated, + DeprecationMessage: b.DeprecationMessage, } for _, name := range sortedKeys(b.Attributes) { a := b.Attributes[name] attr := &proto.Schema_Attribute{ - Name: name, - Description: a.Description, - DescriptionKind: protoStringKind(a.DescriptionKind), - Optional: a.Optional, - Computed: a.Computed, - Required: a.Required, - Sensitive: a.Sensitive, - Deprecated: a.Deprecated, - WriteOnly: a.WriteOnly, + Name: name, + Description: a.Description, + DescriptionKind: protoStringKind(a.DescriptionKind), + Optional: a.Optional, + Computed: a.Computed, + Required: a.Required, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + DeprecationMessage: a.DeprecationMessage, + WriteOnly: a.WriteOnly, } ty, err := json.Marshal(a.Type) @@ -158,21 +160,23 @@ func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block { Attributes: make(map[string]*configschema.Attribute), BlockTypes: make(map[string]*configschema.NestedBlock), - Description: b.Description, - DescriptionKind: schemaStringKind(b.DescriptionKind), - Deprecated: b.Deprecated, + Description: b.Description, + DescriptionKind: schemaStringKind(b.DescriptionKind), + Deprecated: b.Deprecated, + DeprecationMessage: b.DeprecationMessage, } for _, a := range b.Attributes { attr := &configschema.Attribute{ - Description: a.Description, - DescriptionKind: schemaStringKind(a.DescriptionKind), - Required: a.Required, - Optional: a.Optional, - Computed: a.Computed, - Sensitive: a.Sensitive, - Deprecated: a.Deprecated, - WriteOnly: a.WriteOnly, + Description: a.Description, + DescriptionKind: schemaStringKind(a.DescriptionKind), + Required: a.Required, + Optional: a.Optional, + Computed: a.Computed, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + DeprecationMessage: a.DeprecationMessage, + WriteOnly: a.WriteOnly, } if err := json.Unmarshal(a.Type, &attr.Type); err != nil { diff --git a/internal/plugin/convert/schema_test.go b/internal/plugin/convert/schema_test.go index b71dbdc796..eddb67822a 100644 --- a/internal/plugin/convert/schema_test.go +++ b/internal/plugin/convert/schema_test.go @@ -180,6 +180,52 @@ func TestConvertSchemaBlocks(t *testing.T) { }, }, }, + "deprecation messages": { + &proto.Schema_Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated", + Attributes: []*proto.Schema_Attribute{ + { + Name: "foo", + Type: []byte(`"string"`), + Optional: true, + Deprecated: true, + DeprecationMessage: "attribute is deprecated", + }, + }, + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "nested", + Nesting: proto.Schema_NestedBlock_SINGLE, + Block: &proto.Schema_Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + }, + }, + }, + }, + &configschema.Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated", + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + Deprecated: true, + DeprecationMessage: "attribute is deprecated", + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + }, + }, + }, + }, + }, } for name, tc := range tests { @@ -352,6 +398,52 @@ func TestConvertProtoSchemaBlocks(t *testing.T) { }, }, }, + "deprecation messages": { + &proto.Schema_Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated", + Attributes: []*proto.Schema_Attribute{ + { + Name: "foo", + Type: []byte(`"string"`), + Optional: true, + Deprecated: true, + DeprecationMessage: "attribute is deprecated", + }, + }, + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "nested", + Nesting: proto.Schema_NestedBlock_SINGLE, + Block: &proto.Schema_Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + }, + }, + }, + }, + &configschema.Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated", + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + Deprecated: true, + DeprecationMessage: "attribute is deprecated", + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + }, + }, + }, + }, + }, } for name, tc := range tests { diff --git a/internal/plugin6/convert/schema.go b/internal/plugin6/convert/schema.go index ed23208b2c..123c3b2511 100644 --- a/internal/plugin6/convert/schema.go +++ b/internal/plugin6/convert/schema.go @@ -19,24 +19,26 @@ import ( // proto.Schema_Block for a grpc response. func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block { block := &proto.Schema_Block{ - Description: b.Description, - DescriptionKind: protoStringKind(b.DescriptionKind), - Deprecated: b.Deprecated, + Description: b.Description, + DescriptionKind: protoStringKind(b.DescriptionKind), + Deprecated: b.Deprecated, + DeprecationMessage: b.DeprecationMessage, } for _, name := range sortedKeys(b.Attributes) { a := b.Attributes[name] attr := &proto.Schema_Attribute{ - Name: name, - Description: a.Description, - DescriptionKind: protoStringKind(a.DescriptionKind), - Optional: a.Optional, - Computed: a.Computed, - Required: a.Required, - Sensitive: a.Sensitive, - Deprecated: a.Deprecated, - WriteOnly: a.WriteOnly, + Name: name, + Description: a.Description, + DescriptionKind: protoStringKind(a.DescriptionKind), + Optional: a.Optional, + Computed: a.Computed, + Required: a.Required, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + DeprecationMessage: a.DeprecationMessage, + WriteOnly: a.WriteOnly, } if a.Type != cty.NilType { @@ -189,21 +191,23 @@ func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block { Attributes: make(map[string]*configschema.Attribute), BlockTypes: make(map[string]*configschema.NestedBlock), - Description: b.Description, - DescriptionKind: schemaStringKind(b.DescriptionKind), - Deprecated: b.Deprecated, + Description: b.Description, + DescriptionKind: schemaStringKind(b.DescriptionKind), + Deprecated: b.Deprecated, + DeprecationMessage: b.DeprecationMessage, } for _, a := range b.Attributes { attr := &configschema.Attribute{ - Description: a.Description, - DescriptionKind: schemaStringKind(a.DescriptionKind), - Required: a.Required, - Optional: a.Optional, - Computed: a.Computed, - Sensitive: a.Sensitive, - Deprecated: a.Deprecated, - WriteOnly: a.WriteOnly, + Description: a.Description, + DescriptionKind: schemaStringKind(a.DescriptionKind), + Required: a.Required, + Optional: a.Optional, + Computed: a.Computed, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + DeprecationMessage: a.DeprecationMessage, + WriteOnly: a.WriteOnly, } if a.Type != nil { @@ -287,14 +291,15 @@ func protoObjectToConfigSchema(b *proto.Schema_Object) *configschema.Object { for _, a := range b.Attributes { attr := &configschema.Attribute{ - Description: a.Description, - DescriptionKind: schemaStringKind(a.DescriptionKind), - Required: a.Required, - Optional: a.Optional, - Computed: a.Computed, - Sensitive: a.Sensitive, - Deprecated: a.Deprecated, - WriteOnly: a.WriteOnly, + Description: a.Description, + DescriptionKind: schemaStringKind(a.DescriptionKind), + Required: a.Required, + Optional: a.Optional, + Computed: a.Computed, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + DeprecationMessage: a.DeprecationMessage, + WriteOnly: a.WriteOnly, } if a.Type != nil { @@ -349,15 +354,16 @@ func configschemaObjectToProto(b *configschema.Object) *proto.Schema_Object { for _, name := range sortedKeys(b.Attributes) { a := b.Attributes[name] attr := &proto.Schema_Attribute{ - Name: name, - Description: a.Description, - DescriptionKind: protoStringKind(a.DescriptionKind), - Optional: a.Optional, - Computed: a.Computed, - Required: a.Required, - Sensitive: a.Sensitive, - Deprecated: a.Deprecated, - WriteOnly: a.WriteOnly, + Name: name, + Description: a.Description, + DescriptionKind: protoStringKind(a.DescriptionKind), + Optional: a.Optional, + Computed: a.Computed, + Required: a.Required, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + DeprecationMessage: a.DeprecationMessage, + WriteOnly: a.WriteOnly, } if a.Type != cty.NilType { diff --git a/internal/plugin6/convert/schema_test.go b/internal/plugin6/convert/schema_test.go index a62d8d0b9a..da9d6fd3b3 100644 --- a/internal/plugin6/convert/schema_test.go +++ b/internal/plugin6/convert/schema_test.go @@ -396,6 +396,52 @@ func TestConvertSchemaBlocks(t *testing.T) { }, }, }, + "deprecation messages": { + &proto.Schema_Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated", + Attributes: []*proto.Schema_Attribute{ + { + Name: "foo", + Type: []byte(`"string"`), + Optional: true, + Deprecated: true, + DeprecationMessage: "attribute is deprecated", + }, + }, + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "nested", + Nesting: proto.Schema_NestedBlock_SINGLE, + Block: &proto.Schema_Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + }, + }, + }, + }, + &configschema.Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated", + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + Deprecated: true, + DeprecationMessage: "attribute is deprecated", + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + }, + }, + }, + }, + }, } for name, tc := range tests { @@ -614,6 +660,52 @@ func TestConvertProtoSchemaBlocks(t *testing.T) { }, }, }, + "deprecation messages": { + &proto.Schema_Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated", + Attributes: []*proto.Schema_Attribute{ + { + Name: "foo", + Type: []byte(`"string"`), + Optional: true, + Deprecated: true, + DeprecationMessage: "attribute is deprecated", + }, + }, + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "nested", + Nesting: proto.Schema_NestedBlock_SINGLE, + Block: &proto.Schema_Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + }, + }, + }, + }, + &configschema.Block{ + Deprecated: true, + DeprecationMessage: "resource is deprecated", + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + Deprecated: true, + DeprecationMessage: "attribute is deprecated", + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated", + }, + }, + }, + }, + }, } for name, tc := range tests { diff --git a/internal/terraform/context_validate_test.go b/internal/terraform/context_validate_test.go index a63a480dec..109b62eedc 100644 --- a/internal/terraform/context_validate_test.go +++ b/internal/terraform/context_validate_test.go @@ -3000,6 +3000,149 @@ output "a" { })) } +func TestContext2Validate_deprecated_resource_message(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_resource" "test" { + attr = "value" +} +output "a" { + value = test_resource.test.attr # WARNING +} +`, + }) + p := new(testing_provider.MockProvider) + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_resource": { + Deprecated: true, + DeprecationMessage: "test_resource is deprecated, use test_resource_v2", + Attributes: map[string]*configschema.Attribute{ + "attr": { + Type: cty.String, + Computed: true, + }, + }, + }, + }, + }) + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + diags := ctx.Validate(m, &ValidateOpts{}) + tfdiags.AssertDiagnosticsMatch(t, diags, tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: "Deprecated value used", + Detail: "test_resource is deprecated, use test_resource_v2", + Subject: &hcl.Range{ + Filename: filepath.Join(m.Module.SourceDir, "main.tf"), + Start: hcl.Pos{Line: 6, Column: 13, Byte: 81}, + End: hcl.Pos{Line: 6, Column: 36, Byte: 104}, + }, + })) +} + +func TestContext2Validate_deprecated_attribute_message(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_resource" "test" { +} +output "a" { + value = test_resource.test.attr +} +`, + }) + + p := new(testing_provider.MockProvider) + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_resource": { + Attributes: map[string]*configschema.Attribute{ + "attr": { + Type: cty.String, + Computed: true, + Deprecated: true, + DeprecationMessage: "attr is deprecated, use new_attr", + }, + }, + }, + }, + }) + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + diags := ctx.Validate(m, &ValidateOpts{}) + tfdiags.AssertDiagnosticsMatch(t, diags, tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: "Deprecated value used", + Detail: "attr is deprecated, use new_attr", + Subject: &hcl.Range{ + Filename: filepath.Join(m.Module.SourceDir, "main.tf"), + Start: hcl.Pos{Line: 5, Column: 13, Byte: 62}, + End: hcl.Pos{Line: 5, Column: 36, Byte: 85}, + }, + })) +} + +func TestContext2Validate_deprecated_block_message(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_resource" "test" { +} +output "a" { + value = test_resource.test.nested +} +`, + }) + + p := new(testing_provider.MockProvider) + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_resource": { + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Deprecated: true, + DeprecationMessage: "nested block is deprecated, use replacement_nested", + Attributes: map[string]*configschema.Attribute{ + "value": { + Type: cty.String, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }) + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + diags := ctx.Validate(m, &ValidateOpts{}) + tfdiags.AssertDiagnosticsMatch(t, diags, tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: "Deprecated value used", + Detail: "nested block is deprecated, use replacement_nested", + Subject: &hcl.Range{ + Filename: filepath.Join(m.Module.SourceDir, "main.tf"), + Start: hcl.Pos{Line: 5, Column: 13, Byte: 62}, + End: hcl.Pos{Line: 5, Column: 38, Byte: 87}, + }, + })) +} + func TestContext2Validate_unknownForEach(t *testing.T) { p := testProvider("aws") m := testModuleInline(t, map[string]string{ diff --git a/internal/tfplugin5/tfplugin5.pb.go b/internal/tfplugin5/tfplugin5.pb.go index 52e39a6c63..b0a151dc2b 100644 --- a/internal/tfplugin5/tfplugin5.pb.go +++ b/internal/tfplugin5/tfplugin5.pb.go @@ -2428,15 +2428,16 @@ func (x *ResourceIdentitySchema_IdentityAttribute) GetDescription() string { } type Schema_Block struct { - state protoimpl.MessageState `protogen:"open.v1"` - Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Attributes []*Schema_Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"` - BlockTypes []*Schema_NestedBlock `protobuf:"bytes,3,rep,name=block_types,json=blockTypes,proto3" json:"block_types,omitempty"` - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` - DescriptionKind StringKind `protobuf:"varint,5,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin5.StringKind" json:"description_kind,omitempty"` - Deprecated bool `protobuf:"varint,6,opt,name=deprecated,proto3" json:"deprecated,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Attributes []*Schema_Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"` + BlockTypes []*Schema_NestedBlock `protobuf:"bytes,3,rep,name=block_types,json=blockTypes,proto3" json:"block_types,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + DescriptionKind StringKind `protobuf:"varint,5,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin5.StringKind" json:"description_kind,omitempty"` + Deprecated bool `protobuf:"varint,6,opt,name=deprecated,proto3" json:"deprecated,omitempty"` + DeprecationMessage string `protobuf:"bytes,7,opt,name=deprecation_message,json=deprecationMessage,proto3" json:"deprecation_message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Schema_Block) Reset() { @@ -2511,20 +2512,28 @@ func (x *Schema_Block) GetDeprecated() bool { return false } +func (x *Schema_Block) GetDeprecationMessage() string { + if x != nil { + return x.DeprecationMessage + } + return "" +} + type Schema_Attribute struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Type []byte `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` - Optional bool `protobuf:"varint,5,opt,name=optional,proto3" json:"optional,omitempty"` - Computed bool `protobuf:"varint,6,opt,name=computed,proto3" json:"computed,omitempty"` - Sensitive bool `protobuf:"varint,7,opt,name=sensitive,proto3" json:"sensitive,omitempty"` - DescriptionKind StringKind `protobuf:"varint,8,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin5.StringKind" json:"description_kind,omitempty"` - Deprecated bool `protobuf:"varint,9,opt,name=deprecated,proto3" json:"deprecated,omitempty"` - WriteOnly bool `protobuf:"varint,10,opt,name=write_only,json=writeOnly,proto3" json:"write_only,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type []byte `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` + Optional bool `protobuf:"varint,5,opt,name=optional,proto3" json:"optional,omitempty"` + Computed bool `protobuf:"varint,6,opt,name=computed,proto3" json:"computed,omitempty"` + Sensitive bool `protobuf:"varint,7,opt,name=sensitive,proto3" json:"sensitive,omitempty"` + DescriptionKind StringKind `protobuf:"varint,8,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin5.StringKind" json:"description_kind,omitempty"` + Deprecated bool `protobuf:"varint,9,opt,name=deprecated,proto3" json:"deprecated,omitempty"` + WriteOnly bool `protobuf:"varint,10,opt,name=write_only,json=writeOnly,proto3" json:"write_only,omitempty"` + DeprecationMessage string `protobuf:"bytes,11,opt,name=deprecation_message,json=deprecationMessage,proto3" json:"deprecation_message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Schema_Attribute) Reset() { @@ -2627,6 +2636,13 @@ func (x *Schema_Attribute) GetWriteOnly() bool { return false } +func (x *Schema_Attribute) GetDeprecationMessage() string { + if x != nil { + return x.DeprecationMessage + } + return "" +} + type Schema_NestedBlock struct { state protoimpl.MessageState `protogen:"open.v1"` TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` @@ -6980,10 +6996,10 @@ const file_tfplugin5_proto_rawDesc = "" + "\x14ResourceIdentityData\x12<\n" + "\ridentity_data\x18\x01 \x01(\v2\x17.tfplugin5.DynamicValueR\fidentityData\"9\n" + "\fActionSchema\x12)\n" + - "\x06schema\x18\x01 \x01(\v2\x11.tfplugin5.SchemaR\x06schema\"\xeb\a\n" + + "\x06schema\x18\x01 \x01(\v2\x11.tfplugin5.SchemaR\x06schema\"\xcd\b\n" + "\x06Schema\x12\x18\n" + "\aversion\x18\x01 \x01(\x03R\aversion\x12-\n" + - "\x05block\x18\x02 \x01(\v2\x17.tfplugin5.Schema.BlockR\x05block\x1a\xa2\x02\n" + + "\x05block\x18\x02 \x01(\v2\x17.tfplugin5.Schema.BlockR\x05block\x1a\xd3\x02\n" + "\x05Block\x12\x18\n" + "\aversion\x18\x01 \x01(\x03R\aversion\x12;\n" + "\n" + @@ -6995,7 +7011,8 @@ const file_tfplugin5_proto_rawDesc = "" + "\x10description_kind\x18\x05 \x01(\x0e2\x15.tfplugin5.StringKindR\x0fdescriptionKind\x12\x1e\n" + "\n" + "deprecated\x18\x06 \x01(\bR\n" + - "deprecated\x1a\xc8\x02\n" + + "deprecated\x12/\n" + + "\x13deprecation_message\x18\a \x01(\tR\x12deprecationMessage\x1a\xf9\x02\n" + "\tAttribute\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + "\x04type\x18\x02 \x01(\fR\x04type\x12 \n" + @@ -7010,7 +7027,8 @@ const file_tfplugin5_proto_rawDesc = "" + "deprecated\x12\x1d\n" + "\n" + "write_only\x18\n" + - " \x01(\bR\twriteOnly\x1a\xa7\x02\n" + + " \x01(\bR\twriteOnly\x12/\n" + + "\x13deprecation_message\x18\v \x01(\tR\x12deprecationMessage\x1a\xa7\x02\n" + "\vNestedBlock\x12\x1b\n" + "\ttype_name\x18\x01 \x01(\tR\btypeName\x12-\n" + "\x05block\x18\x02 \x01(\v2\x17.tfplugin5.Schema.BlockR\x05block\x12C\n" + diff --git a/internal/tfplugin6/tfplugin6.pb.go b/internal/tfplugin6/tfplugin6.pb.go index 1e25f7aaea..e0df6a375b 100644 --- a/internal/tfplugin6/tfplugin6.pb.go +++ b/internal/tfplugin6/tfplugin6.pb.go @@ -2859,15 +2859,16 @@ func (x *ResourceIdentitySchema_IdentityAttribute) GetDescription() string { } type Schema_Block struct { - state protoimpl.MessageState `protogen:"open.v1"` - Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Attributes []*Schema_Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"` - BlockTypes []*Schema_NestedBlock `protobuf:"bytes,3,rep,name=block_types,json=blockTypes,proto3" json:"block_types,omitempty"` - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` - DescriptionKind StringKind `protobuf:"varint,5,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin6.StringKind" json:"description_kind,omitempty"` - Deprecated bool `protobuf:"varint,6,opt,name=deprecated,proto3" json:"deprecated,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Attributes []*Schema_Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"` + BlockTypes []*Schema_NestedBlock `protobuf:"bytes,3,rep,name=block_types,json=blockTypes,proto3" json:"block_types,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + DescriptionKind StringKind `protobuf:"varint,5,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin6.StringKind" json:"description_kind,omitempty"` + Deprecated bool `protobuf:"varint,6,opt,name=deprecated,proto3" json:"deprecated,omitempty"` + DeprecationMessage string `protobuf:"bytes,7,opt,name=deprecation_message,json=deprecationMessage,proto3" json:"deprecation_message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Schema_Block) Reset() { @@ -2942,21 +2943,29 @@ func (x *Schema_Block) GetDeprecated() bool { return false } +func (x *Schema_Block) GetDeprecationMessage() string { + if x != nil { + return x.DeprecationMessage + } + return "" +} + type Schema_Attribute struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Type []byte `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` - NestedType *Schema_Object `protobuf:"bytes,10,opt,name=nested_type,json=nestedType,proto3" json:"nested_type,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` - Optional bool `protobuf:"varint,5,opt,name=optional,proto3" json:"optional,omitempty"` - Computed bool `protobuf:"varint,6,opt,name=computed,proto3" json:"computed,omitempty"` - Sensitive bool `protobuf:"varint,7,opt,name=sensitive,proto3" json:"sensitive,omitempty"` - DescriptionKind StringKind `protobuf:"varint,8,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin6.StringKind" json:"description_kind,omitempty"` - Deprecated bool `protobuf:"varint,9,opt,name=deprecated,proto3" json:"deprecated,omitempty"` - WriteOnly bool `protobuf:"varint,11,opt,name=write_only,json=writeOnly,proto3" json:"write_only,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type []byte `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + NestedType *Schema_Object `protobuf:"bytes,10,opt,name=nested_type,json=nestedType,proto3" json:"nested_type,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` + Optional bool `protobuf:"varint,5,opt,name=optional,proto3" json:"optional,omitempty"` + Computed bool `protobuf:"varint,6,opt,name=computed,proto3" json:"computed,omitempty"` + Sensitive bool `protobuf:"varint,7,opt,name=sensitive,proto3" json:"sensitive,omitempty"` + DescriptionKind StringKind `protobuf:"varint,8,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin6.StringKind" json:"description_kind,omitempty"` + Deprecated bool `protobuf:"varint,9,opt,name=deprecated,proto3" json:"deprecated,omitempty"` + WriteOnly bool `protobuf:"varint,11,opt,name=write_only,json=writeOnly,proto3" json:"write_only,omitempty"` + DeprecationMessage string `protobuf:"bytes,12,opt,name=deprecation_message,json=deprecationMessage,proto3" json:"deprecation_message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Schema_Attribute) Reset() { @@ -3066,6 +3075,13 @@ func (x *Schema_Attribute) GetWriteOnly() bool { return false } +func (x *Schema_Attribute) GetDeprecationMessage() string { + if x != nil { + return x.DeprecationMessage + } + return "" +} + type Schema_NestedBlock struct { state protoimpl.MessageState `protogen:"open.v1"` TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` @@ -8118,11 +8134,10 @@ const file_tfplugin6_proto_rawDesc = "" + "\x14ResourceIdentityData\x12<\n" + "\ridentity_data\x18\x01 \x01(\v2\x17.tfplugin6.DynamicValueR\fidentityData\"9\n" + "\fActionSchema\x12)\n" + - "\x06schema\x18\x01 \x01(\v2\x11.tfplugin6.SchemaR\x06schema\"\xb4\n" + - "\n" + + "\x06schema\x18\x01 \x01(\v2\x11.tfplugin6.SchemaR\x06schema\"\x96\v\n" + "\x06Schema\x12\x18\n" + "\aversion\x18\x01 \x01(\x03R\aversion\x12-\n" + - "\x05block\x18\x02 \x01(\v2\x17.tfplugin6.Schema.BlockR\x05block\x1a\xa2\x02\n" + + "\x05block\x18\x02 \x01(\v2\x17.tfplugin6.Schema.BlockR\x05block\x1a\xd3\x02\n" + "\x05Block\x12\x18\n" + "\aversion\x18\x01 \x01(\x03R\aversion\x12;\n" + "\n" + @@ -8134,7 +8149,8 @@ const file_tfplugin6_proto_rawDesc = "" + "\x10description_kind\x18\x05 \x01(\x0e2\x15.tfplugin6.StringKindR\x0fdescriptionKind\x12\x1e\n" + "\n" + "deprecated\x18\x06 \x01(\bR\n" + - "deprecated\x1a\x83\x03\n" + + "deprecated\x12/\n" + + "\x13deprecation_message\x18\a \x01(\tR\x12deprecationMessage\x1a\xb4\x03\n" + "\tAttribute\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + "\x04type\x18\x02 \x01(\fR\x04type\x129\n" + @@ -8151,7 +8167,8 @@ const file_tfplugin6_proto_rawDesc = "" + "deprecated\x18\t \x01(\bR\n" + "deprecated\x12\x1d\n" + "\n" + - "write_only\x18\v \x01(\bR\twriteOnly\x1a\xa7\x02\n" + + "write_only\x18\v \x01(\bR\twriteOnly\x12/\n" + + "\x13deprecation_message\x18\f \x01(\tR\x12deprecationMessage\x1a\xa7\x02\n" + "\vNestedBlock\x12\x1b\n" + "\ttype_name\x18\x01 \x01(\tR\btypeName\x12-\n" + "\x05block\x18\x02 \x01(\v2\x17.tfplugin6.Schema.BlockR\x05block\x12C\n" +