Merge pull request #28201 from hashicorp/alisdair/planfile-sensitive-required-replace

Add sensitivity and required-replace fields to plan file, and expose sensitivity to JSON plan
This commit is contained in:
Alisdair McDiarmid 2021-03-29 15:08:36 -04:00 committed by GitHub
commit 340543d122
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 811 additions and 129 deletions

View File

@ -59,6 +59,7 @@ type variables map[string]*variable
type variable struct {
Default json.RawMessage `json:"default,omitempty"`
Description string `json:"description,omitempty"`
Sensitive bool `json:"sensitive,omitempty"`
}
// Resource is the representation of a resource in the config
@ -263,6 +264,7 @@ func marshalModule(c *configs.Config, schemas *terraform.Schemas, addr string) (
vars[k] = &variable{
Default: defaultValJSON,
Description: v.Description,
Sensitive: v.Sensitive,
}
}
module.Variables = vars

View File

@ -67,9 +67,23 @@ type change struct {
// or "after" is unset (respectively). For ["no-op"], the before and after
// values are identical. The "after" value will be incomplete if there are
// values within it that won't be known until after apply.
Before json.RawMessage `json:"before,omitempty"`
After json.RawMessage `json:"after,omitempty"`
Before json.RawMessage `json:"before,omitempty"`
After json.RawMessage `json:"after,omitempty"`
// AfterUnknown is an object value with similar structure to After, but
// with all unknown leaf values replaced with true, and all known leaf
// values omitted. This can be combined with After to reconstruct a full
// value after the action, including values which will only be known after
// apply.
AfterUnknown json.RawMessage `json:"after_unknown,omitempty"`
// BeforeSensitive and AfterSensitive are object values with similar
// structure to Before and After, but with all sensitive leaf values
// replaced with true, and all non-sensitive leaf values omitted. These
// objects should be combined with Before and After to prevent accidental
// display of sensitive values in user interfaces.
BeforeSensitive json.RawMessage `json:"before_sensitive,omitempty"`
AfterSensitive json.RawMessage `json:"after_sensitive,omitempty"`
}
type output struct {
@ -192,12 +206,18 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
}
var before, after []byte
var beforeSensitive, afterSensitive []byte
var afterUnknown cty.Value
if changeV.Before != cty.NilVal {
before, err = ctyjson.Marshal(changeV.Before, changeV.Before.Type())
if err != nil {
return err
}
bs := sensitiveAsBool(changeV.Before.MarkWithPaths(rc.BeforeValMarks))
beforeSensitive, err = ctyjson.Marshal(bs, bs.Type())
if err != nil {
return err
}
}
if changeV.After != cty.NilVal {
if changeV.After.IsWhollyKnown() {
@ -218,6 +238,11 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
}
afterUnknown = unknownAsBool(changeV.After)
}
as := sensitiveAsBool(changeV.After.MarkWithPaths(rc.AfterValMarks))
afterSensitive, err = ctyjson.Marshal(as, as.Type())
if err != nil {
return err
}
}
a, err := ctyjson.Marshal(afterUnknown, afterUnknown.Type())
@ -226,10 +251,12 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
}
r.Change = change{
Actions: actionString(rc.Action.String()),
Before: json.RawMessage(before),
After: json.RawMessage(after),
AfterUnknown: a,
Actions: actionString(rc.Action.String()),
Before: json.RawMessage(before),
After: json.RawMessage(after),
AfterUnknown: a,
BeforeSensitive: json.RawMessage(beforeSensitive),
AfterSensitive: json.RawMessage(afterSensitive),
}
if rc.DeposedKey != states.NotDeposed {
@ -297,13 +324,28 @@ func (p *plan) marshalOutputChanges(changes *plans.Changes) error {
}
}
// The only information we have in the plan about output sensitivity is
// a boolean which is true if the output was or is marked sensitive. As
// a result, BeforeSensitive and AfterSensitive will be identical, and
// either false or true.
outputSensitive := cty.False
if oc.Sensitive {
outputSensitive = cty.True
}
sensitive, err := ctyjson.Marshal(outputSensitive, outputSensitive.Type())
if err != nil {
return err
}
a, _ := ctyjson.Marshal(afterUnknown, afterUnknown.Type())
c := change{
Actions: actionString(oc.Action.String()),
Before: json.RawMessage(before),
After: json.RawMessage(after),
AfterUnknown: a,
Actions: actionString(oc.Action.String()),
Before: json.RawMessage(before),
After: json.RawMessage(after),
AfterUnknown: a,
BeforeSensitive: json.RawMessage(sensitive),
AfterSensitive: json.RawMessage(sensitive),
}
p.OutputChanges[oc.Addr.OutputValue.Name] = c
@ -392,6 +434,9 @@ func omitUnknowns(val cty.Value) cty.Value {
// tuple types and all mapping types are converted to object types, since we
// assume the result of this is just going to be serialized as JSON (and thus
// lose those distinctions) anyway.
//
// For map/object values, all known attribute values will be omitted instead of
// returning false, as this results in a more compact serialization.
func unknownAsBool(val cty.Value) cty.Value {
ty := val.Type()
switch {
@ -439,7 +484,9 @@ func unknownAsBool(val cty.Value) cty.Value {
for it.Next() {
k, v := it.Element()
vAsBool := unknownAsBool(v)
if !vAsBool.RawEquals(cty.False) { // all of the "false"s for known values for more compact serialization
// Omit all of the "false"s for known values for more compact
// serialization
if !vAsBool.RawEquals(cty.False) {
vals[k.AsString()] = unknownAsBool(v)
}
}
@ -455,6 +502,78 @@ func unknownAsBool(val cty.Value) cty.Value {
}
}
// recursively iterate through a marked cty.Value, replacing sensitive values
// with cty.True and non-sensitive values with cty.False.
//
// The result also normalizes some types: all sequence types are turned into
// tuple types and all mapping types are converted to object types, since we
// assume the result of this is just going to be serialized as JSON (and thus
// lose those distinctions) anyway.
//
// For map/object values, all non-sensitive attribute values will be omitted
// instead of returning false, as this results in a more compact serialization.
func sensitiveAsBool(val cty.Value) cty.Value {
if val.HasMark("sensitive") {
return cty.True
}
ty := val.Type()
switch {
case val.IsNull(), ty.IsPrimitiveType(), ty.Equals(cty.DynamicPseudoType):
return cty.False
case ty.IsListType() || ty.IsTupleType() || ty.IsSetType():
length := val.LengthInt()
if length == 0 {
// If there are no elements then we can't have sensitive values
return cty.EmptyTupleVal
}
vals := make([]cty.Value, 0, length)
it := val.ElementIterator()
for it.Next() {
_, v := it.Element()
vals = append(vals, sensitiveAsBool(v))
}
// The above transform may have changed the types of some of the
// elements, so we'll always use a tuple here in case we've now made
// different elements have different types. Our ultimate goal is to
// marshal to JSON anyway, and all of these sequence types are
// indistinguishable in JSON.
return cty.TupleVal(vals)
case ty.IsMapType() || ty.IsObjectType():
var length int
switch {
case ty.IsMapType():
length = val.LengthInt()
default:
length = len(val.Type().AttributeTypes())
}
if length == 0 {
// If there are no elements then we can't have sensitive values
return cty.EmptyObjectVal
}
vals := make(map[string]cty.Value)
it := val.ElementIterator()
for it.Next() {
k, v := it.Element()
s := sensitiveAsBool(v)
// Omit all of the "false"s for non-sensitive values for more
// compact serialization
if !s.RawEquals(cty.False) {
vals[k.AsString()] = s
}
}
// The above transform may have changed the types of some of the
// elements, so we'll always use an object here in case we've now made
// different elements have different types. Our ultimate goal is to
// marshal to JSON anyway, and all of these mapping types are
// indistinguishable in JSON.
return cty.ObjectVal(vals)
default:
// Should never happen, since the above should cover all types
panic(fmt.Sprintf("sensitiveAsBool cannot handle %#v", val))
}
}
func actionString(action string) []string {
switch {
case action == "NoOp":

View File

@ -262,3 +262,200 @@ func TestUnknownAsBool(t *testing.T) {
}
}
}
func TestSensitiveAsBool(t *testing.T) {
sensitive := "sensitive"
tests := []struct {
Input cty.Value
Want cty.Value
}{
{
cty.StringVal("hello"),
cty.False,
},
{
cty.NullVal(cty.String),
cty.False,
},
{
cty.StringVal("hello").Mark(sensitive),
cty.True,
},
{
cty.NullVal(cty.String).Mark(sensitive),
cty.True,
},
{
cty.NullVal(cty.DynamicPseudoType).Mark(sensitive),
cty.True,
},
{
cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})),
cty.False,
},
{
cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})).Mark(sensitive),
cty.True,
},
{
cty.DynamicVal,
cty.False,
},
{
cty.DynamicVal.Mark(sensitive),
cty.True,
},
{
cty.ListValEmpty(cty.String),
cty.EmptyTupleVal,
},
{
cty.ListValEmpty(cty.String).Mark(sensitive),
cty.True,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("friend").Mark(sensitive),
}),
cty.TupleVal([]cty.Value{
cty.False,
cty.True,
}),
},
{
cty.SetValEmpty(cty.String),
cty.EmptyTupleVal,
},
{
cty.SetValEmpty(cty.String).Mark(sensitive),
cty.True,
},
{
cty.SetVal([]cty.Value{cty.StringVal("hello")}),
cty.TupleVal([]cty.Value{cty.False}),
},
{
cty.SetVal([]cty.Value{cty.StringVal("hello").Mark(sensitive)}),
cty.True,
},
{
cty.EmptyTupleVal.Mark(sensitive),
cty.True,
},
{
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("friend").Mark(sensitive),
}),
cty.TupleVal([]cty.Value{
cty.False,
cty.True,
}),
},
{
cty.MapValEmpty(cty.String),
cty.EmptyObjectVal,
},
{
cty.MapValEmpty(cty.String).Mark(sensitive),
cty.True,
},
{
cty.MapVal(map[string]cty.Value{
"greeting": cty.StringVal("hello"),
"animal": cty.StringVal("horse"),
}),
cty.EmptyObjectVal,
},
{
cty.MapVal(map[string]cty.Value{
"greeting": cty.StringVal("hello"),
"animal": cty.StringVal("horse").Mark(sensitive),
}),
cty.ObjectVal(map[string]cty.Value{
"animal": cty.True,
}),
},
{
cty.MapVal(map[string]cty.Value{
"greeting": cty.StringVal("hello"),
"animal": cty.StringVal("horse").Mark(sensitive),
}).Mark(sensitive),
cty.True,
},
{
cty.EmptyObjectVal,
cty.EmptyObjectVal,
},
{
cty.ObjectVal(map[string]cty.Value{
"greeting": cty.StringVal("hello"),
"animal": cty.StringVal("horse"),
}),
cty.EmptyObjectVal,
},
{
cty.ObjectVal(map[string]cty.Value{
"greeting": cty.StringVal("hello"),
"animal": cty.StringVal("horse").Mark(sensitive),
}),
cty.ObjectVal(map[string]cty.Value{
"animal": cty.True,
}),
},
{
cty.ObjectVal(map[string]cty.Value{
"greeting": cty.StringVal("hello"),
"animal": cty.StringVal("horse").Mark(sensitive),
}).Mark(sensitive),
cty.True,
},
{
cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"a": cty.UnknownVal(cty.String),
}),
cty.ObjectVal(map[string]cty.Value{
"a": cty.StringVal("known").Mark(sensitive),
}),
}),
cty.TupleVal([]cty.Value{
cty.EmptyObjectVal,
cty.ObjectVal(map[string]cty.Value{
"a": cty.True,
}),
}),
},
{
cty.ListVal([]cty.Value{
cty.MapValEmpty(cty.String),
cty.MapVal(map[string]cty.Value{
"a": cty.StringVal("known").Mark(sensitive),
}),
cty.MapVal(map[string]cty.Value{
"a": cty.UnknownVal(cty.String),
}),
}),
cty.TupleVal([]cty.Value{
cty.EmptyObjectVal,
cty.ObjectVal(map[string]cty.Value{
"a": cty.True,
}),
cty.EmptyObjectVal,
}),
},
}
for _, test := range tests {
got := sensitiveAsBool(test.Input)
if !reflect.DeepEqual(got, test.Want) {
t.Errorf(
"wrong result\ninput: %#v\ngot: %#v\nwant: %#v",
test.Input, got, test.Want,
)
}
}
}

