From 9f5681a79ce0e625a057c615663bb07412104dba Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 10 Feb 2020 13:08:55 +0000 Subject: [PATCH] Fix Markdown rendering --- main.go | 97 +--------------- tfconfig/markdown.go | 105 ++++++++++++++++++ tfconfig/markdown_test.go | 53 +++++++++ tfconfig/test-fixtures/basics/basics.out.md | 18 +++ .../data-resources/data-resources.out.md | 11 ++ tfconfig/test-fixtures/empty/empty.out.md | 3 + .../for-expression/for-expression.out.md | 8 ++ .../invalid-braces/invalid-braces.out.md | 6 + .../legacy-block-labels.out.md | 27 +++++ .../module-calls/module-calls.out.md | 8 ++ .../test-fixtures/overrides/overrides.out.md | 22 ++++ .../provider-configs/provider-configs.out.md | 11 ++ .../provider-source/provider-source.out.md | 7 ++ .../resource-provider-alias.out.md | 16 +++ .../syntax-error/syntax-error.out.md | 11 ++ .../type-conversions/type-conversions.out.md | 23 ++++ .../type-errors/type-errors.out.md | 69 ++++++++++++ .../variable-types/variable-types.out.md | 9 ++ 18 files changed, 408 insertions(+), 96 deletions(-) create mode 100644 tfconfig/markdown.go create mode 100644 tfconfig/markdown_test.go create mode 100644 tfconfig/test-fixtures/basics/basics.out.md create mode 100644 tfconfig/test-fixtures/data-resources/data-resources.out.md create mode 100644 tfconfig/test-fixtures/empty/empty.out.md create mode 100644 tfconfig/test-fixtures/for-expression/for-expression.out.md create mode 100644 tfconfig/test-fixtures/invalid-braces/invalid-braces.out.md create mode 100644 tfconfig/test-fixtures/legacy-block-labels/legacy-block-labels.out.md create mode 100644 tfconfig/test-fixtures/module-calls/module-calls.out.md create mode 100644 tfconfig/test-fixtures/overrides/overrides.out.md create mode 100644 tfconfig/test-fixtures/provider-configs/provider-configs.out.md create mode 100644 tfconfig/test-fixtures/provider-source/provider-source.out.md create mode 100644 tfconfig/test-fixtures/resource-provider-alias/resource-provider-alias.out.md create mode 100644 tfconfig/test-fixtures/syntax-error/syntax-error.out.md create mode 100644 tfconfig/test-fixtures/type-conversions/type-conversions.out.md create mode 100644 tfconfig/test-fixtures/type-errors/type-errors.out.md create mode 100644 tfconfig/test-fixtures/variable-types/variable-types.out.md diff --git a/main.go b/main.go index aa0fb37..1c5ba76 100644 --- a/main.go +++ b/main.go @@ -4,8 +4,6 @@ import ( "encoding/json" "fmt" "os" - "strings" - "text/template" "github.com/hashicorp/terraform-config-inspect/tfconfig" flag "github.com/spf13/pflag" @@ -47,102 +45,9 @@ func showModuleJSON(module *tfconfig.Module) { } func showModuleMarkdown(module *tfconfig.Module) { - tmpl := template.New("md") - tmpl.Funcs(template.FuncMap{ - "tt": func(s string) string { - return "`" + s + "`" - }, - "commas": func(s []string) string { - return strings.Join(s, ", ") - }, - "json": func(v interface{}) (string, error) { - j, err := json.Marshal(v) - return string(j), err - }, - "severity": func(s tfconfig.DiagSeverity) string { - switch s { - case tfconfig.DiagError: - return "Error: " - case tfconfig.DiagWarning: - return "Warning: " - default: - return "" - } - }, - }) - template.Must(tmpl.Parse(markdownTemplate)) - err := tmpl.Execute(os.Stdout, module) + err := tfconfig.RenderMarkdown(os.Stdout, module) if err != nil { fmt.Fprintf(os.Stderr, "error rendering template: %s\n", err) os.Exit(2) } } - -const markdownTemplate = ` -# Module {{ tt .Path }} - -{{- if .RequiredCore}} - -Core Version Constraints: -{{- range .RequiredCore }} -* {{ tt . }} -{{- end}}{{end}} - -{{- if .RequiredProviders}} - -Provider Requirements: -{{- range $name, $versions := .RequiredProviders }} -* **{{ $name }}:** {{ if $versions }}{{ commas $versions | tt }}{{ else }}(any version){{ end }} -{{- end}}{{end}} - -{{- if .Variables}} - -## Input Variables -{{- range .Variables }} -* {{ tt .Name }}{{ if .Default }} (default {{ json .Default | tt }}){{else}} (required){{end}} -{{- if .Description}}: {{ .Description }}{{ end }} -{{- end}}{{end}} - -{{- if .Outputs}} - -## Output Values -{{- range .Outputs }} -* {{ tt .Name }}{{ if .Description}}: {{ .Description }}{{ end }} -{{- end}}{{end}} - -{{- if .ManagedResources}} - -## Managed Resources -{{- range .ManagedResources }} -* {{ printf "%s.%s" .Type .Name | tt }} from {{ tt .Provider.Name }} -{{- end}}{{end}} - -{{- if .DataResources}} - -## Data Resources -{{- range .DataResources }} -* {{ printf "data.%s.%s" .Type .Name | tt }} from {{ tt .Provider.Name }} -{{- end}}{{end}} - -{{- if .ModuleCalls}} - -## Child Modules -{{- range .ModuleCalls }} -* {{ tt .Name }} from {{ tt .Source }}{{ if .Version }} ({{ tt .Version }}){{ end }} -{{- end}}{{end}} - -{{- if .Diagnostics}} - -## Problems -{{- range .Diagnostics }} - -## {{ severity .Severity }}{{ .Summary }}{{ if .Pos }} - -(at {{ tt .Pos.Filename }} line {{ .Pos.Line }}{{ end }}) -{{ if .Detail }} -{{ .Detail }} -{{- end }} - -{{- end}}{{end}} - -` diff --git a/tfconfig/markdown.go b/tfconfig/markdown.go new file mode 100644 index 0000000..9ace608 --- /dev/null +++ b/tfconfig/markdown.go @@ -0,0 +1,105 @@ +package tfconfig + +import ( + "encoding/json" + "io" + "strings" + "text/template" +) + +func RenderMarkdown(w io.Writer, module *Module) error { + tmpl := template.New("md") + tmpl.Funcs(template.FuncMap{ + "tt": func(s string) string { + return "`" + s + "`" + }, + "commas": func(s []string) string { + return strings.Join(s, ", ") + }, + "json": func(v interface{}) (string, error) { + j, err := json.Marshal(v) + return string(j), err + }, + "severity": func(s DiagSeverity) string { + switch s { + case DiagError: + return "Error: " + case DiagWarning: + return "Warning: " + default: + return "" + } + }, + }) + template.Must(tmpl.Parse(markdownTemplate)) + return tmpl.Execute(w, module) +} + +const markdownTemplate = ` +# Module {{ tt .Path }} + +{{- if .RequiredCore}} + +Core Version Constraints: +{{- range .RequiredCore }} +* {{ tt . }} +{{- end}}{{end}} + +{{- if .RequiredProviders}} + +Provider Requirements: +{{- range $name, $req := .RequiredProviders }} +* **{{ $name }}{{ if $req.Source }} ({{ $req.Source | tt }}){{ end }}:** {{ if $req.VersionConstraints }}{{ commas $req.VersionConstraints | tt }}{{ else }}(any version){{ end }} +{{- end}}{{end}} + +{{- if .Variables}} + +## Input Variables +{{- range .Variables }} +* {{ tt .Name }}{{ if .Default }} (default {{ json .Default | tt }}){{else}} (required){{end}} +{{- if .Description}}: {{ .Description }}{{ end }} +{{- end}}{{end}} + +{{- if .Outputs}} + +## Output Values +{{- range .Outputs }} +* {{ tt .Name }}{{ if .Description}}: {{ .Description }}{{ end }} +{{- end}}{{end}} + +{{- if .ManagedResources}} + +## Managed Resources +{{- range .ManagedResources }} +* {{ printf "%s.%s" .Type .Name | tt }} from {{ tt .Provider.Name }} +{{- end}}{{end}} + +{{- if .DataResources}} + +## Data Resources +{{- range .DataResources }} +* {{ printf "data.%s.%s" .Type .Name | tt }} from {{ tt .Provider.Name }} +{{- end}}{{end}} + +{{- if .ModuleCalls}} + +## Child Modules +{{- range .ModuleCalls }} +* {{ tt .Name }} from {{ tt .Source }}{{ if .Version }} ({{ tt .Version }}){{ end }} +{{- end}}{{end}} + +{{- if .Diagnostics}} + +## Problems +{{- range .Diagnostics }} + +## {{ severity .Severity }}{{ .Summary }}{{ if .Pos }} + +(at {{ tt .Pos.Filename }} line {{ .Pos.Line }}{{ end }}) +{{ if .Detail }} +{{ .Detail }} +{{- end }} + +{{- end}}{{end}} + +` diff --git a/tfconfig/markdown_test.go b/tfconfig/markdown_test.go new file mode 100644 index 0000000..c22cdf5 --- /dev/null +++ b/tfconfig/markdown_test.go @@ -0,0 +1,53 @@ +package tfconfig + +import ( + "bytes" + "io/ioutil" + "path/filepath" + "testing" + + "github.com/go-test/deep" +) + +func TestRenderMarkdown(t *testing.T) { + fixturesDir := "test-fixtures" + testDirs, err := ioutil.ReadDir(fixturesDir) + if err != nil { + t.Fatal(err) + } + + for _, info := range testDirs { + if !info.IsDir() { + continue + } + + t.Run(info.Name(), func(t *testing.T) { + name := info.Name() + path := filepath.Join(fixturesDir, name) + + fullPath := filepath.Join(path, name+".out.md") + expected, err := ioutil.ReadFile(fullPath) + if err != nil { + t.Skipf("%q not found, skipping test", fullPath) + } + + module, _ := LoadModule(path) + if module == nil { + t.Fatalf("result object is nil; want a real object") + } + + var b bytes.Buffer + buf := &b + err = RenderMarkdown(buf, module) + if err != nil { + t.Fatal(err) + } + + if diff := deep.Equal(buf.String(), string(expected)); diff != nil { + for _, problem := range diff { + t.Errorf("%s", problem) + } + } + }) + } +} diff --git a/tfconfig/test-fixtures/basics/basics.out.md b/tfconfig/test-fixtures/basics/basics.out.md new file mode 100644 index 0000000..e1b6eef --- /dev/null +++ b/tfconfig/test-fixtures/basics/basics.out.md @@ -0,0 +1,18 @@ + +# Module `test-fixtures/basics` + +Provider Requirements: +* **null:** (any version) + +## Input Variables +* `A` (default `"A default"`) +* `B` (required): The B variable + +## Output Values +* `A` +* `B`: I am B + +## Managed Resources +* `null_resource.A` from `null` +* `null_resource.B` from `null` + diff --git a/tfconfig/test-fixtures/data-resources/data-resources.out.md b/tfconfig/test-fixtures/data-resources/data-resources.out.md new file mode 100644 index 0000000..25e22b5 --- /dev/null +++ b/tfconfig/test-fixtures/data-resources/data-resources.out.md @@ -0,0 +1,11 @@ + +# Module `test-fixtures/data-resources` + +Provider Requirements: +* **external:** (any version) +* **notexternal:** (any version) + +## Data Resources +* `data.external.bar` from `notexternal` +* `data.external.foo` from `external` + diff --git a/tfconfig/test-fixtures/empty/empty.out.md b/tfconfig/test-fixtures/empty/empty.out.md new file mode 100644 index 0000000..07d26df --- /dev/null +++ b/tfconfig/test-fixtures/empty/empty.out.md @@ -0,0 +1,3 @@ + +# Module `test-fixtures/empty` + diff --git a/tfconfig/test-fixtures/for-expression/for-expression.out.md b/tfconfig/test-fixtures/for-expression/for-expression.out.md new file mode 100644 index 0000000..b0347f9 --- /dev/null +++ b/tfconfig/test-fixtures/for-expression/for-expression.out.md @@ -0,0 +1,8 @@ + +# Module `test-fixtures/for-expression` + +## Input Variables +* `enabled` (default `true`) +* `log_categories` (default `["one","two","three"]`) +* `retention_days` (default `7`) + diff --git a/tfconfig/test-fixtures/invalid-braces/invalid-braces.out.md b/tfconfig/test-fixtures/invalid-braces/invalid-braces.out.md new file mode 100644 index 0000000..71778a7 --- /dev/null +++ b/tfconfig/test-fixtures/invalid-braces/invalid-braces.out.md @@ -0,0 +1,6 @@ + +# Module `test-fixtures/invalid-braces` + +## Input Variables +* `foo` (default `"123"`) + diff --git a/tfconfig/test-fixtures/legacy-block-labels/legacy-block-labels.out.md b/tfconfig/test-fixtures/legacy-block-labels/legacy-block-labels.out.md new file mode 100644 index 0000000..bf2117e --- /dev/null +++ b/tfconfig/test-fixtures/legacy-block-labels/legacy-block-labels.out.md @@ -0,0 +1,27 @@ + +# Module `test-fixtures/legacy-block-labels` + +Core Version Constraints: +* `>= 0.11.0` + +Provider Requirements: +* **aws:** `1.0.0` +* **external:** (any version) +* **notnull:** (any version) +* **noversion:** (any version) + +## Input Variables +* `foo` (default `"foo default"`): foo description + +## Output Values +* `foo`: foo description + +## Managed Resources +* `null_resource.foo` from `notnull` + +## Data Resources +* `data.external.foo` from `external` + +## Child Modules +* `foo` from `foo/bar/baz` (`1.2.3`) + diff --git a/tfconfig/test-fixtures/module-calls/module-calls.out.md b/tfconfig/test-fixtures/module-calls/module-calls.out.md new file mode 100644 index 0000000..fcb4593 --- /dev/null +++ b/tfconfig/test-fixtures/module-calls/module-calls.out.md @@ -0,0 +1,8 @@ + +# Module `test-fixtures/module-calls` + +## Child Modules +* `bar` from `./child` +* `baz` from `../elsewhere` +* `foo` from `foo/bar/baz` (`1.0.2`) + diff --git a/tfconfig/test-fixtures/overrides/overrides.out.md b/tfconfig/test-fixtures/overrides/overrides.out.md new file mode 100644 index 0000000..d0dcbfc --- /dev/null +++ b/tfconfig/test-fixtures/overrides/overrides.out.md @@ -0,0 +1,22 @@ + +# Module `test-fixtures/overrides` + +Provider Requirements: +* **null:** (any version) + +## Input Variables +* `A` (required): The A variable OVERRIDDEN +* `B` (required): The B variable +* `C` (required): An entirely new variable C + +## Output Values +* `A`: I am an overridden output! +* `B`: I am B + +## Managed Resources +* `null_resource.A` from `null` +* `null_resource.B` from `null` + +## Child Modules +* `foo` from `foo/bar/baz` (`1.0.2_override`) + diff --git a/tfconfig/test-fixtures/provider-configs/provider-configs.out.md b/tfconfig/test-fixtures/provider-configs/provider-configs.out.md new file mode 100644 index 0000000..514a366 --- /dev/null +++ b/tfconfig/test-fixtures/provider-configs/provider-configs.out.md @@ -0,0 +1,11 @@ + +# Module `test-fixtures/provider-configs` + +Provider Requirements: +* **bar:** `1.0.0, 1.1.0` +* **baz:** `2.0.0` +* **foo:** (any version) + +## Managed Resources +* `bar_bar.bar` from `bar` + diff --git a/tfconfig/test-fixtures/provider-source/provider-source.out.md b/tfconfig/test-fixtures/provider-source/provider-source.out.md new file mode 100644 index 0000000..963e2c2 --- /dev/null +++ b/tfconfig/test-fixtures/provider-source/provider-source.out.md @@ -0,0 +1,7 @@ + +# Module `test-fixtures/provider-source` + +Provider Requirements: +* **bat (`baz/bat`):** `1.0.0` +* **foo:** `2.0.0` + diff --git a/tfconfig/test-fixtures/resource-provider-alias/resource-provider-alias.out.md b/tfconfig/test-fixtures/resource-provider-alias/resource-provider-alias.out.md new file mode 100644 index 0000000..f827a21 --- /dev/null +++ b/tfconfig/test-fixtures/resource-provider-alias/resource-provider-alias.out.md @@ -0,0 +1,16 @@ + +# Module `test-fixtures/resource-provider-alias` + +Provider Requirements: +* **aws:** (any version) +* **notaws:** (any version) + +## Managed Resources +* `aws_instance.bar` from `notaws` +* `aws_instance.baz` from `aws` +* `aws_instance.deprecated_bar` from `notaws` +* `aws_instance.deprecated_baz` from `aws` +* `aws_instance.foo` from `aws` +* `aws_instance.json_bar` from `notaws` +* `aws_instance.json_baz` from `aws` + diff --git a/tfconfig/test-fixtures/syntax-error/syntax-error.out.md b/tfconfig/test-fixtures/syntax-error/syntax-error.out.md new file mode 100644 index 0000000..3d95de2 --- /dev/null +++ b/tfconfig/test-fixtures/syntax-error/syntax-error.out.md @@ -0,0 +1,11 @@ + +# Module `test-fixtures/syntax-error` + +## Problems + +## Error: Argument or block definition required + +(at `test-fixtures/syntax-error/syntax-error.tf` line 1) + +An argument or block definition is required here. + diff --git a/tfconfig/test-fixtures/type-conversions/type-conversions.out.md b/tfconfig/test-fixtures/type-conversions/type-conversions.out.md new file mode 100644 index 0000000..3a28ee9 --- /dev/null +++ b/tfconfig/test-fixtures/type-conversions/type-conversions.out.md @@ -0,0 +1,23 @@ + +# Module `test-fixtures/type-conversions` + +Core Version Constraints: +* `true` + +Provider Requirements: +* **foo:** `true` +* **true:** (any version) +* **yep:** `true` + +## Input Variables +* `foo` (required): true + +## Output Values +* `foo`: true + +## Managed Resources +* `foo.foo` from `true` + +## Child Modules +* `foo` from `true` (`true`) + diff --git a/tfconfig/test-fixtures/type-errors/type-errors.out.md b/tfconfig/test-fixtures/type-errors/type-errors.out.md new file mode 100644 index 0000000..8c9d06a --- /dev/null +++ b/tfconfig/test-fixtures/type-errors/type-errors.out.md @@ -0,0 +1,69 @@ + +# Module `test-fixtures/type-errors` + +Provider Requirements: +* **:** (any version) +* **foo:** (any version) + +## Input Variables +* `foo` (required) + +## Output Values +* `foo` + +## Managed Resources +* `foo.foo` from `` + +## Child Modules +* `foo` from `` + +## Problems + +## Error: Unsuitable value type + +(at `test-fixtures/type-errors/type-errors.tf` line 3) + +Unsuitable value: string required + +## Error: Unsuitable value type + +(at `test-fixtures/type-errors/type-errors.tf` line 7) + +Unsuitable value: string required + +## Error: Unsuitable value type + +(at `test-fixtures/type-errors/type-errors.tf` line 11) + +Unsuitable value: string required + +## Error: Unsuitable value type + +(at `test-fixtures/type-errors/type-errors.tf` line 12) + +Unsuitable value: string required + +## Error: Unsuitable value type + +(at `test-fixtures/type-errors/type-errors.tf` line 16) + +Unsuitable value: string required + +## Error: Invalid provider reference + +(at `test-fixtures/type-errors/type-errors.tf` line 20) + +Provider argument requires a provider name followed by an optional alias, like "aws.foo". + +## Error: Unsuitable value type + +(at `test-fixtures/type-errors/type-errors.tf` line 24) + +Unsuitable value: string required + +## Error: Unsuitable value type + +(at `test-fixtures/type-errors/type-errors.tf` line 26) + +Unsuitable value: string required + diff --git a/tfconfig/test-fixtures/variable-types/variable-types.out.md b/tfconfig/test-fixtures/variable-types/variable-types.out.md new file mode 100644 index 0000000..741fd6a --- /dev/null +++ b/tfconfig/test-fixtures/variable-types/variable-types.out.md @@ -0,0 +1,9 @@ + +# Module `test-fixtures/variable-types` + +## Input Variables +* `list` (required) +* `list_json` (required) +* `map` (required) +* `primitive` (required) +