mirror of https://github.com/hashicorp/terraform
When rendering a stored plan file as JSON, we include a data structure representing the sensitivity of the changed resource values. Prior to this commit, this was a direct representation of the sensitivity marks applied to values via mechanisms such as sensitive variables, sensitive outputs, and the `sensitive` function. This commit extends this to include sensitivity based on the provider schema. This is in line with the UI rendering of the plan, which considers these two different types of sensitivity to be equivalent. Co-authored-by: Kristin Laemmert <mildwonkey@users.noreply.github.com>pull/28523/head
parent
4997b9b5bb
commit
c89004d223
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue