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 {
|
||||
Sensitive bool `json:"sensitive"`
|
||||
Type json.RawMessage `json:"type,omitempty"`
|
||||
Value json.RawMessage `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ func marshalPlannedOutputs(changes *plans.Changes) (map[string]output, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
var after []byte
|
||||
var after, afterType []byte
|
||||
changeV, err := oc.Decode()
|
||||
if err != nil {
|
||||
return ret, err
|
||||
@ -68,7 +68,12 @@ func marshalPlannedOutputs(changes *plans.Changes) (map[string]output, error) {
|
||||
changeV.After, _ = changeV.After.UnmarkDeep()
|
||||
|
||||
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 {
|
||||
return ret, err
|
||||
}
|
||||
@ -76,6 +81,7 @@ func marshalPlannedOutputs(changes *plans.Changes) (map[string]output, error) {
|
||||
|
||||
ret[oc.Addr.OutputValue.Name] = output{
|
||||
Value: json.RawMessage(after),
|
||||
Type: json.RawMessage(afterType),
|
||||
Sensitive: oc.Sensitive,
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +137,7 @@ func TestMarshalPlannedOutputs(t *testing.T) {
|
||||
map[string]output{
|
||||
"bar": {
|
||||
Sensitive: false,
|
||||
Type: json.RawMessage(`"string"`),
|
||||
Value: json.RawMessage(`"after"`),
|
||||
},
|
||||
},
|
||||
|
@ -38,6 +38,7 @@ type stateValues struct {
|
||||
type output struct {
|
||||
Sensitive bool `json:"sensitive"`
|
||||
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
|
||||
@ -180,12 +181,18 @@ func marshalOutputs(outputs map[string]*states.OutputValue) (map[string]output,
|
||||
|
||||
ret := make(map[string]output)
|
||||
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 {
|
||||
return ret, err
|
||||
}
|
||||
ret[k] = output{
|
||||
Value: ov,
|
||||
Type: ot,
|
||||
Sensitive: v.Sensitive,
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ func TestMarshalOutputs(t *testing.T) {
|
||||
"test": {
|
||||
Sensitive: true,
|
||||
Value: json.RawMessage(`"sekret"`),
|
||||
Type: json.RawMessage(`"string"`),
|
||||
},
|
||||
},
|
||||
false,
|
||||
@ -51,6 +52,39 @@ func TestMarshalOutputs(t *testing.T) {
|
||||
"test": {
|
||||
Sensitive: false,
|
||||
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,
|
||||
@ -67,10 +101,8 @@ func TestMarshalOutputs(t *testing.T) {
|
||||
} else if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
eq := reflect.DeepEqual(got, test.Want)
|
||||
if !eq {
|
||||
// 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)
|
||||
if !cmp.Equal(test.Want, got) {
|
||||
t.Fatalf("wrong result:\n%s", cmp.Diff(test.Want, got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": true,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
@ -71,6 +72,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": true,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
|
@ -5,6 +5,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "baz"
|
||||
}
|
||||
},
|
||||
|
@ -9,6 +9,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
@ -62,6 +63,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
|
@ -9,6 +9,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
@ -94,6 +95,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
|
@ -9,6 +9,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
@ -73,6 +74,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
|
@ -13,6 +13,7 @@
|
||||
"outputs": {
|
||||
"foo_id": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "placeholder"
|
||||
}
|
||||
},
|
||||
@ -37,6 +38,7 @@
|
||||
"outputs": {
|
||||
"foo_id": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "placeholder"
|
||||
}
|
||||
},
|
||||
|
@ -4,6 +4,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "baz"
|
||||
}
|
||||
},
|
||||
@ -79,6 +80,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "baz"
|
||||
}
|
||||
},
|
||||
|
@ -10,6 +10,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
@ -113,6 +114,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
|
@ -9,6 +9,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
@ -62,6 +63,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
|
@ -9,6 +9,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
@ -62,6 +63,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
|
@ -9,6 +9,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": true,
|
||||
"type": "string",
|
||||
"value": "boop"
|
||||
}
|
||||
},
|
||||
@ -74,6 +75,7 @@
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": true,
|
||||
"type": "string",
|
||||
"value": "boop"
|
||||
}
|
||||
},
|
||||
|
@ -217,6 +217,7 @@ The following example illustrates the structure of a `<values-representation>`:
|
||||
"outputs": {
|
||||
"private_ip": {
|
||||
"value": "192.168.3.2",
|
||||
"type": "string",
|
||||
"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.
|
||||
|
||||
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.
|
||||
|
||||
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