View File

@ -83,7 +83,9 @@
},
"after": {
"ami": "bar"
}
},
"after_sensitive": {},
"before_sensitive": false
}
},
{
@ -103,7 +105,9 @@
},
"after": {
"ami": "bar"
}
},
"after_sensitive": {},
"before_sensitive": false
}
},
{
@ -123,7 +127,9 @@
},
"after": {
"ami": "bar"
}
},
"after_sensitive": {},
"before_sensitive": false
}
}
],
@ -134,7 +140,9 @@
],
"before": null,
"after": "bar",
"after_unknown": false
"after_unknown": false,
"before_sensitive": false,
"after_sensitive": false
}
},
"configuration": {

View File

@ -48,7 +48,9 @@
"ami": "bar",
"id": "placeholder"
},
"after_unknown": {}
"after_unknown": {},
"after_sensitive": {},
"before_sensitive": {}
}
},
{
@ -66,7 +68,9 @@
"id": "placeholder"
},
"after": null,
"after_unknown": {}
"after_unknown": {},
"after_sensitive": false,
"before_sensitive": {}
}
}
],
@ -77,7 +81,9 @@
],
"before": null,
"after": "bar",
"after_unknown": false
"after_unknown": false,
"before_sensitive": false,
"after_sensitive": false
}
},
"prior_state": {

View File

@ -48,7 +48,9 @@
"ami": "bar",
"id": "placeholder"
},
"after_unknown": {}
"after_unknown": {},
"after_sensitive": {},
"before_sensitive": {}
}
}
],
@ -59,7 +61,9 @@
],
"before": "bar",
"after": "bar",
"after_unknown": false
"after_unknown": false,
"before_sensitive": false,
"after_sensitive": false
}
},
"prior_state": {

View File

@ -35,7 +35,9 @@
},
"after_unknown": {
"id": true
}
},
"after_sensitive": {},
"before_sensitive": false
}
}
],

