mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Merge pull request #30945 from hashicorp/alisdair/jsonstate-output-type
json-output: Add output type to JSON format
This commit is contained in:
commit
b02867bed3
@ -106,6 +106,7 @@ type change struct {
|
|||||||
|
|
||||||
type output struct {
|
type output struct {
|
||||||
Sensitive bool `json:"sensitive"`
|
Sensitive bool `json:"sensitive"`
|
||||||
|
Type json.RawMessage `json:"type,omitempty"`
|
||||||
Value json.RawMessage `json:"value,omitempty"`
|
Value json.RawMessage `json:"value,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ func marshalPlannedOutputs(changes *plans.Changes) (map[string]output, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var after []byte
|
var after, afterType []byte
|
||||||
changeV, err := oc.Decode()
|
changeV, err := oc.Decode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ret, err
|
return ret, err
|
||||||
@ -68,7 +68,12 @@ func marshalPlannedOutputs(changes *plans.Changes) (map[string]output, error) {
|
|||||||
changeV.After, _ = changeV.After.UnmarkDeep()
|
changeV.After, _ = changeV.After.UnmarkDeep()
|
||||||
|
|
||||||
if changeV.After != cty.NilVal && changeV.After.IsWhollyKnown() {
|
if changeV.After != cty.NilVal && changeV.After.IsWhollyKnown() {
|
||||||
after, err = ctyjson.Marshal(changeV.After, changeV.After.Type())
|
ty := changeV.After.Type()
|
||||||
|
after, err = ctyjson.Marshal(changeV.After, ty)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
afterType, err = ctyjson.MarshalType(ty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
@ -76,6 +81,7 @@ func marshalPlannedOutputs(changes *plans.Changes) (map[string]output, error) {
|
|||||||
|
|
||||||
ret[oc.Addr.OutputValue.Name] = output{
|
ret[oc.Addr.OutputValue.Name] = output{
|
||||||
Value: json.RawMessage(after),
|
Value: json.RawMessage(after),
|
||||||
|
Type: json.RawMessage(afterType),
|
||||||
Sensitive: oc.Sensitive,
|
Sensitive: oc.Sensitive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@ func TestMarshalPlannedOutputs(t *testing.T) {
|
|||||||
map[string]output{
|
map[string]output{
|
||||||
"bar": {
|
"bar": {
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
|
Type: json.RawMessage(`"string"`),
|
||||||
Value: json.RawMessage(`"after"`),
|
Value: json.RawMessage(`"after"`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -38,6 +38,7 @@ type stateValues struct {
|
|||||||
type output struct {
|
type output struct {
|
||||||
Sensitive bool `json:"sensitive"`
|
Sensitive bool `json:"sensitive"`
|
||||||
Value json.RawMessage `json:"value,omitempty"`
|
Value json.RawMessage `json:"value,omitempty"`
|
||||||
|
Type json.RawMessage `json:"type,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// module is the representation of a module in state. This can be the root module
|
// module is the representation of a module in state. This can be the root module
|
||||||
@ -180,12 +181,18 @@ func marshalOutputs(outputs map[string]*states.OutputValue) (map[string]output,
|
|||||||
|
|
||||||
ret := make(map[string]output)
|
ret := make(map[string]output)
|
||||||
for k, v := range outputs {
|
for k, v := range outputs {
|
||||||
ov, err := ctyjson.Marshal(v.Value, v.Value.Type())
|
ty := v.Value.Type()
|
||||||
|
ov, err := ctyjson.Marshal(v.Value, ty)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
ot, err := ctyjson.MarshalType(ty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
ret[k] = output{
|
ret[k] = output{
|
||||||
Value: ov,
|
Value: ov,
|
||||||
|
Type: ot,
|
||||||
Sensitive: v.Sensitive,
|
Sensitive: v.Sensitive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ func TestMarshalOutputs(t *testing.T) {
|
|||||||
"test": {
|
"test": {
|
||||||
Sensitive: true,
|
Sensitive: true,
|
||||||
Value: json.RawMessage(`"sekret"`),
|
Value: json.RawMessage(`"sekret"`),
|
||||||
|
Type: json.RawMessage(`"string"`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
@ -51,6 +52,39 @@ func TestMarshalOutputs(t *testing.T) {
|
|||||||
"test": {
|
"test": {
|
||||||
Sensitive: false,
|
Sensitive: false,
|
||||||
Value: json.RawMessage(`"not_so_sekret"`),
|
Value: json.RawMessage(`"not_so_sekret"`),
|
||||||
|
Type: json.RawMessage(`"string"`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
map[string]*states.OutputValue{
|
||||||
|
"mapstring": {
|
||||||
|
Sensitive: false,
|
||||||
|
Value: cty.MapVal(map[string]cty.Value{
|
||||||
|
"beep": cty.StringVal("boop"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
"setnumber": {
|
||||||
|
Sensitive: false,
|
||||||
|
Value: cty.SetVal([]cty.Value{
|
||||||
|
cty.NumberIntVal(3),
|
||||||
|
cty.NumberIntVal(5),
|
||||||
|
cty.NumberIntVal(7),
|
||||||
|
cty.NumberIntVal(11),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string]output{
|
||||||
|
"mapstring": {
|
||||||
|
Sensitive: false,
|
||||||
|
Value: json.RawMessage(`{"beep":"boop"}`),
|
||||||
|
Type: json.RawMessage(`["map","string"]`),
|
||||||
|
},
|
||||||
|
"setnumber": {
|
||||||
|
Sensitive: false,
|
||||||
|
Value: json.RawMessage(`[3,5,7,11]`),
|
||||||
|
Type: json.RawMessage(`["set","number"]`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
@ -67,10 +101,8 @@ func TestMarshalOutputs(t *testing.T) {
|
|||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
t.Fatalf("unexpected error: %s", err)
|
t.Fatalf("unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
eq := reflect.DeepEqual(got, test.Want)
|
if !cmp.Equal(test.Want, got) {
|
||||||
if !eq {
|
t.Fatalf("wrong result:\n%s", cmp.Diff(test.Want, got))
|
||||||
// printing the output isn't terribly useful, but it does help indicate which test case failed
|
|
||||||
t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": true,
|
"sensitive": true,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -71,6 +72,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": true,
|
"sensitive": true,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "baz"
|
"value": "baz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -62,6 +63,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -94,6 +95,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -73,6 +74,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"foo_id": {
|
"foo_id": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "placeholder"
|
"value": "placeholder"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -37,6 +38,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"foo_id": {
|
"foo_id": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "placeholder"
|
"value": "placeholder"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "baz"
|
"value": "baz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -79,6 +80,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "baz"
|
"value": "baz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -113,6 +114,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -62,6 +63,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -62,6 +63,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
"value": "bar"
|
"value": "bar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": true,
|
"sensitive": true,
|
||||||
|
"type": "string",
|
||||||
"value": "boop"
|
"value": "boop"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -74,6 +75,7 @@
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"test": {
|
"test": {
|
||||||
"sensitive": true,
|
"sensitive": true,
|
||||||
|
"type": "string",
|
||||||
"value": "boop"
|
"value": "boop"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -217,6 +217,7 @@ The following example illustrates the structure of a `<values-representation>`:
|
|||||||
"outputs": {
|
"outputs": {
|
||||||
"private_ip": {
|
"private_ip": {
|
||||||
"value": "192.168.3.2",
|
"value": "192.168.3.2",
|
||||||
|
"type": "string",
|
||||||
"sensitive": false
|
"sensitive": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -307,6 +308,8 @@ The following example illustrates the structure of a `<values-representation>`:
|
|||||||
|
|
||||||
The translation of attribute and output values is the same intuitive mapping from HCL types to JSON types used by Terraform's [`jsonencode`](/language/functions/jsonencode) function. This mapping does lose some information: lists, sets, and tuples all lower to JSON arrays while maps and objects both lower to JSON objects. Unknown values and null values are both treated as absent or null.
|
The translation of attribute and output values is the same intuitive mapping from HCL types to JSON types used by Terraform's [`jsonencode`](/language/functions/jsonencode) function. This mapping does lose some information: lists, sets, and tuples all lower to JSON arrays while maps and objects both lower to JSON objects. Unknown values and null values are both treated as absent or null.
|
||||||
|
|
||||||
|
Output values include a `"type"` field, which is a [serialization of the value's type](https://pkg.go.dev/github.com/zclconf/go-cty/cty#Type.MarshalJSON). For primitive types this is a string value, such as `"number"` or `"bool"`. Complex types are represented as a nested JSON array, such as `["map","string"]` or `["object",{"a":"number"}]`. This can be used to reconstruct the output value with the correct type.
|
||||||
|
|
||||||
Only the "current" object for each resource instance is described. "Deposed" objects are not reflected in this structure at all; in plan representations, you can refer to the change representations for further details.
|
Only the "current" object for each resource instance is described. "Deposed" objects are not reflected in this structure at all; in plan representations, you can refer to the change representations for further details.
|
||||||
|
|
||||||
The intent of this structure is to give a caller access to a similar level of detail as is available to expressions within the configuration itself. This common representation is not suitable for all use-cases because it loses information compared to the data structures it is built from. For more complex needs, use the more elaborate changes and configuration representations.
|
The intent of this structure is to give a caller access to a similar level of detail as is available to expressions within the configuration itself. This common representation is not suitable for all use-cases because it loses information compared to the data structures it is built from. For more complex needs, use the more elaborate changes and configuration representations.
|
||||||
|
Loading…
Reference in New Issue
Block a user