mirror of
https://github.com/terraform-docs/terraform-docs.git
synced 2026-03-27 12:58:35 +07:00
feat: Show sensitivity of the output value in rendered result (#207)
This commit is contained in:
@@ -29,6 +29,8 @@ var rootCmd = &cobra.Command{
|
||||
settings.ShowInputs = oppositeBool("no-inputs")
|
||||
settings.ShowOutputs = oppositeBool("no-outputs")
|
||||
|
||||
settings.OutputValues = options.OutputValues
|
||||
|
||||
settings.ShowColor = oppositeBool("no-color")
|
||||
settings.SortByName = oppositeBool("no-sort")
|
||||
settings.ShowRequired = oppositeBool("no-required")
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
{
|
||||
"output-0.12": {
|
||||
"sensitive": false,
|
||||
"type": "string",
|
||||
"value": ""
|
||||
"sensitive": true,
|
||||
"type": "string",
|
||||
"value": "sensitive-content-should-be-hidden"
|
||||
},
|
||||
"output-1": {
|
||||
"sensitive": false,
|
||||
"type": "int",
|
||||
"value": 1
|
||||
"sensitive": false,
|
||||
"type": "int",
|
||||
"value": 1
|
||||
},
|
||||
"output-2": {
|
||||
"sensitive": false,
|
||||
"type": "array",
|
||||
"value": ["jack", "lola"]
|
||||
"sensitive": false,
|
||||
"type": "array",
|
||||
"value": [
|
||||
"jack",
|
||||
"lola"
|
||||
]
|
||||
},
|
||||
"unquoted": {
|
||||
"sensitive": false,
|
||||
"type": "map",
|
||||
"value": {"leon":"cat"}
|
||||
"sensitive": false,
|
||||
"type": "map",
|
||||
"value": {
|
||||
"leon": "cat"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ const (
|
||||
Type: {{ tostring .Type | type }}
|
||||
|
||||
{{ if or .HasDefault (not isRequired) }}
|
||||
Default: {{ default "n/a" .Value | value }}
|
||||
Default: {{ default "n/a" .GetValue | value }}
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
@@ -93,8 +93,12 @@ const (
|
||||
{{ indent 1 }} {{ name .Name }}
|
||||
|
||||
Description: {{ tostring .Description | sanitizeDoc }}
|
||||
{{ if $.Settings.OutputValues }}
|
||||
Value: {{ .Value | sanitizeInterface | sanitizeDoc }}
|
||||
|
||||
{{ if $.Settings.OutputValues }}
|
||||
{{- $sensitive := ternary .Sensitive "<sensitive>" .GetValue -}}
|
||||
Value: {{ value $sensitive | sanitizeDoc }}
|
||||
|
||||
Sensitive: {{ ternary (.Sensitive) "yes" "no" }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -280,6 +280,7 @@ func TestJsonEscapeCharacters(t *testing.T) {
|
||||
assert.Nil(err)
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
func TestJsonOutputValues(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
settings := testutil.Settings().WithSections().With(&print.Settings{
|
||||
|
||||
@@ -38,7 +38,7 @@ const (
|
||||
{{- with .Module.Inputs }}
|
||||
{{- printf "\n" -}}
|
||||
{{- range . }}
|
||||
{{ printf "input.%s" .Name | colorize "\033[36m" }} ({{ default "required" .Value }})
|
||||
{{ printf "input.%s" .Name | colorize "\033[36m" }} ({{ default "required" .GetValue }})
|
||||
{{ tostring .Description | trimSuffix "\n" | default "n/a" | colorize "\033[90m" }}
|
||||
{{ end }}
|
||||
{{- printf "\n" -}}
|
||||
@@ -54,7 +54,7 @@ const (
|
||||
{{ printf "output.%s" .Name | colorize "\033[36m" }}
|
||||
{{- if $.Settings.OutputValues -}}
|
||||
{{- printf " " -}}
|
||||
({{ sanitizeInterface .Value }})
|
||||
({{ ternary .Sensitive "<sensitive>" .GetValue }})
|
||||
{{- end }}
|
||||
{{ tostring .Description | trimSuffix "\n" | default "n/a" | colorize "\033[90m" }}
|
||||
{{ end }}
|
||||
|
||||
@@ -39,19 +39,13 @@ const (
|
||||
{{ if not .Module.Inputs }}
|
||||
No input.
|
||||
{{ else }}
|
||||
{{ if not .Settings.ShowRequired }}
|
||||
| Name | Description | Type | Default |
|
||||
|------|-------------|------|---------|
|
||||
{{- else }}
|
||||
| Name | Description | Type | Default | Required |
|
||||
|------|-------------|------|---------|:--------:|
|
||||
{{- end }}
|
||||
| Name | Description | Type | Default |{{ if .Settings.ShowRequired }} Required |{{ end }}
|
||||
|------|-------------|------|---------|{{ if .Settings.ShowRequired }}:--------:|{{ end }}
|
||||
{{- range .Module.Inputs }}
|
||||
{{- if not $.Settings.ShowRequired }}
|
||||
| {{ name .Name }} | {{ tostring .Description | sanitizeTbl }} | {{ tostring .Type | type | sanitizeTbl }} | {{ value .Value | sanitizeTbl }} |
|
||||
{{- else }}
|
||||
| {{ name .Name }} | {{ tostring .Description | sanitizeTbl }} | {{ tostring .Type | type | sanitizeTbl }} | {{ value .Value | sanitizeTbl }} | {{ ternary (.Value) "no" "yes" }} |
|
||||
{{- end }}
|
||||
| {{ name .Name }} | {{ tostring .Description | sanitizeTbl }} | {{ tostring .Type | type | sanitizeTbl }} | {{ value .GetValue | sanitizeTbl }} |
|
||||
{{- if $.Settings.ShowRequired -}}
|
||||
{{ printf " " }}{{ ternary (.GetValue) "no" "yes" }} |
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
@@ -63,10 +57,14 @@ const (
|
||||
{{ if not .Module.Outputs }}
|
||||
No output.
|
||||
{{ else }}
|
||||
| Name | Description |{{ if $.Settings.OutputValues }} Value |{{ end }}
|
||||
|------|-------------|{{ if $.Settings.OutputValues }}-------|{{ end }}
|
||||
| Name | Description |{{ if .Settings.OutputValues }} Value | Sensitive |{{ end }}
|
||||
|------|-------------|{{ if .Settings.OutputValues }}-------|:---------:|{{ end }}
|
||||
{{- range .Module.Outputs }}
|
||||
| {{ name .Name }} | {{ tostring .Description | sanitizeTbl }} |{{ if $.Settings.OutputValues }} {{ .Value | sanitizeInterface | sanitizeTbl }} |{{ end }}
|
||||
| {{ name .Name }} | {{ tostring .Description | sanitizeTbl }} |
|
||||
{{- if $.Settings.OutputValues -}}
|
||||
{{- $sensitive := ternary .Sensitive "<sensitive>" .GetValue -}}
|
||||
{{ printf " " }}{{ value $sensitive | sanitizeTbl }} | {{ ternary (.Sensitive) "yes" "no" }} |
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
|
||||
@@ -303,22 +303,43 @@ The following outputs are exported:
|
||||
|
||||
Description: It's unquoted output.
|
||||
|
||||
Value: map["leon":"cat"]
|
||||
Value:
|
||||
|
||||
```json
|
||||
{
|
||||
"leon": "cat"
|
||||
}
|
||||
```
|
||||
|
||||
Sensitive: no
|
||||
|
||||
### output-2
|
||||
|
||||
Description: It's output number two.
|
||||
|
||||
Value: ["jack" "lola"]
|
||||
Value:
|
||||
|
||||
```json
|
||||
[
|
||||
"jack",
|
||||
"lola"
|
||||
]
|
||||
```
|
||||
|
||||
Sensitive: no
|
||||
|
||||
### output-1
|
||||
|
||||
Description: It's output number one.
|
||||
|
||||
Value: 1
|
||||
Value: `1`
|
||||
|
||||
Sensitive: no
|
||||
|
||||
### output-0.12
|
||||
|
||||
Description: terraform 0.12 only
|
||||
|
||||
Value: ""
|
||||
Value: `<sensitive>`
|
||||
|
||||
Sensitive: yes
|
||||
|
||||
@@ -171,7 +171,8 @@
|
||||
"description": "It's unquoted output.",
|
||||
"value": {
|
||||
"leon": "cat"
|
||||
}
|
||||
},
|
||||
"sensitive": false
|
||||
},
|
||||
{
|
||||
"name": "output-2",
|
||||
@@ -179,17 +180,20 @@
|
||||
"value": [
|
||||
"jack",
|
||||
"lola"
|
||||
]
|
||||
],
|
||||
"sensitive": false
|
||||
},
|
||||
{
|
||||
"name": "output-1",
|
||||
"description": "It's output number one.",
|
||||
"value": 1
|
||||
"value": 1,
|
||||
"sensitive": false
|
||||
},
|
||||
{
|
||||
"name": "output-0.12",
|
||||
"description": "terraform 0.12 only",
|
||||
"value": ""
|
||||
"value": "<sensitive>",
|
||||
"sensitive": true
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
|
||||
@@ -154,15 +154,20 @@ It spans over multiple lines.[0m
|
||||
|
||||
|
||||
|
||||
[36moutput.unquoted[0m (map["leon":"cat"])
|
||||
[36moutput.unquoted[0m ({
|
||||
"leon": "cat"
|
||||
})
|
||||
[90mIt's unquoted output.[0m
|
||||
|
||||
[36moutput.output-2[0m (["jack" "lola"])
|
||||
[36moutput.output-2[0m ([
|
||||
"jack",
|
||||
"lola"
|
||||
])
|
||||
[90mIt's output number two.[0m
|
||||
|
||||
[36moutput.output-1[0m (1)
|
||||
[90mIt's output number one.[0m
|
||||
|
||||
[36moutput.output-0.12[0m ("")
|
||||
[36moutput.output-0.12[0m (<sensitive>)
|
||||
[90mterraform 0.12 only[0m
|
||||
|
||||
|
||||
@@ -75,9 +75,9 @@ followed by another line of text.
|
||||
|
||||
## Outputs
|
||||
|
||||
| Name | Description | Value |
|
||||
|------|-------------|-------|
|
||||
| unquoted | It's unquoted output. | map["leon":"cat"] |
|
||||
| output-2 | It's output number two. | ["jack" "lola"] |
|
||||
| output-1 | It's output number one. | 1 |
|
||||
| output-0.12 | terraform 0.12 only | "" |
|
||||
| Name | Description | Value | Sensitive |
|
||||
|------|-------------|-------|:---------:|
|
||||
| unquoted | It's unquoted output. | <pre>{<br> "leon": "cat"<br>}</pre> | no |
|
||||
| output-2 | It's output number two. | <pre>[<br> "jack",<br> "lola"<br>]</pre> | no |
|
||||
| output-1 | It's output number one. | `1` | no |
|
||||
| output-0.12 | terraform 0.12 only | `<sensitive>` | yes |
|
||||
|
||||
@@ -163,17 +163,21 @@ outputs:
|
||||
description: It's unquoted output.
|
||||
value:
|
||||
leon: cat
|
||||
sensitive: false
|
||||
- name: output-2
|
||||
description: It's output number two.
|
||||
value:
|
||||
- jack
|
||||
- lola
|
||||
sensitive: false
|
||||
- name: output-1
|
||||
description: It's output number one.
|
||||
value: 1
|
||||
sensitive: false
|
||||
- name: output-0.12
|
||||
description: terraform 0.12 only
|
||||
value: ""
|
||||
value: <sensitive>
|
||||
sensitive: true
|
||||
providers:
|
||||
- name: tls
|
||||
alias: null
|
||||
|
||||
@@ -113,6 +113,14 @@ func loadInputs(tfmodule *tfconfig.Module) ([]*tfconf.Input, []*tfconf.Input, []
|
||||
|
||||
func loadOutputs(tfmodule *tfconfig.Module, options *Options) []*tfconf.Output {
|
||||
outputs := make([]*tfconf.Output, 0, len(tfmodule.Outputs))
|
||||
values := make(map[string]*TerraformOutput, 0)
|
||||
if options.OutputValues {
|
||||
var err error
|
||||
values, err = loadOutputValues(options)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, o := range tfmodule.Outputs {
|
||||
description := o.Description
|
||||
if description == "" {
|
||||
@@ -125,16 +133,14 @@ func loadOutputs(tfmodule *tfconfig.Module, options *Options) []*tfconf.Output {
|
||||
Filename: o.Pos.Filename,
|
||||
Line: o.Pos.Line,
|
||||
},
|
||||
ShowValue: options.OutputValues,
|
||||
}
|
||||
if options.OutputValues {
|
||||
terraformOutputs, err := loadOutputValues(options)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if terraformOutputs[output.Name].Sensitive {
|
||||
output.Value = "<sensitive>"
|
||||
output.Sensitive = values[output.Name].Sensitive
|
||||
if values[output.Name].Sensitive {
|
||||
output.Value = types.ValueOf(`<sensitive>`)
|
||||
} else {
|
||||
output.Value = terraformOutputs[output.Name].Value
|
||||
output.Value = types.ValueOf(values[output.Name].Value)
|
||||
}
|
||||
}
|
||||
outputs = append(outputs, output)
|
||||
|
||||
@@ -7,8 +7,9 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Default is a default value of an input
|
||||
// Value is a default value of an input or output.
|
||||
// it can be of several types:
|
||||
//
|
||||
// - Nil
|
||||
// - String
|
||||
// - Empty
|
||||
@@ -16,15 +17,15 @@ import (
|
||||
// - Bool
|
||||
// - List
|
||||
// - Map
|
||||
type Default interface {
|
||||
type Value interface {
|
||||
HasDefault() bool
|
||||
}
|
||||
|
||||
// ValueOf returns actual value of a variable
|
||||
// casted to 'Default' interface. This is done
|
||||
// casted to 'Value' interface. This is done
|
||||
// to be able to attach specific marshaller func
|
||||
// to the type (if such a custom function was needed)
|
||||
func ValueOf(v interface{}) Default {
|
||||
func ValueOf(v interface{}) Value {
|
||||
if v == nil {
|
||||
return new(Nil)
|
||||
}
|
||||
|
||||
@@ -8,17 +8,17 @@ import (
|
||||
|
||||
// Input represents a Terraform input.
|
||||
type Input struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Type types.String `json:"type" yaml:"type"`
|
||||
Description types.String `json:"description" yaml:"description"`
|
||||
Default types.Default `json:"default" yaml:"default"`
|
||||
Position Position `json:"-" yaml:"-"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Type types.String `json:"type" yaml:"type"`
|
||||
Description types.String `json:"description" yaml:"description"`
|
||||
Default types.Value `json:"default" yaml:"default"`
|
||||
Position Position `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
// Value returns JSON representation of the 'Default' value, which is an 'interface'.
|
||||
// GetValue returns JSON representation of the 'Default' value, which is an 'interface'.
|
||||
// If 'Default' is a primitive type, the primitive value of 'Default' will be returned
|
||||
// and not the JSON formatted of it.
|
||||
func (i *Input) Value() string {
|
||||
func (i *Input) GetValue() string {
|
||||
marshaled, err := json.MarshalIndent(i.Default, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package tfconf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/segmentio/terraform-docs/internal/types"
|
||||
)
|
||||
|
||||
@@ -8,6 +11,72 @@ import (
|
||||
type Output struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Description types.String `json:"description" yaml:"description"`
|
||||
Value interface{} `json:"value,omitempty" yaml:"value,omitempty"`
|
||||
Value types.Value `json:"value,omitempty" yaml:"value,omitempty"`
|
||||
Sensitive bool `json:"sensitive,omitempty" yaml:"sensitive,omitempty"`
|
||||
Position Position `json:"-" yaml:"-"`
|
||||
ShowValue bool `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
type withvalue struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Description types.String `json:"description" yaml:"description"`
|
||||
Value types.Value `json:"value" yaml:"value"`
|
||||
Sensitive bool `json:"sensitive" yaml:"sensitive"`
|
||||
Position Position `json:"-" yaml:"-"`
|
||||
ShowValue bool `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
// GetValue returns JSON representation of the 'Value', which is an 'interface'.
|
||||
// If 'Value' is a primitive type, the primitive value of 'Value' will be returned
|
||||
// and not the JSON formatted of it.
|
||||
func (o *Output) GetValue() string {
|
||||
marshaled, err := json.MarshalIndent(o.Value, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if value := string(marshaled); value != "null" {
|
||||
return value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// HasDefault indicates if a Terraform output has a default value set.
|
||||
func (o *Output) HasDefault() bool {
|
||||
return o.Value.HasDefault()
|
||||
}
|
||||
|
||||
// MarshalJSON custom yaml marshal function to take
|
||||
// '--output-values' flag into consideration. It means
|
||||
// if the flag is not set Value and Sensitive fields
|
||||
// are set to 'omitempty', otherwise if output values
|
||||
// are being shown 'omitempty' gets explicitly removed
|
||||
// to show even empty and false values.
|
||||
func (o *Output) MarshalJSON() ([]byte, error) {
|
||||
fn := func(oo interface{}) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetEscapeHTML(false)
|
||||
if err := enc.Encode(oo); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
if o.ShowValue {
|
||||
return fn(withvalue(*o))
|
||||
}
|
||||
return fn(*o)
|
||||
|
||||
}
|
||||
|
||||
// MarshalYAML custom yaml marshal function to take
|
||||
// '--output-values' flag into consideration. It means
|
||||
// if the flag is not set Value and Sensitive fields
|
||||
// are set to 'omitempty', otherwise if output values
|
||||
// are being shown 'omitempty' gets explicitly removed
|
||||
// to show even empty and false values.
|
||||
func (o *Output) MarshalYAML() (interface{}, error) {
|
||||
if o.ShowValue {
|
||||
return withvalue(*o), nil
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
@@ -161,24 +161,6 @@ func builtinFuncs(settings *print.Settings) template.FuncMap {
|
||||
"sanitizeTbl": func(s string) string {
|
||||
return sanitizeItemForTable(s, settings)
|
||||
},
|
||||
"sanitizeInterface": func(i interface{}) string {
|
||||
var v string
|
||||
switch x := fmt.Sprintf("%T", i); x {
|
||||
case "[]interface {}":
|
||||
v = fmt.Sprintf("%q", i)
|
||||
case "map[string]interface {}":
|
||||
v = fmt.Sprintf("%q", i)
|
||||
case "float64":
|
||||
v = fmt.Sprintf("%g", i)
|
||||
case "int":
|
||||
v = fmt.Sprintf("%d", i)
|
||||
case "string":
|
||||
v = fmt.Sprintf("%#v", i)
|
||||
case "bool":
|
||||
v = fmt.Sprintf("%t", i)
|
||||
}
|
||||
return v
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user