View File

@ -99,7 +99,9 @@
},
"after_unknown": {
"id": true
}
},
"after_sensitive": {},
"before_sensitive": false
}
},
{
@ -120,7 +122,9 @@
},
"after_unknown": {
"id": true
}
},
"after_sensitive": {},
"before_sensitive": false
}
},
{
@ -141,7 +145,9 @@
},
"after_unknown": {
"id": true
}
},
"after_sensitive": {},
"before_sensitive": false
}
},
{
@ -162,7 +168,9 @@
},
"after_unknown": {
"id": true
}
},
"after_sensitive": {},
"before_sensitive": false
}
}
],
@ -173,7 +181,9 @@
],
"before": null,
"after": "baz",
"after_unknown": false
"after_unknown": false,
"before_sensitive": false,
"after_sensitive": false
}
},
"configuration": {

View File

@ -63,7 +63,9 @@
"ami": "bar",
"id": "placeholder"
},
"after_unknown": {}
"after_unknown": {},
"after_sensitive": {},
"before_sensitive": {}
}
},
{
@ -83,7 +85,9 @@
},
"after_unknown": {
"id": true
}
},
"after_sensitive": {},
"before_sensitive": false
}
}
],
@ -94,7 +98,9 @@
],
"before": "bar",
"after": "bar",
"after_unknown": false
"after_unknown": false,
"before_sensitive": false,
"after_sensitive": false
}
},
"prior_state": {

View File

@ -45,7 +45,9 @@
},
"after_unknown": {
"id": true
}
},
"after_sensitive": {},
"before_sensitive": false
}
}
],

