read deprecation message for resource deprecations

prototype-expansion-moved-2
Daniel Schmidt 3 months ago
parent 77a54d4c8a
commit a2e232856b

@ -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;
}
}

@ -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;
}
}
}

@ -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.

@ -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 {

@ -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))

@ -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{

@ -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

@ -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), "."))
}

@ -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,

@ -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 {

@ -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 {

@ -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 {

@ -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 {

@ -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{

@ -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" +

@ -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" +

Loading…
Cancel
Save