mirror of https://github.com/hashicorp/terraform
When emitting apply descriptions for changes that contain sensitive values, we need to unmark the value before re-serializing it with MsgPack.radditude/apply-sensitive-values
parent
f7149c216d
commit
d60a3db261
@ -0,0 +1,157 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package stackstate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/lang/marks"
|
||||
"github.com/hashicorp/terraform/internal/plans/planproto"
|
||||
"github.com/hashicorp/terraform/internal/stacks/tfstackdata1"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
ctymsgpack "github.com/zclconf/go-cty/cty/msgpack"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
|
||||
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
|
||||
)
|
||||
|
||||
func TestAppliedChangeAsProto(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Receiver AppliedChange
|
||||
Want *terraform1.AppliedChange
|
||||
}{
|
||||
"resource instance": {
|
||||
Receiver: &AppliedChangeResourceInstanceObject{
|
||||
ResourceInstanceObjectAddr: stackaddrs.AbsResourceInstanceObject{
|
||||
Component: stackaddrs.AbsComponentInstance{
|
||||
Stack: stackaddrs.RootStackInstance.Child("a", addrs.StringKey("boop")),
|
||||
Item: stackaddrs.ComponentInstance{
|
||||
Component: stackaddrs.Component{Name: "foo"},
|
||||
Key: addrs.StringKey("beep"),
|
||||
},
|
||||
},
|
||||
Item: addrs.AbsResourceInstanceObject{
|
||||
ResourceInstance: addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "thingy",
|
||||
Name: "thingamajig",
|
||||
}.Instance(addrs.IntKey(1)).Absolute(
|
||||
addrs.RootModuleInstance.Child("pizza", addrs.StringKey("chicken")),
|
||||
),
|
||||
},
|
||||
},
|
||||
ProviderConfigAddr: addrs.AbsProviderConfig{
|
||||
Module: addrs.RootModule,
|
||||
Provider: addrs.MustParseProviderSourceString("example.com/thingers/thingy"),
|
||||
},
|
||||
Schema: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"secret": {
|
||||
Type: cty.String,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
NewStateSrc: &states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar","secret":"top"}`),
|
||||
AttrSensitivePaths: []cty.PathValueMarks{
|
||||
{
|
||||
Path: []cty.PathStep{cty.GetAttrStep{Name: "secret"}},
|
||||
Marks: map[interface{}]struct{}{marks.Sensitive: {}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Want: &terraform1.AppliedChange{
|
||||
Raw: []*terraform1.AppliedChange_RawChange{
|
||||
{
|
||||
Key: `RSRCstack.a["boop"].component.foo["beep"],module.pizza["chicken"].thingy.thingamajig[1],cur`,
|
||||
Value: mustMarshalAnyPb(t, &tfstackdata1.StateResourceInstanceObjectV1{
|
||||
ValueJson: json.RawMessage(`{"id":"bar","secret":"top"}`),
|
||||
SensitivePaths: []*planproto.Path{
|
||||
{
|
||||
Steps: []*planproto.Path_Step{{
|
||||
Selector: &planproto.Path_Step_AttributeName{AttributeName: "secret"}}},
|
||||
},
|
||||
},
|
||||
ProviderConfigAddr: `provider["example.com/thingers/thingy"]`,
|
||||
Status: tfstackdata1.StateResourceInstanceObjectV1_READY,
|
||||
}),
|
||||
},
|
||||
},
|
||||
Descriptions: []*terraform1.AppliedChange_ChangeDescription{
|
||||
{
|
||||
Key: `RSRCstack.a["boop"].component.foo["beep"],module.pizza["chicken"].thingy.thingamajig[1],cur`,
|
||||
Description: &terraform1.AppliedChange_ChangeDescription_ResourceInstance{
|
||||
ResourceInstance: &terraform1.AppliedChange_ResourceInstance{
|
||||
Addr: &terraform1.ResourceInstanceObjectInStackAddr{
|
||||
ComponentInstanceAddr: `stack.a["boop"].component.foo["beep"]`,
|
||||
ResourceInstanceAddr: `module.pizza["chicken"].thingy.thingamajig[1]`,
|
||||
},
|
||||
NewValue: &terraform1.DynamicValue{
|
||||
Msgpack: mustMsgpack(t, cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
"secret": cty.StringVal("top"),
|
||||
}), cty.Object(map[string]cty.Type{"id": cty.String, "secret": cty.String})),
|
||||
Sensitive: []*terraform1.AttributePath{{
|
||||
Steps: []*terraform1.AttributePath_Step{{
|
||||
Selector: &terraform1.AttributePath_Step_AttributeName{AttributeName: "secret"},
|
||||
}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, err := test.Receiver.AppliedChangeProto()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
spew.Dump(got)
|
||||
if diff := cmp.Diff(test.Want, got, protocmp.Transform()); diff != "" {
|
||||
t.Errorf("wrong result\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mustMarshalAnyPb(t *testing.T, msg proto.Message) *anypb.Any {
|
||||
var ret anypb.Any
|
||||
err := anypb.MarshalFrom(&ret, msg, proto.MarshalOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("error marshalling anypb: %q", err)
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func mustMsgpack(t *testing.T, v cty.Value, ty cty.Type) []byte {
|
||||
t.Helper()
|
||||
|
||||
ret, err := ctymsgpack.Marshal(v, ty)
|
||||
if err != nil {
|
||||
t.Fatalf("error marshalling %#v: %s", v, err)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
Loading…
Reference in new issue