View File

@ -83,7 +83,9 @@
},
"after": {
"ami": "bar"
}
},
"after_sensitive": {},
"before_sensitive": false
}
},
{
@ -103,7 +105,9 @@
},
"after": {
"ami": "bar"
}
},
"after_sensitive": {},
"before_sensitive": false
}
},
{
@ -123,7 +127,9 @@
},
"after": {
"ami": "bar"
}
},
"after_sensitive": {},
"before_sensitive": false
}
}
],
@ -134,7 +140,9 @@
],
"before": null,
"after": "bar",
"after_unknown": false
"after_unknown": false,
"before_sensitive": false,
"after_sensitive": false
}
},
"configuration": {

View File

@ -83,7 +83,9 @@
},
"after": {
"ami": "bar"
}
},
"after_sensitive": {},
"before_sensitive": false
}
},
{
@ -103,7 +105,9 @@
},
"after": {
"ami": "bar"
}
},
"after_sensitive": {},
"before_sensitive": false
}
},
{
@ -123,7 +127,9 @@
},
"after": {
"ami": "bar"
}
},
"after_sensitive": {},
"before_sensitive": false
}
}
],
@ -134,7 +140,9 @@
],
"before": null,
"after": "bar",
"after_unknown": false
"after_unknown": false,
"before_sensitive": false,
"after_sensitive": false
}
},
"configuration": {

View File

@ -0,0 +1,13 @@
variable "test_var" {
default = "boop"
sensitive = true
}
resource "test_instance" "test" {
ami = var.test_var
}
output "test" {
value = test_instance.test.ami
sensitive = true
}

View File

@ -0,0 +1,117 @@
{
"format_version": "0.1",
"variables": {
"test_var": {
"value": "boop"
}
},
"planned_values": {
"outputs": {
"test": {
"sensitive": true,
"value": "boop"
}
},
"root_module": {
"resources": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_name": "registry.terraform.io/hashicorp/test",
"schema_version": 0,
"values": {
"ami": "boop"
}
}
]
}
},
"resource_changes": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"provider_name": "registry.terraform.io/hashicorp/test",
"name": "test",
"change": {
"actions": [
"create"
],
"before": null,
"after": {
"ami": "boop"
},
"after_unknown": {
"id": true
},
"after_sensitive": {
"ami": true
},
"before_sensitive": false
}
}
],
"output_changes": {
"test": {
"actions": [
"create"
],
"before": null,
"after": "boop",
"after_unknown": false,
"before_sensitive": true,
"after_sensitive": true
}
},
"prior_state": {
"format_version": "0.1",
"values": {
"outputs": {
"test": {
"sensitive": true,
"value": "boop"
}
},
"root_module": {}
}
},
"configuration": {
"root_module": {
"outputs": {
"test": {
"expression": {
"references": [
"test_instance.test"
]
},
"sensitive": true
}
},
"resources": [
{
"address": "test_instance.test",
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider_config_key": "test",
"schema_version": 0,
"expressions": {
"ami": {
"references": [
"var.test_var"
]
}
}
}
],
"variables": {
"test_var": {
"default": "boop",
"sensitive": true
}
}
}
}
}

