Merge pull request #28523 from hashicorp/alisdair/json-plan-sensitive-provider-attrs
core: Add sensitive provider attrs to JSON plan
This commit is contained in:
commit
4b1ff4eda7
|
@ -213,7 +213,11 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bs := sensitiveAsBool(changeV.Before.MarkWithPaths(rc.BeforeValMarks))
|
marks := rc.BeforeValMarks
|
||||||
|
if schema.ContainsSensitive() {
|
||||||
|
marks = append(marks, schema.ValueMarks(changeV.Before, nil)...)
|
||||||
|
}
|
||||||
|
bs := sensitiveAsBool(changeV.Before.MarkWithPaths(marks))
|
||||||
beforeSensitive, err = ctyjson.Marshal(bs, bs.Type())
|
beforeSensitive, err = ctyjson.Marshal(bs, bs.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -238,7 +242,11 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
|
||||||
}
|
}
|
||||||
afterUnknown = unknownAsBool(changeV.After)
|
afterUnknown = unknownAsBool(changeV.After)
|
||||||
}
|
}
|
||||||
as := sensitiveAsBool(changeV.After.MarkWithPaths(rc.AfterValMarks))
|
marks := rc.AfterValMarks
|
||||||
|
if schema.ContainsSensitive() {
|
||||||
|
marks = append(marks, schema.ValueMarks(changeV.After, nil)...)
|
||||||
|
}
|
||||||
|
as := sensitiveAsBool(changeV.After.MarkWithPaths(marks))
|
||||||
afterSensitive, err = ctyjson.Marshal(as, as.Type())
|
afterSensitive, err = ctyjson.Marshal(as, as.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -343,6 +343,88 @@ func TestShow_json_output(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShow_json_output_sensitive(t *testing.T) {
|
||||||
|
td := tempDir(t)
|
||||||
|
inputDir := "testdata/show-json-sensitive"
|
||||||
|
testCopyDir(t, inputDir, td)
|
||||||
|
defer os.RemoveAll(td)
|
||||||
|
defer testChdir(t, td)()
|
||||||
|
|
||||||
|
providerSource, close := newMockProviderSource(t, map[string][]string{"test": {"1.2.3"}})
|
||||||
|
defer close()
|
||||||
|
|
||||||
|
p := showFixtureSensitiveProvider()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
view, _ := testView(t)
|
||||||
|
m := Meta{
|
||||||
|
testingOverrides: metaOverridesForProvider(p),
|
||||||
|
Ui: ui,
|
||||||
|
View: view,
|
||||||
|
ProviderSource: providerSource,
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
ic := &InitCommand{
|
||||||
|
Meta: m,
|
||||||
|
}
|
||||||
|
if code := ic.Run([]string{}); code != 0 {
|
||||||
|
t.Fatalf("init failed\n%s", ui.ErrorWriter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush init output
|
||||||
|
ui.OutputWriter.Reset()
|
||||||
|
|
||||||
|
pc := &PlanCommand{
|
||||||
|
Meta: m,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"-out=terraform.plan",
|
||||||
|
}
|
||||||
|
|
||||||
|
if code := pc.Run(args); code != 0 {
|
||||||
|
fmt.Println(ui.OutputWriter.String())
|
||||||
|
t.Fatalf("wrong exit status %d; want 0\nstderr: %s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush the plan output from the mock ui
|
||||||
|
ui.OutputWriter.Reset()
|
||||||
|
sc := &ShowCommand{
|
||||||
|
Meta: m,
|
||||||
|
}
|
||||||
|
|
||||||
|
args = []string{
|
||||||
|
"-json",
|
||||||
|
"terraform.plan",
|
||||||
|
}
|
||||||
|
defer os.Remove("terraform.plan")
|
||||||
|
|
||||||
|
if code := sc.Run(args); code != 0 {
|
||||||
|
t.Fatalf("wrong exit status %d; want 0\nstderr: %s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare ui output to wanted output
|
||||||
|
var got, want plan
|
||||||
|
|
||||||
|
gotString := ui.OutputWriter.String()
|
||||||
|
json.Unmarshal([]byte(gotString), &got)
|
||||||
|
|
||||||
|
wantFile, err := os.Open("output.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
defer wantFile.Close()
|
||||||
|
byteValue, err := ioutil.ReadAll(wantFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
json.Unmarshal([]byte(byteValue), &want)
|
||||||
|
|
||||||
|
if !cmp.Equal(got, want) {
|
||||||
|
t.Fatalf("wrong result:\n %v\n", cmp.Diff(got, want))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// similar test as above, without the plan
|
// similar test as above, without the plan
|
||||||
func TestShow_json_output_state(t *testing.T) {
|
func TestShow_json_output_state(t *testing.T) {
|
||||||
fixtureDir := "testdata/show-json-state"
|
fixtureDir := "testdata/show-json-state"
|
||||||
|
@ -450,6 +532,32 @@ func showFixtureSchema() *providers.GetProviderSchemaResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// showFixtureSensitiveSchema returns a schema suitable for processing the configuration
|
||||||
|
// in testdata/show. This schema should be assigned to a mock provider
|
||||||
|
// named "test". It includes a sensitive attribute.
|
||||||
|
func showFixtureSensitiveSchema() *providers.GetProviderSchemaResponse {
|
||||||
|
return &providers.GetProviderSchemaResponse{
|
||||||
|
Provider: providers.Schema{
|
||||||
|
Block: &configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"region": {Type: cty.String, Optional: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResourceTypes: map[string]providers.Schema{
|
||||||
|
"test_instance": {
|
||||||
|
Block: &configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"id": {Type: cty.String, Optional: true, Computed: true},
|
||||||
|
"ami": {Type: cty.String, Optional: true},
|
||||||
|
"password": {Type: cty.String, Optional: true, Sensitive: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// showFixtureProvider returns a mock provider that is configured for basic
|
// showFixtureProvider returns a mock provider that is configured for basic
|
||||||
// operation with the configuration in testdata/show. This mock has
|
// operation with the configuration in testdata/show. This mock has
|
||||||
// GetSchemaResponse, PlanResourceChangeFn, and ApplyResourceChangeFn populated,
|
// GetSchemaResponse, PlanResourceChangeFn, and ApplyResourceChangeFn populated,
|
||||||
|
@ -487,6 +595,43 @@ func showFixtureProvider() *terraform.MockProvider {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// showFixtureSensitiveProvider returns a mock provider that is configured for basic
|
||||||
|
// operation with the configuration in testdata/show. This mock has
|
||||||
|
// GetSchemaResponse, PlanResourceChangeFn, and ApplyResourceChangeFn populated,
|
||||||
|
// with the plan/apply steps just passing through the data determined by
|
||||||
|
// Terraform Core. It also has a sensitive attribute in the provider schema.
|
||||||
|
func showFixtureSensitiveProvider() *terraform.MockProvider {
|
||||||
|
p := testProvider()
|
||||||
|
p.GetProviderSchemaResponse = showFixtureSensitiveSchema()
|
||||||
|
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
||||||
|
idVal := req.ProposedNewState.GetAttr("id")
|
||||||
|
if idVal.IsNull() {
|
||||||
|
idVal = cty.UnknownVal(cty.String)
|
||||||
|
}
|
||||||
|
return providers.PlanResourceChangeResponse{
|
||||||
|
PlannedState: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": idVal,
|
||||||
|
"ami": req.ProposedNewState.GetAttr("ami"),
|
||||||
|
"password": req.ProposedNewState.GetAttr("password"),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
|
||||||
|
idVal := req.PlannedState.GetAttr("id")
|
||||||
|
if !idVal.IsKnown() {
|
||||||
|
idVal = cty.StringVal("placeholder")
|
||||||
|
}
|
||||||
|
return providers.ApplyResourceChangeResponse{
|
||||||
|
NewState: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": idVal,
|
||||||
|
"ami": req.PlannedState.GetAttr("ami"),
|
||||||
|
"password": req.PlannedState.GetAttr("password"),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
// showFixturePlanFile creates a plan file at a temporary location containing a
|
// showFixturePlanFile creates a plan file at a temporary location containing a
|
||||||
// single change to create or update the test_instance.foo that is included in the "show"
|
// single change to create or update the test_instance.foo that is included in the "show"
|
||||||
// test fixture, returning the location of that plan file.
|
// test fixture, returning the location of that plan file.
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
provider "test" {
|
||||||
|
region = "somewhere"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "test_var" {
|
||||||
|
default = "bar"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "test_instance" "test" {
|
||||||
|
// this variable is sensitive
|
||||||
|
ami = var.test_var
|
||||||
|
// the password attribute is sensitive in the showFixtureSensitiveProvider schema.
|
||||||
|
password = "secret"
|
||||||
|
count = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
output "test" {
|
||||||
|
value = var.test_var
|
||||||
|
sensitive = true
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
{
|
||||||
|
"format_version": "0.1",
|
||||||
|
"variables": {
|
||||||
|
"test_var": {
|
||||||
|
"value": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"planned_values": {
|
||||||
|
"outputs": {
|
||||||
|
"test": {
|
||||||
|
"sensitive": true,
|
||||||
|
"value": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root_module": {
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"address": "test_instance.test[0]",
|
||||||
|
"index": 0,
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "test_instance",
|
||||||
|
"name": "test",
|
||||||
|
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||||
|
"schema_version": 0,
|
||||||
|
"values": {
|
||||||
|
"ami": "bar",
|
||||||
|
"password": "secret"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "test_instance.test[1]",
|
||||||
|
"index": 1,
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "test_instance",
|
||||||
|
"name": "test",
|
||||||
|
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||||
|
"schema_version": 0,
|
||||||
|
"values": {
|
||||||
|
"ami": "bar",
|
||||||
|
"password": "secret"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "test_instance.test[2]",
|
||||||
|
"index": 2,
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "test_instance",
|
||||||
|
"name": "test",
|
||||||
|
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||||
|
"schema_version": 0,
|
||||||
|
"values": {
|
||||||
|
"ami": "bar",
|
||||||
|
"password": "secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prior_state": {
|
||||||
|
"format_version": "0.1",
|
||||||
|
"values": {
|
||||||
|
"outputs": {
|
||||||
|
"test": {
|
||||||
|
"sensitive": true,
|
||||||
|
"value": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root_module": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resource_changes": [
|
||||||
|
{
|
||||||
|
"address": "test_instance.test[0]",
|
||||||
|
"index": 0,
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "test_instance",
|
||||||
|
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||||
|
"name": "test",
|
||||||
|
"change": {
|
||||||
|
"actions": [
|
||||||
|
"create"
|
||||||
|
],
|
||||||
|
"before": null,
|
||||||
|
"after_unknown": {
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"after": {
|
||||||
|
"ami": "bar",
|
||||||
|
"password": "secret"
|
||||||
|
},
|
||||||
|
"after_sensitive": {"ami": true, "password": true},
|
||||||
|
"before_sensitive": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "test_instance.test[1]",
|
||||||
|
"index": 1,
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "test_instance",
|
||||||
|
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||||
|
"name": "test",
|
||||||
|
"change": {
|
||||||
|
"actions": [
|
||||||
|
"create"
|
||||||
|
],
|
||||||
|
"before": null,
|
||||||
|
"after_unknown": {
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"after": {
|
||||||
|
"ami": "bar",
|
||||||
|
"password": "secret"
|
||||||
|
},
|
||||||
|
"after_sensitive": {"ami": true, "password": true},
|
||||||
|
"before_sensitive": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "test_instance.test[2]",
|
||||||
|
"index": 2,
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "test_instance",
|
||||||
|
"provider_name": "registry.terraform.io/hashicorp/test",
|
||||||
|
"name": "test",
|
||||||
|
"change": {
|
||||||
|
"actions": [
|
||||||
|
"create"
|
||||||
|
],
|
||||||
|
"before": null,
|
||||||
|
"after_unknown": {
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"after": {
|
||||||
|
"ami": "bar",
|
||||||
|
"password": "secret"
|
||||||
|
},
|
||||||
|
"after_sensitive": {"ami": true, "password": true},
|
||||||
|
"before_sensitive": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"output_changes": {
|
||||||
|
"test": {
|
||||||
|
"actions": [
|
||||||
|
"create"
|
||||||
|
],
|
||||||
|
"before": null,
|
||||||
|
"after": "bar",
|
||||||
|
"after_unknown": false,
|
||||||
|
"before_sensitive": true,
|
||||||
|
"after_sensitive": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"provider_config": {
|
||||||
|
"test": {
|
||||||
|
"name": "test",
|
||||||
|
"expressions": {
|
||||||
|
"region": {
|
||||||
|
"constant_value": "somewhere"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root_module": {
|
||||||
|
"outputs": {
|
||||||
|
"test": {
|
||||||
|
"expression": {
|
||||||
|
"references": [
|
||||||
|
"var.test_var"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"password": {"constant_value": "secret"}
|
||||||
|
},
|
||||||
|
"count_expression": {
|
||||||
|
"constant_value": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variables": {
|
||||||
|
"test_var": {
|
||||||
|
"default": "bar",
|
||||||
|
"sensitive": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package configschema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValueMarks returns a set of path value marks for a given value and path,
|
||||||
|
// based on the sensitive flag for each attribute within the schema. Nested
|
||||||
|
// blocks are descended (if present in the given value).
|
||||||
|
func (b *Block) ValueMarks(val cty.Value, path cty.Path) []cty.PathValueMarks {
|
||||||
|
var pvm []cty.PathValueMarks
|
||||||
|
for name, attrS := range b.Attributes {
|
||||||
|
if attrS.Sensitive {
|
||||||
|
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
|
||||||
|
attrPath := make(cty.Path, len(path), len(path)+1)
|
||||||
|
copy(attrPath, path)
|
||||||
|
attrPath = append(path, cty.GetAttrStep{Name: name})
|
||||||
|
pvm = append(pvm, cty.PathValueMarks{
|
||||||
|
Path: attrPath,
|
||||||
|
Marks: cty.NewValueMarks("sensitive"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, blockS := range b.BlockTypes {
|
||||||
|
// If our block doesn't contain any sensitive attributes, skip inspecting it
|
||||||
|
if !blockS.Block.ContainsSensitive() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
blockV := val.GetAttr(name)
|
||||||
|
if blockV.IsNull() || !blockV.IsKnown() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
|
||||||
|
blockPath := make(cty.Path, len(path), len(path)+1)
|
||||||
|
copy(blockPath, path)
|
||||||
|
blockPath = append(path, cty.GetAttrStep{Name: name})
|
||||||
|
|
||||||
|
switch blockS.Nesting {
|
||||||
|
case NestingSingle, NestingGroup:
|
||||||
|
pvm = append(pvm, blockS.Block.ValueMarks(blockV, blockPath)...)
|
||||||
|
case NestingList, NestingMap, NestingSet:
|
||||||
|
for it := blockV.ElementIterator(); it.Next(); {
|
||||||
|
idx, blockEV := it.Element()
|
||||||
|
morePaths := blockS.Block.ValueMarks(blockEV, append(blockPath, cty.IndexStep{Key: idx}))
|
||||||
|
pvm = append(pvm, morePaths...)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pvm
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package configschema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBlockValueMarks(t *testing.T) {
|
||||||
|
schema := &Block{
|
||||||
|
Attributes: map[string]*Attribute{
|
||||||
|
"unsensitive": {
|
||||||
|
Type: cty.String,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"sensitive": {
|
||||||
|
Type: cty.String,
|
||||||
|
Sensitive: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
BlockTypes: map[string]*NestedBlock{
|
||||||
|
"list": {
|
||||||
|
Nesting: NestingList,
|
||||||
|
Block: Block{
|
||||||
|
Attributes: map[string]*Attribute{
|
||||||
|
"unsensitive": {
|
||||||
|
Type: cty.String,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"sensitive": {
|
||||||
|
Type: cty.String,
|
||||||
|
Sensitive: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
given cty.Value
|
||||||
|
expect cty.Value
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.UnknownVal(schema.ImpliedType()),
|
||||||
|
cty.UnknownVal(schema.ImpliedType()),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive": cty.UnknownVal(cty.String),
|
||||||
|
"unsensitive": cty.UnknownVal(cty.String),
|
||||||
|
"list": cty.UnknownVal(schema.BlockTypes["list"].ImpliedType()),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive": cty.UnknownVal(cty.String).Mark("sensitive"),
|
||||||
|
"unsensitive": cty.UnknownVal(cty.String),
|
||||||
|
"list": cty.UnknownVal(schema.BlockTypes["list"].ImpliedType()),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive": cty.NullVal(cty.String),
|
||||||
|
"unsensitive": cty.UnknownVal(cty.String),
|
||||||
|
"list": cty.ListVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive": cty.UnknownVal(cty.String),
|
||||||
|
"unsensitive": cty.UnknownVal(cty.String),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive": cty.NullVal(cty.String),
|
||||||
|
"unsensitive": cty.NullVal(cty.String),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive": cty.NullVal(cty.String).Mark("sensitive"),
|
||||||
|
"unsensitive": cty.UnknownVal(cty.String),
|
||||||
|
"list": cty.ListVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive": cty.UnknownVal(cty.String).Mark("sensitive"),
|
||||||
|
"unsensitive": cty.UnknownVal(cty.String),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive": cty.NullVal(cty.String).Mark("sensitive"),
|
||||||
|
"unsensitive": cty.NullVal(cty.String),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("%#v", tc.given), func(t *testing.T) {
|
||||||
|
got := tc.given.MarkWithPaths(schema.ValueMarks(tc.given, nil))
|
||||||
|
if !got.RawEquals(tc.expect) {
|
||||||
|
t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expect, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -760,7 +760,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
|
||||||
// If our provider schema contains sensitive values, mark those as sensitive
|
// If our provider schema contains sensitive values, mark those as sensitive
|
||||||
afterMarks := change.AfterValMarks
|
afterMarks := change.AfterValMarks
|
||||||
if schema.ContainsSensitive() {
|
if schema.ContainsSensitive() {
|
||||||
afterMarks = append(afterMarks, getValMarks(schema, val, nil)...)
|
afterMarks = append(afterMarks, schema.ValueMarks(val, nil)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
instances[key] = val.MarkWithPaths(afterMarks)
|
instances[key] = val.MarkWithPaths(afterMarks)
|
||||||
|
@ -789,7 +789,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
|
||||||
if schema.ContainsSensitive() {
|
if schema.ContainsSensitive() {
|
||||||
var marks []cty.PathValueMarks
|
var marks []cty.PathValueMarks
|
||||||
val, marks = val.UnmarkDeepWithPaths()
|
val, marks = val.UnmarkDeepWithPaths()
|
||||||
marks = append(marks, getValMarks(schema, val, nil)...)
|
marks = append(marks, schema.ValueMarks(val, nil)...)
|
||||||
val = val.MarkWithPaths(marks)
|
val = val.MarkWithPaths(marks)
|
||||||
}
|
}
|
||||||
instances[key] = val
|
instances[key] = val
|
||||||
|
@ -959,50 +959,3 @@ func moduleDisplayAddr(addr addrs.ModuleInstance) string {
|
||||||
return addr.String()
|
return addr.String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getValMarks(schema *configschema.Block, val cty.Value, path cty.Path) []cty.PathValueMarks {
|
|
||||||
var pvm []cty.PathValueMarks
|
|
||||||
for name, attrS := range schema.Attributes {
|
|
||||||
if attrS.Sensitive {
|
|
||||||
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
|
|
||||||
attrPath := make(cty.Path, len(path), len(path)+1)
|
|
||||||
copy(attrPath, path)
|
|
||||||
attrPath = append(path, cty.GetAttrStep{Name: name})
|
|
||||||
pvm = append(pvm, cty.PathValueMarks{
|
|
||||||
Path: attrPath,
|
|
||||||
Marks: cty.NewValueMarks("sensitive"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, blockS := range schema.BlockTypes {
|
|
||||||
// If our block doesn't contain any sensitive attributes, skip inspecting it
|
|
||||||
if !blockS.Block.ContainsSensitive() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
blockV := val.GetAttr(name)
|
|
||||||
if blockV.IsNull() || !blockV.IsKnown() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a copy of the path, with this step added, to add to our PathValueMarks slice
|
|
||||||
blockPath := make(cty.Path, len(path), len(path)+1)
|
|
||||||
copy(blockPath, path)
|
|
||||||
blockPath = append(path, cty.GetAttrStep{Name: name})
|
|
||||||
|
|
||||||
switch blockS.Nesting {
|
|
||||||
case configschema.NestingSingle, configschema.NestingGroup:
|
|
||||||
pvm = append(pvm, getValMarks(&blockS.Block, blockV, blockPath)...)
|
|
||||||
case configschema.NestingList, configschema.NestingMap, configschema.NestingSet:
|
|
||||||
for it := blockV.ElementIterator(); it.Next(); {
|
|
||||||
idx, blockEV := it.Element()
|
|
||||||
morePaths := getValMarks(&blockS.Block, blockEV, append(blockPath, cty.IndexStep{Key: idx}))
|
|
||||||
pvm = append(pvm, morePaths...)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pvm
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -562,95 +561,3 @@ func evaluatorForModule(stateSync *states.SyncState, changesSync *plans.ChangesS
|
||||||
Changes: changesSync,
|
Changes: changesSync,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetValMarks(t *testing.T) {
|
|
||||||
schema := &configschema.Block{
|
|
||||||
Attributes: map[string]*configschema.Attribute{
|
|
||||||
"unsensitive": {
|
|
||||||
Type: cty.String,
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
"sensitive": {
|
|
||||||
Type: cty.String,
|
|
||||||
Sensitive: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
BlockTypes: map[string]*configschema.NestedBlock{
|
|
||||||
"list": &configschema.NestedBlock{
|
|
||||||
Nesting: configschema.NestingList,
|
|
||||||
Block: configschema.Block{
|
|
||||||
Attributes: map[string]*configschema.Attribute{
|
|
||||||
"unsensitive": {
|
|
||||||
Type: cty.String,
|
|
||||||
Optional: true,
|
|
||||||
},
|
|
||||||
"sensitive": {
|
|
||||||
Type: cty.String,
|
|
||||||
Sensitive: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range []struct {
|
|
||||||
given cty.Value
|
|
||||||
expect cty.Value
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
cty.UnknownVal(schema.ImpliedType()),
|
|
||||||
cty.UnknownVal(schema.ImpliedType()),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
|
||||||
"sensitive": cty.UnknownVal(cty.String),
|
|
||||||
"unsensitive": cty.UnknownVal(cty.String),
|
|
||||||
"list": cty.UnknownVal(schema.BlockTypes["list"].ImpliedType()),
|
|
||||||
}),
|
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
|
||||||
"sensitive": cty.UnknownVal(cty.String).Mark("sensitive"),
|
|
||||||
"unsensitive": cty.UnknownVal(cty.String),
|
|
||||||
"list": cty.UnknownVal(schema.BlockTypes["list"].ImpliedType()),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
|
||||||
"sensitive": cty.NullVal(cty.String),
|
|
||||||
"unsensitive": cty.UnknownVal(cty.String),
|
|
||||||
"list": cty.ListVal([]cty.Value{
|
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
|
||||||
"sensitive": cty.UnknownVal(cty.String),
|
|
||||||
"unsensitive": cty.UnknownVal(cty.String),
|
|
||||||
}),
|
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
|
||||||
"sensitive": cty.NullVal(cty.String),
|
|
||||||
"unsensitive": cty.NullVal(cty.String),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
|
||||||
"sensitive": cty.NullVal(cty.String).Mark("sensitive"),
|
|
||||||
"unsensitive": cty.UnknownVal(cty.String),
|
|
||||||
"list": cty.ListVal([]cty.Value{
|
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
|
||||||
"sensitive": cty.UnknownVal(cty.String).Mark("sensitive"),
|
|
||||||
"unsensitive": cty.UnknownVal(cty.String),
|
|
||||||
}),
|
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
|
||||||
"sensitive": cty.NullVal(cty.String).Mark("sensitive"),
|
|
||||||
"unsensitive": cty.NullVal(cty.String),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(fmt.Sprintf("%#v", tc.given), func(t *testing.T) {
|
|
||||||
got := tc.given.MarkWithPaths(getValMarks(schema, tc.given, nil))
|
|
||||||
if !got.RawEquals(tc.expect) {
|
|
||||||
t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expect, got)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue