feat: Extract and render output values from Terraform (#191)

* Allow users to pass '--output-values'

Pass empty string to CreateModule.outputValuePath

* Fix bug causing 'pretty' tests to fail

* Link formats documentation in README (#181)

Co-authored-by: Khosrow Moossavi <khos2ow@gmail.com>

* docs: Auto generate formats document from examples (#192)

* Auto generate formats document from examples

* fix lint issues

* refactor: Add tfconf.Options to load Module with (#193)

* Update Changelog

* Allow users to pass '--output-values'

Read the outputValuesPath from an env variable

Use an env var with a path for '--output-values'

Update Changelog

Use an env var with a path for '--output-values'

Update Changelog

properly write output values for evrythng but yaml

* Fix failing json+yaml tests

* Remove unneeded code block from output.go

* Remove unused import statement from output.go

* Fix some noob mistakes

* Create two flags to use for output value injection

* Fix bug vanilla commands+build test to fail

* Modify all tests and add new for outputvalues

* Modify to include many output types

* Optimize imports to appease checkfmt

* Create loadOutputValues function

* Fix linter issue

* Code review fixes. Hopefully the final commit!

* appease linter

* Not allow sensitive output values to be injected

* Remove trailing slash from tests

* Remove default values from `--output-values-from`

Co-authored-by: Martyn Ranyard <iMartyn@users.noreply.github.com>
Co-authored-by: Khosrow Moossavi <khos2ow@gmail.com>
This commit is contained in:
Gretchen Shelby-Dormer
2020-02-19 11:46:43 -05:00
committed by GitHub
parent b86f6715f9
commit 31cdef0f67
22 changed files with 1359 additions and 112 deletions

View File

@@ -11,6 +11,7 @@ import (
)
var settings = print.NewSettings()
var options = tfconf.Options{}
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
@@ -35,6 +36,8 @@ var rootCmd = &cobra.Command{
settings.ShowInputs = !noinputs
settings.ShowOutputs = !nooutputs
settings.OutputValues = options.OutputValues
settings.ShowColor = !nocolor
settings.SortByName = !nosort
settings.ShowRequired = !norequired
@@ -51,6 +54,9 @@ func init() {
rootCmd.PersistentFlags().BoolVar(new(bool), "no-sort", false, "do no sort items")
rootCmd.PersistentFlags().BoolVar(&settings.SortByRequired, "sort-by-required", false, "sort items by name and print required ones first")
rootCmd.PersistentFlags().BoolVar(&options.OutputValues, "output-values", false, "inject output values into outputs")
rootCmd.PersistentFlags().StringVar(&options.OutputValuesPath, "output-values-from", "", "inject output values from file into outputs")
//-----------------------------
// deprecated - will be removed
//-----------------------------
@@ -87,10 +93,8 @@ func FormatterCmds() []*cobra.Command {
}
func doPrint(path string, fn func(*tfconf.Module) (string, error)) {
options := &tfconf.Options{
Path: path,
}
module, err := tfconf.CreateModule(options)
options.Path = path
module, err := tfconf.CreateModule(&options)
if err != nil {
log.Fatal(err)
}

View File

@@ -0,0 +1,23 @@
{
"output-0.12": {
"sensitive": false,
"type": "string",
"value": ""
},
"output-1": {
"sensitive": false,
"type": "int",
"value": 1
},
"output-2": {
"sensitive": false,
"type": "array",
"value": ["jack", "lola"]
},
"unquoted": {
"sensitive": false,
"type": "map",
"value": {"leon":"cat"}
}
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/segmentio/terraform-docs/internal/pkg/print"
"github.com/segmentio/terraform-docs/internal/pkg/testutil"
"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
"github.com/stretchr/testify/assert"
)
@@ -12,7 +13,10 @@ func TestJson(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().Build()
module, expected, err := testutil.GetExpected("json")
expected, err := testutil.GetExpected("json")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -27,7 +31,10 @@ func TestJsonSortByName(t *testing.T) {
SortByName: true,
}).Build()
module, expected, err := testutil.GetExpected("json-SortByName")
expected, err := testutil.GetExpected("json-SortByName")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -43,7 +50,10 @@ func TestJsonSortByRequired(t *testing.T) {
SortByRequired: true,
}).Build()
module, expected, err := testutil.GetExpected("json-SortByRequired")
expected, err := testutil.GetExpected("json-SortByRequired")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -61,7 +71,10 @@ func TestJsonNoHeader(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("json-NoHeader")
expected, err := testutil.GetExpected("json-NoHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -79,7 +92,10 @@ func TestJsonNoProviders(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("json-NoProviders")
expected, err := testutil.GetExpected("json-NoProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -97,7 +113,10 @@ func TestJsonNoInputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("json-NoInputs")
expected, err := testutil.GetExpected("json-NoInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -115,7 +134,10 @@ func TestJsonNoOutputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("json-NoOutputs")
expected, err := testutil.GetExpected("json-NoOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -133,7 +155,10 @@ func TestJsonOnlyHeader(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("json-OnlyHeader")
expected, err := testutil.GetExpected("json-OnlyHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -151,7 +176,10 @@ func TestJsonOnlyProviders(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("json-OnlyProviders")
expected, err := testutil.GetExpected("json-OnlyProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -169,7 +197,10 @@ func TestJsonOnlyInputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("json-OnlyInputs")
expected, err := testutil.GetExpected("json-OnlyInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -187,7 +218,10 @@ func TestJsonOnlyOutputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("json-OnlyOutputs")
expected, err := testutil.GetExpected("json-OnlyOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -202,7 +236,31 @@ func TestJsonEscapeCharacters(t *testing.T) {
EscapeCharacters: true,
}).Build()
module, expected, err := testutil.GetExpected("json-EscapeCharacters")
expected, err := testutil.GetExpected("json-EscapeCharacters")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
assert.Nil(err)
assert.Equal(expected, actual)
}
func TestJsonOutputValues(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().With(&print.Settings{
OutputValues: true,
}).Build()
expected, err := testutil.GetExpected("json-OutputValues")
assert.Nil(err)
options := &tfconf.Options{
OutputValues: true,
OutputValuesPath: "output_values.json",
}
module, err := testutil.GetModule(options)
assert.Nil(err)
actual, err := Print(module, settings)

View File

@@ -0,0 +1,175 @@
{
"header": "Usage:\n\nExample of 'foo_bar' module in `foo_bar.tf`.\n\n- list item 1\n- list item 2\n\nEven inline **formatting** in _here_ is possible.\nand some [link](https://domain.com/)\n\n* list item 3\n* list item 4\n\n```hcl\nmodule \"foo_bar\" {\n source = \"github.com/foo/bar\"\n\n id = \"1234567890\"\n name = \"baz\"\n\n zones = [\"us-east-1\", \"us-west-1\"]\n\n tags = {\n Name = \"baz\"\n Created-By = \"first.last@email.com\"\n Date-Created = \"20180101\"\n }\n}\n```\n\nHere is some trailing text after code block,\nfollowed by another line of text.\n\n| Name | Description |\n|------|-----------------|\n| Foo | Foo description |\n| Bar | Bar description |",
"inputs": [
{
"name": "unquoted",
"type": "any",
"description": null,
"default": null
},
{
"name": "string-3",
"type": "string",
"description": null,
"default": ""
},
{
"name": "string-2",
"type": "string",
"description": "It's string number two.",
"default": null
},
{
"name": "string-1",
"type": "string",
"description": "It's string number one.",
"default": "bar"
},
{
"name": "map-3",
"type": "map",
"description": null,
"default": {}
},
{
"name": "map-2",
"type": "map",
"description": "It's map number two.",
"default": null
},
{
"name": "map-1",
"type": "map",
"description": "It's map number one.",
"default": {
"a": 1,
"b": 2,
"c": 3
}
},
{
"name": "list-3",
"type": "list",
"description": null,
"default": []
},
{
"name": "list-2",
"type": "list",
"description": "It's list number two.",
"default": null
},
{
"name": "list-1",
"type": "list",
"description": "It's list number one.",
"default": [
"a",
"b",
"c"
]
},
{
"name": "input_with_underscores",
"type": "any",
"description": "A variable with underscores.",
"default": null
},
{
"name": "input-with-pipe",
"type": "string",
"description": "It includes v1 | v2 | v3",
"default": "v1"
},
{
"name": "input-with-code-block",
"type": "list",
"description": "This is a complicated one. We need a newline. \nAnd an example in a code block\n```\ndefault = [\n \"machine rack01:neptune\"\n]\n```\n",
"default": [
"name rack:location"
]
},
{
"name": "long_type",
"type": "object({\n name = string,\n foo = object({ foo = string, bar = string }),\n bar = object({ foo = string, bar = string }),\n fizz = list(string),\n buzz = list(string)\n })",
"description": "This description is itself markdown.\n\nIt spans over multiple lines.\n",
"default": {
"bar": {
"bar": "bar",
"foo": "bar"
},
"buzz": [
"fizz",
"buzz"
],
"fizz": [],
"foo": {
"bar": "foo",
"foo": "foo"
},
"name": "hello"
}
},
{
"name": "no-escape-default-value",
"type": "string",
"description": "The description contains `something_with_underscore`. Defaults to 'VALUE_WITH_UNDERSCORE'.",
"default": "VALUE_WITH_UNDERSCORE"
},
{
"name": "with-url",
"type": "string",
"description": "The description contains url. https://www.domain.com/foo/bar_baz.html",
"default": ""
}
],
"outputs": [
{
"name": "unquoted",
"description": "It's unquoted output.",
"value": {
"leon": "cat"
}
},
{
"name": "output-2",
"description": "It's output number two.",
"value": [
"jack",
"lola"
]
},
{
"name": "output-1",
"description": "It's output number one.",
"value": 1
},
{
"name": "output-0.12",
"description": "terraform 0.12 only",
"value": ""
}
],
"providers": [
{
"name": "tls",
"alias": null,
"version": null
},
{
"name": "aws",
"alias": null,
"version": ">= 2.15.0"
},
{
"name": "aws",
"alias": "ident",
"version": ">= 2.15.0"
},
{
"name": "null",
"alias": null,
"version": null
}
]
}

View File

@@ -94,7 +94,10 @@ const (
{{ indent 1 }} {{ name .Name }}
Description: {{ tostring .Description | sanitizeDoc }}
{{- end }}
{{ if $.Settings.OutputValues }}
Value: {{ .Value | sanitizeInterface | sanitizeDoc }}
{{ end }}
{{ end }}
{{ end }}
{{ end -}}
`

View File

@@ -5,6 +5,7 @@ import (
"github.com/segmentio/terraform-docs/internal/pkg/print"
"github.com/segmentio/terraform-docs/internal/pkg/testutil"
"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
"github.com/stretchr/testify/assert"
)
@@ -12,7 +13,10 @@ func TestDocument(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().Build()
module, expected, err := testutil.GetExpected("document")
expected, err := testutil.GetExpected("document")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -27,7 +31,10 @@ func TestDocumentWithRequired(t *testing.T) {
ShowRequired: true,
}).Build()
module, expected, err := testutil.GetExpected("document-WithRequired")
expected, err := testutil.GetExpected("document-WithRequired")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -42,7 +49,10 @@ func TestDocumentSortByName(t *testing.T) {
SortByName: true,
}).Build()
module, expected, err := testutil.GetExpected("document-SortByName")
expected, err := testutil.GetExpected("document-SortByName")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -58,7 +68,10 @@ func TestDocumentSortByRequired(t *testing.T) {
SortByRequired: true,
}).Build()
module, expected, err := testutil.GetExpected("document-SortByRequired")
expected, err := testutil.GetExpected("document-SortByRequired")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -76,7 +89,10 @@ func TestDocumentNoHeader(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("document-NoHeader")
expected, err := testutil.GetExpected("document-NoHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -94,7 +110,10 @@ func TestDocumentNoProviders(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("document-NoProviders")
expected, err := testutil.GetExpected("document-NoProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -112,7 +131,10 @@ func TestDocumentNoInputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("document-NoInputs")
expected, err := testutil.GetExpected("document-NoInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -130,7 +152,10 @@ func TestDocumentNoOutputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("document-NoOutputs")
expected, err := testutil.GetExpected("document-NoOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -148,7 +173,10 @@ func TestDocumentOnlyHeader(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("document-OnlyHeader")
expected, err := testutil.GetExpected("document-OnlyHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -166,7 +194,10 @@ func TestDocumentOnlyProviders(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("document-OnlyProviders")
expected, err := testutil.GetExpected("document-OnlyProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -184,7 +215,10 @@ func TestDocumentOnlyInputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("document-OnlyInputs")
expected, err := testutil.GetExpected("document-OnlyInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -202,7 +236,10 @@ func TestDocumentOnlyOutputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("document-OnlyOutputs")
expected, err := testutil.GetExpected("document-OnlyOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -217,7 +254,10 @@ func TestDocumentEscapeCharacters(t *testing.T) {
EscapeCharacters: true,
}).Build()
module, expected, err := testutil.GetExpected("document-EscapeCharacters")
expected, err := testutil.GetExpected("document-EscapeCharacters")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -226,13 +266,16 @@ func TestDocumentEscapeCharacters(t *testing.T) {
assert.Equal(expected, actual)
}
func TestDocumentIndentationBellowAllowed(t *testing.T) {
func TestDocumentIndentationBelowAllowed(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().With(&print.Settings{
MarkdownIndent: 0,
}).Build()
module, expected, err := testutil.GetExpected("document-IndentationBellowAllowed")
expected, err := testutil.GetExpected("document-IndentationBelowAllowed")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -247,7 +290,10 @@ func TestDocumentIndentationAboveAllowed(t *testing.T) {
MarkdownIndent: 10,
}).Build()
module, expected, err := testutil.GetExpected("document-IndentationAboveAllowed")
expected, err := testutil.GetExpected("document-IndentationAboveAllowed")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -262,7 +308,32 @@ func TestDocumentIndentationOfFour(t *testing.T) {
MarkdownIndent: 4,
}).Build()
module, expected, err := testutil.GetExpected("document-IndentationOfFour")
expected, err := testutil.GetExpected("document-IndentationOfFour")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
assert.Nil(err)
assert.Equal(expected, actual)
}
func TestDocumentOutputValues(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().With(&print.Settings{
OutputValues: true,
}).Build()
expected, err := testutil.GetExpected("document-OutputValues")
assert.Nil(err)
options := &tfconf.Options{
OutputValues: true,
OutputValuesPath: "output_values.json",
}
module, err := testutil.GetModule(options)
assert.Nil(err)
actual, err := Print(module, settings)

View File

@@ -0,0 +1,268 @@
Usage:
Example of 'foo_bar' module in `foo_bar.tf`.
- list item 1
- list item 2
Even inline **formatting** in _here_ is possible.
and some [link](https://domain.com/)
* list item 3
* list item 4
```hcl
module "foo_bar" {
source = "github.com/foo/bar"
id = "1234567890"
name = "baz"
zones = ["us-east-1", "us-west-1"]
tags = {
Name = "baz"
Created-By = "first.last@email.com"
Date-Created = "20180101"
}
}
```
Here is some trailing text after code block,
followed by another line of text.
| Name | Description |
|------|-----------------|
| Foo | Foo description |
| Bar | Bar description |
## Providers
The following providers are used by this module:
- tls
- aws (>= 2.15.0)
- aws.ident (>= 2.15.0)
- null
## Inputs
The following input variables are supported:
### unquoted
Description: n/a
Type: `any`
Default: n/a
### string-3
Description: n/a
Type: `string`
Default: `""`
### string-2
Description: It's string number two.
Type: `string`
Default: n/a
### string-1
Description: It's string number one.
Type: `string`
Default: `"bar"`
### map-3
Description: n/a
Type: `map`
Default: `{}`
### map-2
Description: It's map number two.
Type: `map`
Default: n/a
### map-1
Description: It's map number one.
Type: `map`
Default:
```json
{
"a": 1,
"b": 2,
"c": 3
}
```
### list-3
Description: n/a
Type: `list`
Default: `[]`
### list-2
Description: It's list number two.
Type: `list`
Default: n/a
### list-1
Description: It's list number one.
Type: `list`
Default:
```json
[
"a",
"b",
"c"
]
```
### input_with_underscores
Description: A variable with underscores.
Type: `any`
Default: n/a
### input-with-pipe
Description: It includes v1 \| v2 \| v3
Type: `string`
Default: `"v1"`
### input-with-code-block
Description: This is a complicated one. We need a newline.
And an example in a code block
```
default = [
"machine rack01:neptune"
]
```
Type: `list`
Default:
```json
[
"name rack:location"
]
```
### long_type
Description: This description is itself markdown.
It spans over multiple lines.
Type:
```hcl
object({
name = string,
foo = object({ foo = string, bar = string }),
bar = object({ foo = string, bar = string }),
fizz = list(string),
buzz = list(string)
})
```
Default:
```json
{
"bar": {
"bar": "bar",
"foo": "bar"
},
"buzz": [
"fizz",
"buzz"
],
"fizz": [],
"foo": {
"bar": "foo",
"foo": "foo"
},
"name": "hello"
}
```
### no-escape-default-value
Description: The description contains `something_with_underscore`. Defaults to 'VALUE_WITH_UNDERSCORE'.
Type: `string`
Default: `"VALUE_WITH_UNDERSCORE"`
### with-url
Description: The description contains url. https://www.domain.com/foo/bar_baz.html
Type: `string`
Default: `""`
## Outputs
The following outputs are exported:
### unquoted
Description: It's unquoted output.
Value: map["leon":"cat"]
### output-2
Description: It's output number two.
Value: ["jack" "lola"]
### output-1
Description: It's output number one.
Value: 1
### output-0.12
Description: terraform 0.12 only
Value: ""

View File

@@ -64,10 +64,10 @@ const (
{{ if not .Module.Outputs }}
No output.
{{ else }}
| Name | Description |
|------|-------------|
| Name | Description |{{ if $.Settings.OutputValues }} Value |{{ end }}
|------|-------------|{{ if $.Settings.OutputValues }}-------|{{ end }}
{{- range .Module.Outputs }}
| {{ name .Name }} | {{ tostring .Description | sanitizeTbl }} |
| {{ name .Name }} | {{ tostring .Description | sanitizeTbl }} |{{ if $.Settings.OutputValues }} {{ .Value | sanitizeInterface | sanitizeTbl }} |{{ end }}
{{- end }}
{{ end }}
{{ end -}}

View File

@@ -5,6 +5,7 @@ import (
"github.com/segmentio/terraform-docs/internal/pkg/print"
"github.com/segmentio/terraform-docs/internal/pkg/testutil"
"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
"github.com/stretchr/testify/assert"
)
@@ -12,7 +13,10 @@ func TestTable(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().Build()
module, expected, err := testutil.GetExpected("table")
expected, err := testutil.GetExpected("table")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -27,7 +31,10 @@ func TestTableWithRequired(t *testing.T) {
ShowRequired: true,
}).Build()
module, expected, err := testutil.GetExpected("table-WithRequired")
expected, err := testutil.GetExpected("table-WithRequired")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -42,7 +49,10 @@ func TestTableSortByName(t *testing.T) {
SortByName: true,
}).Build()
module, expected, err := testutil.GetExpected("table-SortByName")
expected, err := testutil.GetExpected("table-SortByName")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -58,7 +68,10 @@ func TestTableSortByRequired(t *testing.T) {
SortByRequired: true,
}).Build()
module, expected, err := testutil.GetExpected("table-SortByRequired")
expected, err := testutil.GetExpected("table-SortByRequired")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -76,7 +89,10 @@ func TestTableNoHeader(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("table-NoHeader")
expected, err := testutil.GetExpected("table-NoHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -94,7 +110,10 @@ func TestTableNoProviders(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("table-NoProviders")
expected, err := testutil.GetExpected("table-NoProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -112,7 +131,10 @@ func TestTableNoInputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("table-NoInputs")
expected, err := testutil.GetExpected("table-NoInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -130,7 +152,10 @@ func TestTableNoOutputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("table-NoOutputs")
expected, err := testutil.GetExpected("table-NoOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -148,7 +173,10 @@ func TestTableOnlyHeader(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("table-OnlyHeader")
expected, err := testutil.GetExpected("table-OnlyHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -166,7 +194,10 @@ func TestTableOnlyProviders(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("table-OnlyProviders")
expected, err := testutil.GetExpected("table-OnlyProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -184,7 +215,10 @@ func TestTableOnlyInputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("table-OnlyInputs")
expected, err := testutil.GetExpected("table-OnlyInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -202,7 +236,10 @@ func TestTableOnlyOutputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("table-OnlyOutputs")
expected, err := testutil.GetExpected("table-OnlyOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -217,7 +254,10 @@ func TestTableEscapeCharacters(t *testing.T) {
EscapeCharacters: true,
}).Build()
module, expected, err := testutil.GetExpected("table-EscapeCharacters")
expected, err := testutil.GetExpected("table-EscapeCharacters")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -232,7 +272,10 @@ func TestTableIndentationBellowAllowed(t *testing.T) {
MarkdownIndent: 0,
}).Build()
module, expected, err := testutil.GetExpected("table-IndentationBellowAllowed")
expected, err := testutil.GetExpected("table-IndentationBellowAllowed")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -247,7 +290,10 @@ func TestTableIndentationAboveAllowed(t *testing.T) {
MarkdownIndent: 10,
}).Build()
module, expected, err := testutil.GetExpected("table-IndentationAboveAllowed")
expected, err := testutil.GetExpected("table-IndentationAboveAllowed")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -262,7 +308,32 @@ func TestTableIndentationOfFour(t *testing.T) {
MarkdownIndent: 4,
}).Build()
module, expected, err := testutil.GetExpected("table-IndentationOfFour")
expected, err := testutil.GetExpected("table-IndentationOfFour")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
assert.Nil(err)
assert.Equal(expected, actual)
}
func TestTableOutputValues(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().With(&print.Settings{
OutputValues: true,
}).Build()
expected, err := testutil.GetExpected("table-OutputValues")
assert.Nil(err)
options := &tfconf.Options{
OutputValues: true,
OutputValuesPath: "output_values.json",
}
module, err := testutil.GetModule(options)
assert.Nil(err)
actual, err := Print(module, settings)

View File

@@ -0,0 +1,76 @@
Usage:
Example of 'foo_bar' module in `foo_bar.tf`.
- list item 1
- list item 2
Even inline **formatting** in _here_ is possible.
and some [link](https://domain.com/)
* list item 3
* list item 4
```hcl
module "foo_bar" {
source = "github.com/foo/bar"
id = "1234567890"
name = "baz"
zones = ["us-east-1", "us-west-1"]
tags = {
Name = "baz"
Created-By = "first.last@email.com"
Date-Created = "20180101"
}
}
```
Here is some trailing text after code block,
followed by another line of text.
| Name | Description |
|------|-----------------|
| Foo | Foo description |
| Bar | Bar description |
## Providers
| Name | Version |
|------|---------|
| tls | n/a |
| aws | >= 2.15.0 |
| aws.ident | >= 2.15.0 |
| null | n/a |
## Inputs
| Name | Description | Type | Default |
|------|-------------|------|---------|
| unquoted | n/a | `any` | n/a |
| string-3 | n/a | `string` | `""` |
| string-2 | It's string number two. | `string` | n/a |
| string-1 | It's string number one. | `string` | `"bar"` |
| map-3 | n/a | `map` | `{}` |
| map-2 | It's map number two. | `map` | n/a |
| map-1 | It's map number one. | `map` | <pre>{<br> "a": 1,<br> "b": 2,<br> "c": 3<br>}</pre> |
| list-3 | n/a | `list` | `[]` |
| list-2 | It's list number two. | `list` | n/a |
| list-1 | It's list number one. | `list` | <pre>[<br> "a",<br> "b",<br> "c"<br>]</pre> |
| input_with_underscores | A variable with underscores. | `any` | n/a |
| input-with-pipe | It includes v1 \| v2 \| v3 | `string` | `"v1"` |
| input-with-code-block | This is a complicated one. We need a newline.<br>And an example in a code block<pre>default = [<br> "machine rack01:neptune"<br>]</pre> | `list` | <pre>[<br> "name rack:location"<br>]</pre> |
| long_type | This description is itself markdown.<br><br>It spans over multiple lines. | <pre>object({<br> name = string,<br> foo = object({ foo = string, bar = string }),<br> bar = object({ foo = string, bar = string }),<br> fizz = list(string),<br> buzz = list(string)<br> })</pre> | <pre>{<br> "bar": {<br> "bar": "bar",<br> "foo": "bar"<br> },<br> "buzz": [<br> "fizz",<br> "buzz"<br> ],<br> "fizz": [],<br> "foo": {<br> "bar": "foo",<br> "foo": "foo"<br> },<br> "name": "hello"<br>}</pre> |
| no-escape-default-value | The description contains `something_with_underscore`. Defaults to 'VALUE_WITH_UNDERSCORE'. | `string` | `"VALUE_WITH_UNDERSCORE"` |
| with-url | The description contains url. https://www.domain.com/foo/bar_baz.html | `string` | `""` |
## 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 | "" |

View File

@@ -52,7 +52,11 @@ const (
{{- printf "\n" -}}
{{- range . }}
{{ printf "output.%s" .Name | colorize "\033[36m" }}
{{ tostring .Description | trimSuffix "\n" | default "n/a" | colorize "\033[90m" }}
{{- if $.Settings.OutputValues -}}
{{- printf " " -}}
({{ sanitizeInterface .Value }})
{{- end }}
{{ tostring .Description | trimSuffix "\n" | default "n/a" | colorize "\033[90m" }}
{{ end }}
{{ end -}}
{{ end -}}

View File

@@ -5,6 +5,7 @@ import (
"github.com/segmentio/terraform-docs/internal/pkg/print"
"github.com/segmentio/terraform-docs/internal/pkg/testutil"
"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
"github.com/stretchr/testify/assert"
)
@@ -12,7 +13,10 @@ func TestPretty(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().WithColor().Build()
module, expected, err := testutil.GetExpected("pretty")
expected, err := testutil.GetExpected("pretty")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -27,7 +31,10 @@ func TestPrettySortByName(t *testing.T) {
SortByName: true,
}).Build()
module, expected, err := testutil.GetExpected("pretty-SortByName")
expected, err := testutil.GetExpected("pretty-SortByName")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -43,7 +50,10 @@ func TestPrettySortByRequired(t *testing.T) {
SortByRequired: true,
}).Build()
module, expected, err := testutil.GetExpected("pretty-SortByRequired")
expected, err := testutil.GetExpected("pretty-SortByRequired")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -61,7 +71,10 @@ func TestPrettyNoHeader(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("pretty-NoHeader")
expected, err := testutil.GetExpected("pretty-NoHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -79,7 +92,10 @@ func TestPrettyNoProviders(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("pretty-NoProviders")
expected, err := testutil.GetExpected("pretty-NoProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -97,7 +113,10 @@ func TestPrettyNoInputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("pretty-NoInputs")
expected, err := testutil.GetExpected("pretty-NoInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -115,7 +134,10 @@ func TestPrettyNoOutputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("pretty-NoOutputs")
expected, err := testutil.GetExpected("pretty-NoOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -133,7 +155,10 @@ func TestPrettyOnlyHeader(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("pretty-OnlyHeader")
expected, err := testutil.GetExpected("pretty-OnlyHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -151,7 +176,10 @@ func TestPrettyOnlyProviders(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("pretty-OnlyProviders")
expected, err := testutil.GetExpected("pretty-OnlyProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -169,7 +197,10 @@ func TestPrettyOnlyInputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("pretty-OnlyInputs")
expected, err := testutil.GetExpected("pretty-OnlyInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -187,7 +218,10 @@ func TestPrettyOnlyOutputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("pretty-OnlyOutputs")
expected, err := testutil.GetExpected("pretty-OnlyOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -202,7 +236,32 @@ func TestPrettyNoColor(t *testing.T) {
ShowColor: false,
}).Build()
module, expected, err := testutil.GetExpected("pretty-NoColor")
expected, err := testutil.GetExpected("pretty-NoColor")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
assert.Nil(err)
assert.Equal(expected, actual)
}
func TestPrettyOutputValues(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().WithColor().With(&print.Settings{
OutputValues: true,
}).Build()
expected, err := testutil.GetExpected("pretty-OutputValues")
assert.Nil(err)
options := &tfconf.Options{
OutputValues: true,
OutputValuesPath: "output_values.json",
}
module, err := testutil.GetModule(options)
assert.Nil(err)
actual, err := Print(module, settings)

View File

@@ -0,0 +1,147 @@
Usage:
Example of 'foo_bar' module in `foo_bar.tf`.
- list item 1
- list item 2
Even inline **formatting** in _here_ is possible.
and some [link](https://domain.com/)
* list item 3
* list item 4
```hcl
module "foo_bar" {
source = "github.com/foo/bar"
id = "1234567890"
name = "baz"
zones = ["us-east-1", "us-west-1"]
tags = {
Name = "baz"
Created-By = "first.last@email.com"
Date-Created = "20180101"
}
}
```
Here is some trailing text after code block,
followed by another line of text.
| Name | Description |
|------|-----------------|
| Foo | Foo description |
| Bar | Bar description |
provider.tls
provider.aws (>= 2.15.0)
provider.aws.ident (>= 2.15.0)
provider.null
input.unquoted (required)
n/a
input.string-3 ("")
n/a
input.string-2 (required)
It's string number two.
input.string-1 ("bar")
It's string number one.
input.map-3 ({})
n/a
input.map-2 (required)
It's map number two.
input.map-1 ({
"a": 1,
"b": 2,
"c": 3
})
It's map number one.
input.list-3 ([])
n/a
input.list-2 (required)
It's list number two.
input.list-1 ([
"a",
"b",
"c"
])
It's list number one.
input.input_with_underscores (required)
A variable with underscores.
input.input-with-pipe ("v1")
It includes v1 | v2 | v3
input.input-with-code-block ([
"name rack:location"
])
This is a complicated one. We need a newline.
And an example in a code block
```
default = [
"machine rack01:neptune"
]
```
input.long_type ({
"bar": {
"bar": "bar",
"foo": "bar"
},
"buzz": [
"fizz",
"buzz"
],
"fizz": [],
"foo": {
"bar": "foo",
"foo": "foo"
},
"name": "hello"
})
This description is itself markdown.
It spans over multiple lines.
input.no-escape-default-value ("VALUE_WITH_UNDERSCORE")
The description contains `something_with_underscore`. Defaults to 'VALUE_WITH_UNDERSCORE'.
input.with-url ("")
The description contains url. https://www.domain.com/foo/bar_baz.html
output.unquoted (map["leon":"cat"])
It's unquoted output.
output.output-2 (["jack" "lola"])
It's output number two.
output.output-1 (1)
It's output number one.
output.output-0.12 ("")
terraform 0.12 only

View File

@@ -14,6 +14,10 @@ type Settings struct {
// scope: Markdown
MarkdownIndent int
// OutputValues ailrghaekrgj
// scope: Global
OutputValues bool
// ShowColor print "colorized" version of result in the terminal (default: true)
// scope: Pretty
ShowColor bool
@@ -53,6 +57,7 @@ func NewSettings() *Settings {
EscapeCharacters: true,
EscapePipe: true,
MarkdownIndent: 2,
OutputValues: false,
ShowColor: true,
ShowHeader: true,
ShowInputs: true,

View File

@@ -0,0 +1,161 @@
header: |-
Usage:
Example of 'foo_bar' module in `foo_bar.tf`.
- list item 1
- list item 2
Even inline **formatting** in _here_ is possible.
and some [link](https://domain.com/)
* list item 3
* list item 4
```hcl
module "foo_bar" {
source = "github.com/foo/bar"
id = "1234567890"
name = "baz"
zones = ["us-east-1", "us-west-1"]
tags = {
Name = "baz"
Created-By = "first.last@email.com"
Date-Created = "20180101"
}
}
```
Here is some trailing text after code block,
followed by another line of text.
| Name | Description |
|------|-----------------|
| Foo | Foo description |
| Bar | Bar description |
inputs:
- name: unquoted
type: any
description: ""
default: null
- name: string-3
type: string
description: ""
default: ""
- name: string-2
type: string
description: It's string number two.
default: null
- name: string-1
type: string
description: It's string number one.
default: bar
- name: map-3
type: map
description: ""
default: {}
- name: map-2
type: map
description: It's map number two.
default: null
- name: map-1
type: map
description: It's map number one.
default:
a: 1
b: 2
c: 3
- name: list-3
type: list
description: ""
default: []
- name: list-2
type: list
description: It's list number two.
default: null
- name: list-1
type: list
description: It's list number one.
default:
- a
- b
- c
- name: input_with_underscores
type: any
description: A variable with underscores.
default: null
- name: input-with-pipe
type: string
description: It includes v1 | v2 | v3
default: v1
- name: input-with-code-block
type: list
description: "This is a complicated one. We need a newline. \nAnd an example in
a code block\n```\ndefault = [\n \"machine rack01:neptune\"\n]\n```\n"
default:
- name rack:location
- name: long_type
type: |-
object({
name = string,
foo = object({ foo = string, bar = string }),
bar = object({ foo = string, bar = string }),
fizz = list(string),
buzz = list(string)
})
description: |
This description is itself markdown.
It spans over multiple lines.
default:
bar:
bar: bar
foo: bar
buzz:
- fizz
- buzz
fizz: []
foo:
bar: foo
foo: foo
name: hello
- name: no-escape-default-value
type: string
description: The description contains `something_with_underscore`. Defaults to 'VALUE_WITH_UNDERSCORE'.
default: VALUE_WITH_UNDERSCORE
- name: with-url
type: string
description: The description contains url. https://www.domain.com/foo/bar_baz.html
default: ""
outputs:
- name: unquoted
description: It's unquoted output.
value:
leon: cat
- name: output-2
description: It's output number two.
value:
- jack
- lola
- name: output-1
description: It's output number one.
value: 1
- name: output-0.12
description: terraform 0.12 only
value: ""
providers:
- name: tls
alias: ""
version: ""
- name: aws
alias: ""
version: '>= 2.15.0'
- name: aws
alias: ident
version: '>= 2.15.0'
- name: "null"
alias: ""
version: ""

View File

@@ -5,6 +5,7 @@ import (
"github.com/segmentio/terraform-docs/internal/pkg/print"
"github.com/segmentio/terraform-docs/internal/pkg/testutil"
"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
"github.com/stretchr/testify/assert"
)
@@ -12,7 +13,10 @@ func TestYaml(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().Build()
module, expected, err := testutil.GetExpected("yaml")
expected, err := testutil.GetExpected("yaml")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -27,7 +31,10 @@ func TestYamlSortByName(t *testing.T) {
SortByName: true,
}).Build()
module, expected, err := testutil.GetExpected("yaml-SortByName")
expected, err := testutil.GetExpected("yaml-SortByName")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -43,7 +50,10 @@ func TestYamlSortByRequired(t *testing.T) {
SortByRequired: true,
}).Build()
module, expected, err := testutil.GetExpected("yaml-SortByRequired")
expected, err := testutil.GetExpected("yaml-SortByRequired")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -61,7 +71,10 @@ func TestYamlNoHeader(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("yaml-NoHeader")
expected, err := testutil.GetExpected("yaml-NoHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -79,7 +92,10 @@ func TestYamlNoProviders(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("yaml-NoProviders")
expected, err := testutil.GetExpected("yaml-NoProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -97,7 +113,10 @@ func TestYamlNoInputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("yaml-NoInputs")
expected, err := testutil.GetExpected("yaml-NoInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -115,7 +134,10 @@ func TestYamlNoOutputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("yaml-NoOutputs")
expected, err := testutil.GetExpected("yaml-NoOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -133,7 +155,10 @@ func TestYamlOnlyHeader(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("yaml-OnlyHeader")
expected, err := testutil.GetExpected("yaml-OnlyHeader")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -151,7 +176,10 @@ func TestYamlOnlyProviders(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("yaml-OnlyProviders")
expected, err := testutil.GetExpected("yaml-OnlyProviders")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -169,7 +197,10 @@ func TestYamlOnlyInputs(t *testing.T) {
ShowOutputs: false,
}).Build()
module, expected, err := testutil.GetExpected("yaml-OnlyInputs")
expected, err := testutil.GetExpected("yaml-OnlyInputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
@@ -187,7 +218,32 @@ func TestYamlOnlyOutputs(t *testing.T) {
ShowOutputs: true,
}).Build()
module, expected, err := testutil.GetExpected("yaml-OnlyOutputs")
expected, err := testutil.GetExpected("yaml-OnlyOutputs")
assert.Nil(err)
module, err := testutil.GetModule(new(tfconf.Options))
assert.Nil(err)
actual, err := Print(module, settings)
assert.Nil(err)
assert.Equal(expected, actual)
}
func TestYamlOutputValues(t *testing.T) {
assert := assert.New(t)
settings := testutil.Settings().WithSections().With(&print.Settings{
OutputValues: true,
}).Build()
expected, err := testutil.GetExpected("yaml-OutputValues")
assert.Nil(err)
options := &tfconf.Options{
OutputValues: true,
OutputValuesPath: "output_values.json",
}
module, err := testutil.GetModule(options)
assert.Nil(err)
actual, err := Print(module, settings)

View File

@@ -9,23 +9,33 @@ import (
"github.com/segmentio/terraform-docs/internal/pkg/tfconf"
)
// GetExpected returns 'example' Module and expected Golden file content
func GetExpected(goldenFile string) (*tfconf.Module, string, error) {
// GetModule returns what is generated by the test params
func GetModule(options *tfconf.Options) (*tfconf.Module, error) {
path, err := getExampleFolder()
if err != nil {
return nil, "", err
return nil, err
}
options := &tfconf.Options{
Path: path,
options.Path = path
if options.OutputValues {
options.OutputValuesPath = filepath.Join(path, options.OutputValuesPath)
}
module, err := tfconf.CreateModule(options)
if err != nil {
return nil, "", err
return nil, err
}
return module, err
}
expected, err := readGoldenFile(goldenFile)
return module, expected, err
// GetExpected returns 'example' Module and expected Golden file content
func GetExpected(name string) (string, error) {
path := filepath.Join(testDataPath(), name+".golden")
bytes, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
return string(bytes), nil
}
func getExampleFolder() (string, error) {
@@ -37,15 +47,6 @@ func getExampleFolder() (string, error) {
return path, nil
}
func readGoldenFile(name string) (string, error) {
path := filepath.Join(testDataPath(), name+".golden")
bytes, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
return string(bytes), nil
}
func testDataPath() string {
return filepath.Join("testdata")
}

View File

@@ -1,8 +1,11 @@
package tfconf
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os/exec"
"sort"
"strings"
@@ -22,8 +25,6 @@ type Module struct {
Providers []*Provider `json:"providers" yaml:"providers"`
RequiredInputs []*Input `json:"-" yaml:"-"`
OptionalInputs []*Input `json:"-" yaml:"-"`
options *Options
}
// HasInputs indicates if the document has inputs.
@@ -68,7 +69,7 @@ func (m *Module) Sort(settings *print.Settings) {
}
// CreateModule returns new instance of Module with all the inputs and
// outputs dircoverd from provided 'path' containing Terraform config
// outputs discovered from provided 'path' containing Terraform config
func CreateModule(options *Options) (*Module, error) {
mod := loadModule(options.Path)
@@ -122,20 +123,34 @@ func CreateModule(options *Options) (*Module, error) {
}
}
// output module
var outputs = make([]*Output, 0, len(mod.Outputs))
for _, output := range mod.Outputs {
outputDescription := output.Description
for _, o := range mod.Outputs {
outputDescription := o.Description
if outputDescription == "" {
outputDescription = readComment(output.Pos.Filename, output.Pos.Line-1)
outputDescription = readComment(o.Pos.Filename, o.Pos.Line-1)
}
outputs = append(outputs, &Output{
Name: output.Name,
output := &Output{
Name: o.Name,
Description: String(outputDescription),
Position: Position{
Filename: output.Pos.Filename,
Line: output.Pos.Line,
Filename: o.Pos.Filename,
Line: o.Pos.Line,
},
})
}
if options.OutputValues {
terraformOutputs, err := loadOutputValues(options)
if err != nil {
log.Fatal(err)
}
if terraformOutputs[output.Name].Sensitive {
output.Value = "<sensitive>"
} else {
output.Value = terraformOutputs[output.Name].Value
}
}
outputs = append(outputs, output)
}
var providerSet = loadProviders(mod.RequiredProviders, mod.ManagedResources, mod.DataResources)
@@ -151,8 +166,6 @@ func CreateModule(options *Options) (*Module, error) {
Providers: providers,
RequiredInputs: requiredInputs,
OptionalInputs: optionalInputs,
options: options,
}
return module, nil
}
@@ -187,3 +200,27 @@ func loadProviders(requiredProviders map[string]*tfconfig.ProviderRequirement, r
}
return providers
}
func loadOutputValues(options *Options) (map[string]*TerraformOutput, error) {
var out []byte
var err error
if options.OutputValuesPath == "" {
cmd := exec.Command("terraform", "output", "-json")
cmd.Dir = options.Path
if out, err = cmd.Output(); err != nil {
return nil, fmt.Errorf("caught error while reading the terraform outputs: %v", err)
}
} else {
if out, err = ioutil.ReadFile(options.OutputValuesPath); err != nil {
return nil, fmt.Errorf("caught error while reading the terraform outputs file at %s: %v", options.OutputValuesPath, err)
}
}
var terraformOutputs map[string]*TerraformOutput
err = json.Unmarshal(out, &terraformOutputs)
if err != nil {
return nil, err
}
return terraformOutputs, err
}

View File

@@ -2,5 +2,7 @@ package tfconf
// Options contains required options to load a Module from path
type Options struct {
Path string
Path string
OutputValues bool
OutputValuesPath string
}

View File

@@ -2,9 +2,10 @@ package tfconf
// Output represents a Terraform output.
type Output struct {
Name string `json:"name" yaml:"name"`
Description String `json:"description" yaml:"description"`
Position Position `json:"-" yaml:"-"`
Name string `json:"name" yaml:"name"`
Description String `json:"description" yaml:"description"`
Value interface{} `json:"value,omitempty" yaml:"value,omitempty"`
Position Position `json:"-" yaml:"-"`
}
type outputsSortedByName []*Output
@@ -34,3 +35,10 @@ func (a outputsSortedByPosition) Swap(i, j int) {
func (a outputsSortedByPosition) Less(i, j int) bool {
return a[i].Position.Filename < a[j].Position.Filename || a[i].Position.Line < a[j].Position.Line
}
// TerraformOutput is used for unmarshalling `terraform outputs --json` into
type TerraformOutput struct {
Sensitive bool `json:"sensitive"`
Type interface{} `json:"type"`
Value interface{} `json:"value"`
}

View File

@@ -160,6 +160,24 @@ func builtinFuncs(settings *print.Settings) map[string]interface{} {
"sanitizeTbl": func(s string) string {
return markdown.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
},
}
}