View File

@ -37,9 +37,6 @@ type ResourceInstanceChangeSrc struct {
// RequiredReplace is a set of paths that caused the change action to be
// Replace rather than Update. Always nil if the change action is not
// Replace.
//
// This is retained only for UI-plan-rendering purposes and so it does not
// currently survive a round-trip through a saved plan file.
RequiredReplace cty.PathSet
// Private allows a provider to stash any extra data that is opaque to

View File

@ -1,13 +1,12 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// protoc-gen-go v1.26.0-devel
// protoc v3.15.6
// source: planfile.proto
package planproto
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@ -21,10 +20,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// Action describes the type of action planned for an object.
// Not all action values are valid for all object types.
type Action int32
@ -347,6 +342,14 @@ type Change struct {
// respectively.
// - For no-op, one value is provided that is left unmodified by this non-change.
Values []*DynamicValue `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"`
// An unordered set of paths into the old value which are marked as
// sensitive. Values at these paths should be obscured in human-readable
// output. This set is always empty for create.
BeforeSensitivePaths []*Path `protobuf:"bytes,3,rep,name=before_sensitive_paths,json=beforeSensitivePaths,proto3" json:"before_sensitive_paths,omitempty"`
// An unordered set of paths into the new value which are marked as
// sensitive. Values at these paths should be obscured in human-readable
// output. This set is always empty for delete.
AfterSensitivePaths []*Path `protobuf:"bytes,4,rep,name=after_sensitive_paths,json=afterSensitivePaths,proto3" json:"after_sensitive_paths,omitempty"`
}
func (x *Change) Reset() {
@ -395,6 +398,20 @@ func (x *Change) GetValues() []*DynamicValue {
return nil
}
func (x *Change) GetBeforeSensitivePaths() []*Path {
if x != nil {
return x.BeforeSensitivePaths
}
return nil
}
func (x *Change) GetAfterSensitivePaths() []*Path {
if x != nil {
return x.AfterSensitivePaths
}
return nil
}
type ResourceInstanceChange struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -941,74 +958,82 @@ var file_planfile_proto_rawDesc = []byte{
0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73,
0x70, 0x61, 0x63, 0x65, 0x22, 0x5e, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26,
0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e,
0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e,
0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x73, 0x22, 0xb9, 0x03, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12,
0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68,
0x12, 0x3f, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b,
0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64,
0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x73, 0x74, 0x72, 0x12, 0x12, 0x0a,
0x03, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x03, 0x69, 0x6e,
0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79,
0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b,
0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26,
0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06,
0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
0x12, 0x37, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70,
0x6c, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70,
0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x22, 0x25, 0x0a, 0x0c, 0x52, 0x65, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x64, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x10, 0x01,
0x42, 0x0e, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79,
0x22, 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68,
0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09,
0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x44, 0x79,
0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x73,
0x67, 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x73, 0x67,
0x70, 0x61, 0x63, 0x6b, 0x22, 0x1e, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06,
0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x68,
0x61, 0x32, 0x35, 0x36, 0x22, 0xa5, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a,
0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74,
0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52,
0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x74, 0x0a, 0x04, 0x53, 0x74, 0x65, 0x70, 0x12, 0x27,
0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74,
0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c,
0x75, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79,
0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2a, 0x70, 0x0a, 0x06,
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00,
0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04,
0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45,
0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16,
0x0a, 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52,
0x45, 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45,
0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x07, 0x42, 0x39,
0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73,
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d,
0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f,
0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
0x70, 0x61, 0x63, 0x65, 0x22, 0xe4, 0x01, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12,
0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e,
0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x16, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f,
0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50,
0x61, 0x74, 0x68, 0x52, 0x14, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x6e, 0x73, 0x69,
0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x40, 0x0a, 0x15, 0x61, 0x66, 0x74,
0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74,
0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61,
0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e,
0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, 0xb9, 0x03, 0x0a, 0x16,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64,
0x75, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3f, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43,
0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f,
0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
0x03, 0x73, 0x74, 0x72, 0x12, 0x12, 0x0a, 0x03, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28,
0x03, 0x48, 0x00, 0x52, 0x03, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f,
0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64,
0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f,
0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f,
0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18,
0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43,
0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,
0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69,
0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52,
0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65,
0x22, 0x25, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x65,
0x12, 0x0b, 0x0a, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x10, 0x00, 0x12, 0x08, 0x0a,
0x04, 0x64, 0x61, 0x74, 0x61, 0x10, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61,
0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75,
0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x63,
0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66,
0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76,
0x65, 0x22, 0x28, 0x0a, 0x0c, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x22, 0x1e, 0x0a, 0x04, 0x48,
0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x22, 0xa5, 0x01, 0x0a, 0x04,
0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74,
0x68, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x74, 0x0a,
0x04, 0x53, 0x74, 0x65, 0x70, 0x12, 0x27, 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37,
0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e,
0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6c, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63,
0x74, 0x6f, 0x72, 0x2a, 0x70, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a,
0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54,
0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a,
0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c,
0x45, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f,
0x54, 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a,
0x12, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c,
0x45, 0x54, 0x45, 0x10, 0x07, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65,
0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -1049,19 +1074,21 @@ var file_planfile_proto_depIdxs = []int32{
7, // 5: tfplan.Backend.config:type_name -> tfplan.DynamicValue
0, // 6: tfplan.Change.action:type_name -> tfplan.Action
7, // 7: tfplan.Change.values:type_name -> tfplan.DynamicValue
1, // 8: tfplan.ResourceInstanceChange.mode:type_name -> tfplan.ResourceInstanceChange.ResourceMode
4, // 9: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change
9, // 10: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path
4, // 11: tfplan.OutputChange.change:type_name -> tfplan.Change
12, // 12: tfplan.Path.steps:type_name -> tfplan.Path.Step
7, // 13: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue
8, // 14: tfplan.Plan.ProviderHashesEntry.value:type_name -> tfplan.Hash
7, // 15: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue
16, // [16:16] is the sub-list for method output_type
16, // [16:16] is the sub-list for method input_type
16, // [16:16] is the sub-list for extension type_name
16, // [16:16] is the sub-list for extension extendee
0, // [0:16] is the sub-list for field type_name
9, // 8: tfplan.Change.before_sensitive_paths:type_name -> tfplan.Path
9, // 9: tfplan.Change.after_sensitive_paths:type_name -> tfplan.Path
1, // 10: tfplan.ResourceInstanceChange.mode:type_name -> tfplan.ResourceInstanceChange.ResourceMode
4, // 11: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change
9, // 12: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path
4, // 13: tfplan.OutputChange.change:type_name -> tfplan.Change
12, // 14: tfplan.Path.steps:type_name -> tfplan.Path.Step
7, // 15: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue
8, // 16: tfplan.Plan.ProviderHashesEntry.value:type_name -> tfplan.Hash
7, // 17: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue
18, // [18:18] is the sub-list for method output_type
18, // [18:18] is the sub-list for method input_type
18, // [18:18] is the sub-list for extension type_name
18, // [18:18] is the sub-list for extension extendee
0, // [0:18] is the sub-list for field type_name
}
func init() { file_planfile_proto_init() }

View File

@ -85,6 +85,16 @@ message Change {
// respectively.
// - For no-op, one value is provided that is left unmodified by this non-change.
repeated DynamicValue values = 2;
// An unordered set of paths into the old value which are marked as
// sensitive. Values at these paths should be obscured in human-readable
// output. This set is always empty for create.
repeated Path before_sensitive_paths = 3;
// An unordered set of paths into the new value which are marked as
// sensitive. Values at these paths should be obscured in human-readable
// output. This set is always empty for delete.
repeated Path after_sensitive_paths = 4;
}
message ResourceInstanceChange {

View File

@ -13,6 +13,7 @@ import (
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
"github.com/hashicorp/terraform/version"
"github.com/zclconf/go-cty/cty"
)
const tfplanFormatVersion = 3
@ -190,6 +191,15 @@ func resourceChangeFromTfplan(rawChange *planproto.ResourceInstanceChange) (*pla
ret.DeposedKey = states.DeposedKey(rawChange.DeposedKey)
}
ret.RequiredReplace = cty.NewPathSet()
for _, p := range rawChange.RequiredReplace {
path, err := pathFromTfplan(p)
if err != nil {
return nil, fmt.Errorf("invalid path in required replace: %s", err)
}
ret.RequiredReplace.Add(path)
}
change, err := changeFromTfplan(rawChange.Change)
if err != nil {
return nil, fmt.Errorf("invalid plan for resource %s: %s", ret.Addr, err)
@ -273,6 +283,22 @@ func changeFromTfplan(rawChange *planproto.Change) (*plans.ChangeSrc, error) {
}
}
sensitive := cty.NewValueMarks("sensitive")
beforeValMarks, err := pathValueMarksFromTfplan(rawChange.BeforeSensitivePaths, sensitive)
if err != nil {
return nil, fmt.Errorf("failed to decode before sensitive paths: %s", err)
}
afterValMarks, err := pathValueMarksFromTfplan(rawChange.AfterSensitivePaths, sensitive)
if err != nil {
return nil, fmt.Errorf("failed to decode after sensitive paths: %s", err)
}
if len(beforeValMarks) > 0 {
ret.BeforeValMarks = beforeValMarks
}
if len(afterValMarks) > 0 {
ret.AfterValMarks = afterValMarks
}
return ret, nil
}
@ -414,6 +440,16 @@ func resourceChangeToTfplan(change *plans.ResourceInstanceChangeSrc) (*planproto
ret.DeposedKey = string(change.DeposedKey)
ret.Provider = change.ProviderAddr.String()
requiredReplace := change.RequiredReplace.List()
ret.RequiredReplace = make([]*planproto.Path, 0, len(requiredReplace))
for _, p := range requiredReplace {
path, err := pathToTfplan(p)
if err != nil {
return nil, fmt.Errorf("invalid path in required replace: %s", err)
}
ret.RequiredReplace = append(ret.RequiredReplace, path)
}
valChange, err := changeToTfplan(&change.ChangeSrc)
if err != nil {
return nil, fmt.Errorf("failed to serialize resource %s change: %s", relAddr, err)
@ -433,6 +469,17 @@ func changeToTfplan(change *plans.ChangeSrc) (*planproto.Change, error) {
before := valueToTfplan(change.Before)
after := valueToTfplan(change.After)
beforeSensitivePaths, err := pathValueMarksToTfplan(change.BeforeValMarks)
if err != nil {
return nil, err
}
afterSensitivePaths, err := pathValueMarksToTfplan(change.AfterValMarks)
if err != nil {
return nil, err
}
ret.BeforeSensitivePaths = beforeSensitivePaths
ret.AfterSensitivePaths = afterSensitivePaths
switch change.Action {
case plans.NoOp:
ret.Action = planproto.Action_NOOP
@ -472,3 +519,86 @@ func valueToTfplan(val plans.DynamicValue) *planproto.DynamicValue {
Msgpack: []byte(val),
}
}
func pathValueMarksFromTfplan(paths []*planproto.Path, marks cty.ValueMarks) ([]cty.PathValueMarks, error) {
ret := make([]cty.PathValueMarks, 0, len(paths))
for _, p := range paths {
path, err := pathFromTfplan(p)
if err != nil {
return nil, err
}
ret = append(ret, cty.PathValueMarks{
Path: path,
Marks: marks,
})
}
return ret, nil
}
func pathValueMarksToTfplan(pvm []cty.PathValueMarks) ([]*planproto.Path, error) {
ret := make([]*planproto.Path, 0, len(pvm))
for _, p := range pvm {
path, err := pathToTfplan(p.Path)
if err != nil {
return nil, err
}
ret = append(ret, path)
}
return ret, nil
}
func pathFromTfplan(path *planproto.Path) (cty.Path, error) {
ret := make([]cty.PathStep, 0, len(path.Steps))
for _, step := range path.Steps {
switch s := step.Selector.(type) {
case *planproto.Path_Step_ElementKey:
dynamicVal, err := valueFromTfplan(s.ElementKey)
if err != nil {
return nil, fmt.Errorf("error decoding path index step: %s", err)
}
ty, err := dynamicVal.ImpliedType()
if err != nil {
return nil, fmt.Errorf("error determining path index type: %s", err)
}
val, err := dynamicVal.Decode(ty)
if err != nil {
return nil, fmt.Errorf("error decoding path index value: %s", err)
}
ret = append(ret, cty.IndexStep{Key: val})
case *planproto.Path_Step_AttributeName:
ret = append(ret, cty.GetAttrStep{Name: s.AttributeName})
default:
return nil, fmt.Errorf("Unsupported path step %t", step.Selector)
}
}
return ret, nil
}
func pathToTfplan(path cty.Path) (*planproto.Path, error) {
steps := make([]*planproto.Path_Step, 0, len(path))
for _, step := range path {
switch s := step.(type) {
case cty.IndexStep:
value, err := plans.NewDynamicValue(s.Key, s.Key.Type())
if err != nil {
return nil, fmt.Errorf("Error encoding path step: %s", err)
}
steps = append(steps, &planproto.Path_Step{
Selector: &planproto.Path_Step_ElementKey{
ElementKey: valueToTfplan(value),
},
})
case cty.GetAttrStep:
steps = append(steps, &planproto.Path_Step{
Selector: &planproto.Path_Step_AttributeName{
AttributeName: s.Name,
},
})
default:
return nil, fmt.Errorf("Unsupported path step %#v (%t)", step, step)
}
}
return &planproto.Path{
Steps: steps,
}, nil
}

View File

@ -64,11 +64,27 @@ func TestTFPlanRoundTrip(t *testing.T) {
Action: plans.DeleteThenCreate,
Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("foo-bar-baz"),
"boop": cty.ListVal([]cty.Value{
cty.StringVal("beep"),
}),
}), objTy),
After: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
"id": cty.UnknownVal(cty.String),
"boop": cty.ListVal([]cty.Value{
cty.StringVal("beep"),
cty.StringVal("honk"),
}),
}), objTy),
AfterValMarks: []cty.PathValueMarks{
{
Path: cty.GetAttrPath("boop").IndexInt(1),
Marks: cty.NewValueMarks("sensitive"),
},
},
},
RequiredReplace: cty.NewPathSet(
cty.GetAttrPath("boop"),
),
},
{
Addr: addrs.Resource{