mirror of
https://github.com/terraform-docs/terraform-docs.git
synced 2026-03-27 12:58:35 +07:00
Make terraform.Module available in content
Add one extra special variable the `content`:
- `{{ .Module }}`
As opposed to the other variables, which are generated sections based on
a selected formatter, the `{{ .Module }}` variable is just a `struct`
representing a Terraform module.
It can be used to build highly complex and highly customized content:
```yaml
content: |-
## Resources
{{ range .Module.Resources }}
- {{ .GetMode }}.{{ .Spec }} ({{ .Position.Filename }}#{{ .Position.Line }})
{{- end }}
```
Signed-off-by: Khosrow Moossavi <khos2ow@gmail.com>
This commit is contained in:
@@ -312,11 +312,11 @@ func buildTerraformDocs(path string, tmpl string) (string, error) {
|
|||||||
|
|
||||||
// // Note: if you don't intend to provide additional template for the generated
|
// // Note: if you don't intend to provide additional template for the generated
|
||||||
// // content, or the target format doesn't provide templating (e.g. json, yaml,
|
// // content, or the target format doesn't provide templating (e.g. json, yaml,
|
||||||
// // xml, or toml) you can use `Content()` function instead of `ExecuteTemplate()`.
|
// // xml, or toml) you can use `Content()` function instead of `Render()`.
|
||||||
// // `Content()` returns all the sections combined with predefined order.
|
// // `Content()` returns all the sections combined with predefined order.
|
||||||
// return formatter.Content(), nil
|
// return formatter.Content(), nil
|
||||||
|
|
||||||
return formatter.ExecuteTemplate(tmpl)
|
return formatter.Render(tmpl)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -29,19 +29,27 @@ will be ignored for other formatters.
|
|||||||
- `{{ .Requirements }}`
|
- `{{ .Requirements }}`
|
||||||
- `{{ .Resources }}`
|
- `{{ .Resources }}`
|
||||||
|
|
||||||
and following functions:
|
|
||||||
|
|
||||||
- `{{ include "relative/path/to/file" }}`
|
|
||||||
|
|
||||||
These variables are the generated output of individual sections in the selected
|
These variables are the generated output of individual sections in the selected
|
||||||
formatter. For example `{{ .Inputs }}` is Markdown Table representation of _inputs_
|
formatter. For example `{{ .Inputs }}` is Markdown Table representation of _inputs_
|
||||||
when formatter is set to `markdown table`.
|
when formatter is set to `markdown table`.
|
||||||
|
|
||||||
{{< alert type="info" >}}
|
{{< alert type="info" >}}
|
||||||
Sections visibility (i.e. `sections.show` and `sections.hide`) takes
|
Sections visibility (i.e. `sections.show` and `sections.hide`) takes precedence
|
||||||
precedence over the `content`.
|
over the `content`.
|
||||||
{{< /alert >}}
|
{{< /alert >}}
|
||||||
|
|
||||||
|
`content` also has the following function:
|
||||||
|
|
||||||
|
- `{{ include "relative/path/to/file" }}`
|
||||||
|
|
||||||
|
Additionally there's also one extra special variable avaialble to the `content`:
|
||||||
|
|
||||||
|
- `{{ .Module }}`
|
||||||
|
|
||||||
|
As opposed to the other variables mentioned above, which are generated sections
|
||||||
|
based on a selected formatter, the `{{ .Module }}` variable is just a `struct`
|
||||||
|
representing a [Terraform module].
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
Available options with their default values.
|
Available options with their default values.
|
||||||
@@ -93,7 +101,7 @@ content: |-
|
|||||||
````
|
````
|
||||||
|
|
||||||
In the following example, although `{{ .Providers }}` is defined it won't be
|
In the following example, although `{{ .Providers }}` is defined it won't be
|
||||||
rendered because `providers` is not set to be shown in `sections.show`.
|
rendered because `providers` is not set to be shown in `sections.show`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
sections:
|
sections:
|
||||||
@@ -113,3 +121,16 @@ content: |-
|
|||||||
|
|
||||||
{{ .Outputs }}
|
{{ .Outputs }}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Building highly complex and highly customized content using `{{ .Module }}` struct:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
content: |-
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
{{ range .Module.Resources }}
|
||||||
|
- {{ .GetMode }}.{{ .Spec }} ({{ .Position.Filename }}#{{ .Position.Line }})
|
||||||
|
{{- end }}
|
||||||
|
```
|
||||||
|
|
||||||
|
[Terraform module]: https://pkg.go.dev/github.com/terraform-docs/terraform-docs/terraform#Module
|
||||||
@@ -32,6 +32,11 @@ sections:
|
|||||||
#
|
#
|
||||||
# and even in between sections
|
# and even in between sections
|
||||||
#
|
#
|
||||||
|
# ## Resources
|
||||||
|
# {{ range .Module.Resources }}
|
||||||
|
# - {{ .GetMode }}.{{ .Spec }} ({{ .Position.Filename }}#{{ .Position.Line }})
|
||||||
|
# {{- end }}
|
||||||
|
#
|
||||||
# ## Examples
|
# ## Examples
|
||||||
#
|
#
|
||||||
# ```hcl
|
# ```hcl
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ var asciidocsDocumentFS embed.FS
|
|||||||
|
|
||||||
// asciidocDocument represents AsciiDoc Document format.
|
// asciidocDocument represents AsciiDoc Document format.
|
||||||
type asciidocDocument struct {
|
type asciidocDocument struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
template *template.Template
|
template *template.Template
|
||||||
@@ -61,7 +61,7 @@ func NewAsciidocDocument(config *print.Config) Type {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return &asciidocDocument{
|
return &asciidocDocument{
|
||||||
Generator: print.NewGenerator("json", config.ModuleRoot),
|
generator: newGenerator(config, true),
|
||||||
config: config,
|
config: config,
|
||||||
template: tt,
|
template: tt,
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ func NewAsciidocDocument(config *print.Config) Type {
|
|||||||
|
|
||||||
// Generate a Terraform module as AsciiDoc document.
|
// Generate a Terraform module as AsciiDoc document.
|
||||||
func (d *asciidocDocument) Generate(module *terraform.Module) error {
|
func (d *asciidocDocument) Generate(module *terraform.Module) error {
|
||||||
err := d.Generator.ForEach(func(name string) (string, error) {
|
err := d.generator.forEach(func(name string) (string, error) {
|
||||||
rendered, err := d.template.Render(name, module)
|
rendered, err := d.template.Render(name, module)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -77,6 +77,8 @@ func (d *asciidocDocument) Generate(module *terraform.Module) error {
|
|||||||
return sanitize(rendered), nil
|
return sanitize(rendered), nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
d.generator.funcs(withModule(module))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,10 +153,7 @@ func TestAsciidocDocument(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ var asciidocTableFS embed.FS
|
|||||||
|
|
||||||
// asciidocTable represents AsciiDoc Table format.
|
// asciidocTable represents AsciiDoc Table format.
|
||||||
type asciidocTable struct {
|
type asciidocTable struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
template *template.Template
|
template *template.Template
|
||||||
@@ -52,7 +52,7 @@ func NewAsciidocTable(config *print.Config) Type {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return &asciidocTable{
|
return &asciidocTable{
|
||||||
Generator: print.NewGenerator("json", config.ModuleRoot),
|
generator: newGenerator(config, true),
|
||||||
config: config,
|
config: config,
|
||||||
template: tt,
|
template: tt,
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ func NewAsciidocTable(config *print.Config) Type {
|
|||||||
|
|
||||||
// Generate a Terraform module as AsciiDoc tables.
|
// Generate a Terraform module as AsciiDoc tables.
|
||||||
func (t *asciidocTable) Generate(module *terraform.Module) error {
|
func (t *asciidocTable) Generate(module *terraform.Module) error {
|
||||||
err := t.Generator.ForEach(func(name string) (string, error) {
|
err := t.generator.forEach(func(name string) (string, error) {
|
||||||
rendered, err := t.template.Render(name, module)
|
rendered, err := t.template.Render(name, module)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -68,6 +68,8 @@ func (t *asciidocTable) Generate(module *terraform.Module) error {
|
|||||||
return sanitize(rendered), nil
|
return sanitize(rendered), nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.generator.funcs(withModule(module))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,10 +153,7 @@ func TestAsciidocTable(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,15 +28,15 @@ the root directory of this source tree.
|
|||||||
// return err
|
// return err
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// output, err := formatter.ExecuteTemplate("")
|
// output, err := formatter.Render"")
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// return err
|
// return err
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Note: if you don't intend to provide additional template for the generated
|
// Note: if you don't intend to provide additional template for the generated
|
||||||
// content, or the target format doesn't provide templating (e.g. json, yaml,
|
// content, or the target format doesn't provide templating (e.g. json, yaml,
|
||||||
// xml, or toml) you can use `Content()` function instead of `ExecuteTemplate()`.
|
// xml, or toml) you can use `Content()` function instead of `Render)`. Note
|
||||||
// `Content()` returns all the sections combined with predefined order.
|
// that `Content()` returns all the sections combined with predefined order.
|
||||||
//
|
//
|
||||||
// output := formatter.Content()
|
// output := formatter.Content()
|
||||||
//
|
//
|
||||||
|
|||||||
267
format/generator.go
Normal file
267
format/generator.go
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The terraform-docs Authors.
|
||||||
|
|
||||||
|
Licensed under the MIT license (the "License"); you may not
|
||||||
|
use this file except in compliance with the License.
|
||||||
|
|
||||||
|
You may obtain a copy of the License at the LICENSE file in
|
||||||
|
the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package format
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
gotemplate "text/template"
|
||||||
|
|
||||||
|
"github.com/terraform-docs/terraform-docs/print"
|
||||||
|
"github.com/terraform-docs/terraform-docs/template"
|
||||||
|
"github.com/terraform-docs/terraform-docs/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
// generateFunc configures generator.
|
||||||
|
type generateFunc func(*generator)
|
||||||
|
|
||||||
|
// withContent specifies how the generator should add content.
|
||||||
|
func withContent(content string) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.content = content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withHeader specifies how the generator should add Header.
|
||||||
|
func withHeader(header string) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.header = header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withFooter specifies how the generator should add Footer.
|
||||||
|
func withFooter(footer string) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.footer = footer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withInputs specifies how the generator should add Inputs.
|
||||||
|
func withInputs(inputs string) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.inputs = inputs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withModules specifies how the generator should add Modules.
|
||||||
|
func withModules(modules string) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.modules = modules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withOutputs specifies how the generator should add Outputs.
|
||||||
|
func withOutputs(outputs string) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.outputs = outputs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withProviders specifies how the generator should add Providers.
|
||||||
|
func withProviders(providers string) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.providers = providers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withRequirements specifies how the generator should add Requirements.
|
||||||
|
func withRequirements(requirements string) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.requirements = requirements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withResources specifies how the generator should add Resources.
|
||||||
|
func withResources(resources string) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.resources = resources
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withModule specifies how the generator should add Resources.
|
||||||
|
func withModule(module *terraform.Module) generateFunc {
|
||||||
|
return func(g *generator) {
|
||||||
|
g.module = module
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generator represents all the sections that can be generated for a Terraform
|
||||||
|
// modules (e.g. header, footer, inputs, etc). All these sections are being
|
||||||
|
// generated individually and if no content template was passed they will be
|
||||||
|
// combined together with a predefined order.
|
||||||
|
//
|
||||||
|
// On the other hand these sections can individually be used in content template
|
||||||
|
// to form a custom format (and order).
|
||||||
|
//
|
||||||
|
// Note that the notion of custom content template will be ignored for incompatible
|
||||||
|
// formatters and custom plugins. Compatible formatters are:
|
||||||
|
//
|
||||||
|
// - asciidoc document
|
||||||
|
// - asciidoc table
|
||||||
|
// - markdown document
|
||||||
|
// - markdown table
|
||||||
|
type generator struct {
|
||||||
|
// all the content combined
|
||||||
|
content string
|
||||||
|
|
||||||
|
// individual sections
|
||||||
|
header string
|
||||||
|
footer string
|
||||||
|
inputs string
|
||||||
|
modules string
|
||||||
|
outputs string
|
||||||
|
providers string
|
||||||
|
requirements string
|
||||||
|
resources string
|
||||||
|
|
||||||
|
config *print.Config
|
||||||
|
module *terraform.Module
|
||||||
|
|
||||||
|
path string // module's path
|
||||||
|
fns []generateFunc // generator helper functions
|
||||||
|
|
||||||
|
canRender bool // indicates if the generator can render with custom template
|
||||||
|
}
|
||||||
|
|
||||||
|
// newGenerator returns a generator for specific formatter name and with
|
||||||
|
// provided sets of GeneratorFunc functions to build and add individual
|
||||||
|
// sections.
|
||||||
|
func newGenerator(config *print.Config, canRender bool, fns ...generateFunc) *generator {
|
||||||
|
g := &generator{
|
||||||
|
config: config,
|
||||||
|
|
||||||
|
path: config.ModuleRoot,
|
||||||
|
fns: []generateFunc{},
|
||||||
|
|
||||||
|
canRender: canRender,
|
||||||
|
}
|
||||||
|
|
||||||
|
g.funcs(fns...)
|
||||||
|
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content returns generted all the sections combined based on the underlying format.
|
||||||
|
func (g *generator) Content() string { return g.content }
|
||||||
|
|
||||||
|
// Header returns generted header section based on the underlying format.
|
||||||
|
func (g *generator) Header() string { return g.header }
|
||||||
|
|
||||||
|
// Footer returns generted footer section based on the underlying format.
|
||||||
|
func (g *generator) Footer() string { return g.footer }
|
||||||
|
|
||||||
|
// Inputs returns generted inputs section based on the underlying format.
|
||||||
|
func (g *generator) Inputs() string { return g.inputs }
|
||||||
|
|
||||||
|
// Modules returns generted modules section based on the underlying format.
|
||||||
|
func (g *generator) Modules() string { return g.modules }
|
||||||
|
|
||||||
|
// Outputs returns generted outputs section based on the underlying format.
|
||||||
|
func (g *generator) Outputs() string { return g.outputs }
|
||||||
|
|
||||||
|
// Providers returns generted providers section based on the underlying format.
|
||||||
|
func (g *generator) Providers() string { return g.providers }
|
||||||
|
|
||||||
|
// Requirements returns generted resources section based on the underlying format.
|
||||||
|
func (g *generator) Requirements() string { return g.requirements }
|
||||||
|
|
||||||
|
// Resources returns generted requirements section based on the underlying format.
|
||||||
|
func (g *generator) Resources() string { return g.resources }
|
||||||
|
|
||||||
|
// Module returns generted requirements section based on the underlying format.
|
||||||
|
func (g *generator) Module() *terraform.Module { return g.module }
|
||||||
|
|
||||||
|
// funcs adds GenerateFunc to the list of available functions, for further use
|
||||||
|
// if need be, and then runs them.
|
||||||
|
func (g *generator) funcs(fns ...generateFunc) {
|
||||||
|
for _, fn := range fns {
|
||||||
|
g.fns = append(g.fns, fn)
|
||||||
|
fn(g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path set path of module's root directory.
|
||||||
|
func (g *generator) Path(root string) {
|
||||||
|
g.path = root
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generator) Render(tpl string) (string, error) {
|
||||||
|
if !g.canRender {
|
||||||
|
return g.content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if tpl == "" {
|
||||||
|
return g.content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tt := template.New(g.config, &template.Item{
|
||||||
|
Name: "content",
|
||||||
|
Text: tpl,
|
||||||
|
})
|
||||||
|
tt.CustomFunc(gotemplate.FuncMap{
|
||||||
|
"include": func(s string) string {
|
||||||
|
content, err := os.ReadFile(filepath.Join(g.path, s))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.TrimSuffix(string(content), "\n")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
*generator
|
||||||
|
Config *print.Config
|
||||||
|
Module *terraform.Module
|
||||||
|
}{
|
||||||
|
generator: g,
|
||||||
|
Config: g.config,
|
||||||
|
Module: g.module,
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered, err := tt.RenderContent("content", data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSuffix(rendered, "\n"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generatorCallback renders a Terraform module and creates a GenerateFunc.
|
||||||
|
type generatorCallback func(string) generateFunc
|
||||||
|
|
||||||
|
// forEach section executes generatorCallback to render the content for that
|
||||||
|
// section and create corresponding GeneratorFunc. If there is any error in
|
||||||
|
// executing the template for the section forEach function immediately returns
|
||||||
|
// it and exits.
|
||||||
|
func (g *generator) forEach(render func(string) (string, error)) error {
|
||||||
|
mappings := map[string]generatorCallback{
|
||||||
|
"all": withContent,
|
||||||
|
"header": withHeader,
|
||||||
|
"footer": withFooter,
|
||||||
|
"inputs": withInputs,
|
||||||
|
"modules": withModules,
|
||||||
|
"outputs": withOutputs,
|
||||||
|
"providers": withProviders,
|
||||||
|
"requirements": withRequirements,
|
||||||
|
"resources": withResources,
|
||||||
|
}
|
||||||
|
for name, callback := range mappings {
|
||||||
|
result, err := render(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fn := callback(result)
|
||||||
|
g.fns = append(g.fns, fn)
|
||||||
|
fn(g)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -8,128 +8,80 @@ You may obtain a copy of the License at the LICENSE file in
|
|||||||
the root directory of this source tree.
|
the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package print
|
package format
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/terraform-docs/terraform-docs/print"
|
||||||
|
"github.com/terraform-docs/terraform-docs/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsCompatible(t *testing.T) {
|
|
||||||
tests := map[string]struct {
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
"asciidoc document": {
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
"asciidoc table": {
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
"markdown document": {
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
"markdown table": {
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
"markdown": {
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
"markdown-table": {
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
"md": {
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
"md tbl": {
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
"md-tbl": {
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
"json": {
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
"yaml": {
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
"xml": {
|
|
||||||
expected: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, tt := range tests {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
generator := NewGenerator(name, "")
|
|
||||||
actual := generator.isCompatible()
|
|
||||||
|
|
||||||
assert.Equal(tt.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExecuteTemplate(t *testing.T) {
|
func TestExecuteTemplate(t *testing.T) {
|
||||||
header := "this is the header"
|
header := "this is the header"
|
||||||
footer := "this is the footer"
|
footer := "this is the footer"
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
name string
|
complex bool
|
||||||
content string
|
content string
|
||||||
template string
|
template string
|
||||||
expected string
|
expected string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"Compatible without template": {
|
"Compatible without template": {
|
||||||
name: "markdown table",
|
complex: true,
|
||||||
content: "this is the header\nthis is the footer",
|
content: "this is the header\nthis is the footer",
|
||||||
template: "",
|
template: "",
|
||||||
expected: "this is the header\nthis is the footer",
|
expected: "this is the header\nthis is the footer",
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
"Compatible with template not empty section": {
|
"Compatible with template not empty section": {
|
||||||
name: "markdown table",
|
complex: true,
|
||||||
content: "this is the header\nthis is the footer",
|
content: "this is the header\nthis is the footer",
|
||||||
template: "{{ .Header }}",
|
template: "{{ .Header }}",
|
||||||
expected: "this is the header",
|
expected: "this is the header",
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
"Compatible with template empty section": {
|
"Compatible with template empty section": {
|
||||||
name: "markdown table",
|
complex: true,
|
||||||
content: "this is the header\nthis is the footer",
|
content: "this is the header\nthis is the footer",
|
||||||
template: "{{ .Inputs }}",
|
template: "{{ .Inputs }}",
|
||||||
expected: "",
|
expected: "",
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
"Compatible with template and unknown section": {
|
"Compatible with template and unknown section": {
|
||||||
name: "markdown table",
|
complex: true,
|
||||||
content: "this is the header\nthis is the footer",
|
content: "this is the header\nthis is the footer",
|
||||||
template: "{{ .Unknown }}",
|
template: "{{ .Unknown }}",
|
||||||
expected: "",
|
expected: "",
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"Compatible with template include file": {
|
"Compatible with template include file": {
|
||||||
name: "markdown table",
|
complex: true,
|
||||||
content: "this is the header\nthis is the footer",
|
content: "this is the header\nthis is the footer",
|
||||||
template: "{{ include \"testdata/sample-file.txt\" }}",
|
template: "{{ include \"testdata/generator/sample-file.txt\" }}",
|
||||||
expected: "Sample file to be included.\n",
|
expected: "Sample file to be included.",
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
"Compatible with template include unknown file": {
|
"Compatible with template include unknown file": {
|
||||||
name: "markdown table",
|
complex: true,
|
||||||
content: "this is the header\nthis is the footer",
|
content: "this is the header\nthis is the footer",
|
||||||
template: "{{ include \"file-not-found\" }}",
|
template: "{{ include \"file-not-found\" }}",
|
||||||
expected: "",
|
expected: "",
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"Incompatible without template": {
|
"Incompatible without template": {
|
||||||
name: "yaml",
|
complex: false,
|
||||||
content: "header: \"this is the header\"\nfooter: \"this is the footer\"",
|
content: "header: \"this is the header\"\nfooter: \"this is the footer\"",
|
||||||
template: "",
|
template: "",
|
||||||
expected: "header: \"this is the header\"\nfooter: \"this is the footer\"",
|
expected: "header: \"this is the header\"\nfooter: \"this is the footer\"",
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
"Incompatible with template": {
|
"Incompatible with template": {
|
||||||
name: "yaml",
|
complex: false,
|
||||||
content: "header: \"this is the header\"\nfooter: \"this is the footer\"",
|
content: "header: \"this is the header\"\nfooter: \"this is the footer\"",
|
||||||
template: "{{ .Header }}",
|
template: "{{ .Header }}",
|
||||||
expected: "header: \"this is the header\"\nfooter: \"this is the footer\"",
|
expected: "header: \"this is the header\"\nfooter: \"this is the footer\"",
|
||||||
@@ -140,12 +92,14 @@ func TestExecuteTemplate(t *testing.T) {
|
|||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
generator := NewGenerator(tt.name, "")
|
config := print.DefaultConfig()
|
||||||
|
|
||||||
|
generator := newGenerator(config, tt.complex)
|
||||||
generator.content = tt.content
|
generator.content = tt.content
|
||||||
generator.header = header
|
generator.header = header
|
||||||
generator.footer = footer
|
generator.footer = footer
|
||||||
|
|
||||||
actual, err := generator.ExecuteTemplate(tt.template)
|
actual, err := generator.Render(tt.template)
|
||||||
|
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
@@ -160,60 +114,92 @@ func TestExecuteTemplate(t *testing.T) {
|
|||||||
func TestGeneratorFunc(t *testing.T) {
|
func TestGeneratorFunc(t *testing.T) {
|
||||||
text := "foo"
|
text := "foo"
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
fn func(string) GenerateFunc
|
fn func(string) generateFunc
|
||||||
actual func(*Generator) string
|
actual func(*generator) string
|
||||||
}{
|
}{
|
||||||
"WithContent": {
|
"withContent": {
|
||||||
fn: WithContent,
|
fn: withContent,
|
||||||
actual: func(r *Generator) string { return r.content },
|
actual: func(r *generator) string { return r.content },
|
||||||
},
|
},
|
||||||
"WithHeader": {
|
"withHeader": {
|
||||||
fn: WithHeader,
|
fn: withHeader,
|
||||||
actual: func(r *Generator) string { return r.header },
|
actual: func(r *generator) string { return r.header },
|
||||||
},
|
},
|
||||||
"WithFooter": {
|
"withFooter": {
|
||||||
fn: WithFooter,
|
fn: withFooter,
|
||||||
actual: func(r *Generator) string { return r.footer },
|
actual: func(r *generator) string { return r.footer },
|
||||||
},
|
},
|
||||||
"WithInputs": {
|
"withInputs": {
|
||||||
fn: WithInputs,
|
fn: withInputs,
|
||||||
actual: func(r *Generator) string { return r.inputs },
|
actual: func(r *generator) string { return r.inputs },
|
||||||
},
|
},
|
||||||
"WithModules": {
|
"withModules": {
|
||||||
fn: WithModules,
|
fn: withModules,
|
||||||
actual: func(r *Generator) string { return r.modules },
|
actual: func(r *generator) string { return r.modules },
|
||||||
},
|
},
|
||||||
"WithOutputs": {
|
"withOutputs": {
|
||||||
fn: WithOutputs,
|
fn: withOutputs,
|
||||||
actual: func(r *Generator) string { return r.outputs },
|
actual: func(r *generator) string { return r.outputs },
|
||||||
},
|
},
|
||||||
"WithProviders": {
|
"withProviders": {
|
||||||
fn: WithProviders,
|
fn: withProviders,
|
||||||
actual: func(r *Generator) string { return r.providers },
|
actual: func(r *generator) string { return r.providers },
|
||||||
},
|
},
|
||||||
"WithRequirements": {
|
"withRequirements": {
|
||||||
fn: WithRequirements,
|
fn: withRequirements,
|
||||||
actual: func(r *Generator) string { return r.requirements },
|
actual: func(r *generator) string { return r.requirements },
|
||||||
},
|
},
|
||||||
"WithResources": {
|
"withResources": {
|
||||||
fn: WithResources,
|
fn: withResources,
|
||||||
actual: func(r *Generator) string { return r.resources },
|
actual: func(r *generator) string { return r.resources },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for name, tt := range tests {
|
for name, tt := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
generator := NewGenerator(name, "", tt.fn(text))
|
config := print.DefaultConfig()
|
||||||
|
config.Sections.Footer = true
|
||||||
|
|
||||||
|
generator := newGenerator(config, false, tt.fn(text))
|
||||||
|
|
||||||
assert.Equal(text, tt.actual(generator))
|
assert.Equal(text, tt.actual(generator))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGeneratorFuncModule(t *testing.T) {
|
||||||
|
t.Run("withModule", func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
config := print.DefaultConfig()
|
||||||
|
config.ModuleRoot = filepath.Join("..", "terraform", "testdata", "full-example")
|
||||||
|
|
||||||
|
module, err := terraform.LoadWithOptions(config)
|
||||||
|
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
generator := newGenerator(config, true, withModule(module))
|
||||||
|
|
||||||
|
path := filepath.Join("..", "terraform", "testdata", "expected", "full-example-mainTf-Header.golden")
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
expected := string(data)
|
||||||
|
|
||||||
|
assert.Equal(expected, generator.module.Header)
|
||||||
|
assert.Equal("", generator.module.Footer)
|
||||||
|
assert.Equal(7, len(generator.module.Inputs))
|
||||||
|
assert.Equal(3, len(generator.module.Outputs))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestForEach(t *testing.T) {
|
func TestForEach(t *testing.T) {
|
||||||
generator := NewGenerator("foo", "")
|
config := print.DefaultConfig()
|
||||||
generator.ForEach(func(name string) (string, error) {
|
|
||||||
|
generator := newGenerator(config, false)
|
||||||
|
generator.forEach(func(name string) (string, error) {
|
||||||
return name, nil
|
return name, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
// json represents JSON format.
|
// json represents JSON format.
|
||||||
type json struct {
|
type json struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ type json struct {
|
|||||||
// NewJSON returns new instance of JSON.
|
// NewJSON returns new instance of JSON.
|
||||||
func NewJSON(config *print.Config) Type {
|
func NewJSON(config *print.Config) Type {
|
||||||
return &json{
|
return &json{
|
||||||
Generator: print.NewGenerator("json", config.ModuleRoot),
|
generator: newGenerator(config, false),
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ func (j *json) Generate(module *terraform.Module) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
j.Generator.Funcs(print.WithContent(strings.TrimSuffix(buffer.String(), "\n")))
|
j.generator.funcs(withContent(strings.TrimSuffix(buffer.String(), "\n")))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,10 +105,7 @@ func TestJson(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ var markdownDocumentFS embed.FS
|
|||||||
|
|
||||||
// markdownDocument represents Markdown Document format.
|
// markdownDocument represents Markdown Document format.
|
||||||
type markdownDocument struct {
|
type markdownDocument struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
template *template.Template
|
template *template.Template
|
||||||
@@ -59,7 +59,7 @@ func NewMarkdownDocument(config *print.Config) Type {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return &markdownDocument{
|
return &markdownDocument{
|
||||||
Generator: print.NewGenerator("json", config.ModuleRoot),
|
generator: newGenerator(config, true),
|
||||||
config: config,
|
config: config,
|
||||||
template: tt,
|
template: tt,
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ func NewMarkdownDocument(config *print.Config) Type {
|
|||||||
|
|
||||||
// Generate a Terraform module as Markdown document.
|
// Generate a Terraform module as Markdown document.
|
||||||
func (d *markdownDocument) Generate(module *terraform.Module) error {
|
func (d *markdownDocument) Generate(module *terraform.Module) error {
|
||||||
err := d.Generator.ForEach(func(name string) (string, error) {
|
err := d.generator.forEach(func(name string) (string, error) {
|
||||||
rendered, err := d.template.Render(name, module)
|
rendered, err := d.template.Render(name, module)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -75,6 +75,8 @@ func (d *markdownDocument) Generate(module *terraform.Module) error {
|
|||||||
return sanitize(rendered), nil
|
return sanitize(rendered), nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
d.generator.funcs(withModule(module))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,10 +190,7 @@ func TestMarkdownDocument(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ var markdownTableFS embed.FS
|
|||||||
|
|
||||||
// markdownTable represents Markdown Table format.
|
// markdownTable represents Markdown Table format.
|
||||||
type markdownTable struct {
|
type markdownTable struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
template *template.Template
|
template *template.Template
|
||||||
@@ -50,7 +50,7 @@ func NewMarkdownTable(config *print.Config) Type {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return &markdownTable{
|
return &markdownTable{
|
||||||
Generator: print.NewGenerator("markdown table", config.ModuleRoot),
|
generator: newGenerator(config, true),
|
||||||
config: config,
|
config: config,
|
||||||
template: tt,
|
template: tt,
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ func NewMarkdownTable(config *print.Config) Type {
|
|||||||
|
|
||||||
// Generate a Terraform module as Markdown tables.
|
// Generate a Terraform module as Markdown tables.
|
||||||
func (t *markdownTable) Generate(module *terraform.Module) error {
|
func (t *markdownTable) Generate(module *terraform.Module) error {
|
||||||
err := t.Generator.ForEach(func(name string) (string, error) {
|
err := t.generator.forEach(func(name string) (string, error) {
|
||||||
rendered, err := t.template.Render(name, module)
|
rendered, err := t.template.Render(name, module)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -66,6 +66,8 @@ func (t *markdownTable) Generate(module *terraform.Module) error {
|
|||||||
return sanitize(rendered), nil
|
return sanitize(rendered), nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.generator.funcs(withModule(module))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,10 +190,7 @@ func TestMarkdownTable(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ var prettyTpl []byte
|
|||||||
|
|
||||||
// pretty represents colorized pretty format.
|
// pretty represents colorized pretty format.
|
||||||
type pretty struct {
|
type pretty struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
template *template.Template
|
template *template.Template
|
||||||
@@ -50,7 +50,7 @@ func NewPretty(config *print.Config) Type {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return &pretty{
|
return &pretty{
|
||||||
Generator: print.NewGenerator("pretty", config.ModuleRoot),
|
generator: newGenerator(config, true),
|
||||||
config: config,
|
config: config,
|
||||||
template: tt,
|
template: tt,
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,8 @@ func (p *pretty) Generate(module *terraform.Module) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Generator.Funcs(print.WithContent(regexp.MustCompile(`(\r?\n)*$`).ReplaceAllString(rendered, "")))
|
p.generator.funcs(withContent(regexp.MustCompile(`(\r?\n)*$`).ReplaceAllString(rendered, "")))
|
||||||
|
p.generator.funcs(withModule(module))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,10 +105,7 @@ func TestPretty(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ var tfvarsHCLTpl []byte
|
|||||||
|
|
||||||
// tfvarsHCL represents Terraform tfvars HCL format.
|
// tfvarsHCL represents Terraform tfvars HCL format.
|
||||||
type tfvarsHCL struct {
|
type tfvarsHCL struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
template *template.Template
|
template *template.Template
|
||||||
@@ -60,7 +60,7 @@ func NewTfvarsHCL(config *print.Config) Type {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return &tfvarsHCL{
|
return &tfvarsHCL{
|
||||||
Generator: print.NewGenerator("tfvars hcl", config.ModuleRoot),
|
generator: newGenerator(config, false),
|
||||||
config: config,
|
config: config,
|
||||||
template: tt,
|
template: tt,
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ func (h *tfvarsHCL) Generate(module *terraform.Module) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Generator.Funcs(print.WithContent(strings.TrimSuffix(sanitize(rendered), "\n")))
|
h.generator.funcs(withContent(strings.TrimSuffix(sanitize(rendered), "\n")))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,10 +97,7 @@ func TestTfvarsHcl(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
|
|
||||||
// tfvarsJSON represents Terraform tfvars JSON format.
|
// tfvarsJSON represents Terraform tfvars JSON format.
|
||||||
type tfvarsJSON struct {
|
type tfvarsJSON struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ type tfvarsJSON struct {
|
|||||||
// NewTfvarsJSON returns new instance of TfvarsJSON.
|
// NewTfvarsJSON returns new instance of TfvarsJSON.
|
||||||
func NewTfvarsJSON(config *print.Config) Type {
|
func NewTfvarsJSON(config *print.Config) Type {
|
||||||
return &tfvarsJSON{
|
return &tfvarsJSON{
|
||||||
Generator: print.NewGenerator("tfvars json", config.ModuleRoot),
|
generator: newGenerator(config, false),
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,10 +53,9 @@ func (j *tfvarsJSON) Generate(module *terraform.Module) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
j.Generator.Funcs(print.WithContent(strings.TrimSuffix(buffer.String(), "\n")))
|
j.generator.funcs(withContent(strings.TrimSuffix(buffer.String(), "\n")))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -88,10 +88,7 @@ func TestTfvarsJson(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
// toml represents TOML format.
|
// toml represents TOML format.
|
||||||
type toml struct {
|
type toml struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ type toml struct {
|
|||||||
// NewTOML returns new instance of TOML.
|
// NewTOML returns new instance of TOML.
|
||||||
func NewTOML(config *print.Config) Type {
|
func NewTOML(config *print.Config) Type {
|
||||||
return &toml{
|
return &toml{
|
||||||
Generator: print.NewGenerator("toml", config.ModuleRoot),
|
generator: newGenerator(config, false),
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ func (t *toml) Generate(module *terraform.Module) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Generator.Funcs(print.WithContent(strings.TrimSuffix(buffer.String(), "\n")))
|
t.generator.funcs(withContent(strings.TrimSuffix(buffer.String(), "\n")))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
|||||||
@@ -98,10 +98,7 @@ func TestToml(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ type Type interface {
|
|||||||
Requirements() string // requirements section based on the underlying format
|
Requirements() string // requirements section based on the underlying format
|
||||||
Resources() string // resources section based on the underlying format
|
Resources() string // resources section based on the underlying format
|
||||||
|
|
||||||
ExecuteTemplate(contentTmpl string) (string, error)
|
Render(tmpl string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializerFn returns a concrete implementation of an Engine.
|
// initializerFn returns a concrete implementation of an Engine.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
|
|
||||||
// xml represents XML format.
|
// xml represents XML format.
|
||||||
type xml struct {
|
type xml struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ type xml struct {
|
|||||||
// NewXML returns new instance of XML.
|
// NewXML returns new instance of XML.
|
||||||
func NewXML(config *print.Config) Type {
|
func NewXML(config *print.Config) Type {
|
||||||
return &xml{
|
return &xml{
|
||||||
Generator: print.NewGenerator("xml", config.ModuleRoot),
|
generator: newGenerator(config, false),
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ func (x *xml) Generate(module *terraform.Module) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
x.Generator.Funcs(print.WithContent(strings.TrimSuffix(string(out), "\n")))
|
x.generator.funcs(withContent(strings.TrimSuffix(string(out), "\n")))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,10 +98,7 @@ func TestXml(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
// yaml represents YAML format.
|
// yaml represents YAML format.
|
||||||
type yaml struct {
|
type yaml struct {
|
||||||
*print.Generator
|
*generator
|
||||||
|
|
||||||
config *print.Config
|
config *print.Config
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ type yaml struct {
|
|||||||
// NewYAML returns new instance of YAML.
|
// NewYAML returns new instance of YAML.
|
||||||
func NewYAML(config *print.Config) Type {
|
func NewYAML(config *print.Config) Type {
|
||||||
return &yaml{
|
return &yaml{
|
||||||
Generator: print.NewGenerator("yaml", config.ModuleRoot),
|
generator: newGenerator(config, false),
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ func (y *yaml) Generate(module *terraform.Module) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
y.Generator.Funcs(print.WithContent(strings.TrimSuffix(buffer.String(), "\n")))
|
y.generator.funcs(withContent(strings.TrimSuffix(buffer.String(), "\n")))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,10 +98,7 @@ func TestYaml(t *testing.T) {
|
|||||||
err = formatter.Generate(module)
|
err = formatter.Generate(module)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
actual, err := formatter.ExecuteTemplate("")
|
assert.Equal(expected, formatter.Content())
|
||||||
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ func generateContent(config *print.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := formatter.ExecuteTemplate(config.Content)
|
content, err := formatter.Render(config.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,8 @@ the root directory of this source tree.
|
|||||||
// Generator holds a reference to all the sections (e.g. header, footer, inputs, etc)
|
// Generator holds a reference to all the sections (e.g. header, footer, inputs, etc)
|
||||||
// and also it renders all of them, in a predefined order, in `Content()`.
|
// and also it renders all of them, in a predefined order, in `Content()`.
|
||||||
//
|
//
|
||||||
// It also provides `ExecuteTemplate(string)` function to process and render the
|
// It also provides `Render(string)` function to process and render the template to generate
|
||||||
// template to generate the final output content. Following variables and functions are
|
// the final output content. Following variables and functions are available:
|
||||||
// available:
|
|
||||||
//
|
//
|
||||||
// • `{{ .Header }}`
|
// • `{{ .Header }}`
|
||||||
// • `{{ .Footer }}`
|
// • `{{ .Footer }}`
|
||||||
|
|||||||
@@ -1,248 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2021 The terraform-docs Authors.
|
|
||||||
|
|
||||||
Licensed under the MIT license (the "License"); you may not
|
|
||||||
use this file except in compliance with the License.
|
|
||||||
|
|
||||||
You may obtain a copy of the License at the LICENSE file in
|
|
||||||
the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package print
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GenerateFunc configures Generator.
|
|
||||||
type GenerateFunc func(*Generator)
|
|
||||||
|
|
||||||
// WithContent specifies how the Generator should add content.
|
|
||||||
func WithContent(content string) GenerateFunc {
|
|
||||||
return func(g *Generator) {
|
|
||||||
g.content = content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithHeader specifies how the Generator should add Header.
|
|
||||||
func WithHeader(header string) GenerateFunc {
|
|
||||||
return func(g *Generator) {
|
|
||||||
g.header = header
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithFooter specifies how the Generator should add Footer.
|
|
||||||
func WithFooter(footer string) GenerateFunc {
|
|
||||||
return func(g *Generator) {
|
|
||||||
g.footer = footer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithInputs specifies how the Generator should add Inputs.
|
|
||||||
func WithInputs(inputs string) GenerateFunc {
|
|
||||||
return func(g *Generator) {
|
|
||||||
g.inputs = inputs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithModules specifies how the Generator should add Modules.
|
|
||||||
func WithModules(modules string) GenerateFunc {
|
|
||||||
return func(g *Generator) {
|
|
||||||
g.modules = modules
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithOutputs specifies how the Generator should add Outputs.
|
|
||||||
func WithOutputs(outputs string) GenerateFunc {
|
|
||||||
return func(g *Generator) {
|
|
||||||
g.outputs = outputs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithProviders specifies how the Generator should add Providers.
|
|
||||||
func WithProviders(providers string) GenerateFunc {
|
|
||||||
return func(g *Generator) {
|
|
||||||
g.providers = providers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithRequirements specifies how the Generator should add Requirements.
|
|
||||||
func WithRequirements(requirements string) GenerateFunc {
|
|
||||||
return func(g *Generator) {
|
|
||||||
g.requirements = requirements
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithResources specifies how the Generator should add Resources.
|
|
||||||
func WithResources(resources string) GenerateFunc {
|
|
||||||
return func(g *Generator) {
|
|
||||||
g.resources = resources
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generator represents all the sections that can be generated for a Terraform
|
|
||||||
// modules (e.g. header, footer, inputs, etc). All these sections are being
|
|
||||||
// generated individually and if no content template was passed they will be
|
|
||||||
// combined together with a predefined order.
|
|
||||||
//
|
|
||||||
// On the other hand these sections can individually be used in content template
|
|
||||||
// to form a custom format (and order).
|
|
||||||
//
|
|
||||||
// Note that the notion of custom content template will be ignored for incompatible
|
|
||||||
// formatters and custom plugins. Compatible formatters are:
|
|
||||||
//
|
|
||||||
// - asciidoc document
|
|
||||||
// - asciidoc table
|
|
||||||
// - markdown document
|
|
||||||
// - markdown table
|
|
||||||
type Generator struct {
|
|
||||||
// all the content combined
|
|
||||||
content string
|
|
||||||
|
|
||||||
// individual sections
|
|
||||||
header string
|
|
||||||
footer string
|
|
||||||
inputs string
|
|
||||||
modules string
|
|
||||||
outputs string
|
|
||||||
providers string
|
|
||||||
requirements string
|
|
||||||
resources string
|
|
||||||
|
|
||||||
path string // module's path
|
|
||||||
formatter string // formatter name
|
|
||||||
|
|
||||||
funcs []GenerateFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGenerator returns a Generator for specific formatter name and with
|
|
||||||
// provided sets of GeneratorFunc functions to build and add individual
|
|
||||||
// sections.
|
|
||||||
func NewGenerator(name string, root string, fns ...GenerateFunc) *Generator {
|
|
||||||
g := &Generator{
|
|
||||||
path: root,
|
|
||||||
formatter: name,
|
|
||||||
funcs: []GenerateFunc{},
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Funcs(fns...)
|
|
||||||
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
|
|
||||||
// Content returns generted all the sections combined based on the underlying format.
|
|
||||||
func (g *Generator) Content() string { return g.content }
|
|
||||||
|
|
||||||
// Header returns generted header section based on the underlying format.
|
|
||||||
func (g *Generator) Header() string { return g.header }
|
|
||||||
|
|
||||||
// Footer returns generted footer section based on the underlying format.
|
|
||||||
func (g *Generator) Footer() string { return g.footer }
|
|
||||||
|
|
||||||
// Inputs returns generted inputs section based on the underlying format.
|
|
||||||
func (g *Generator) Inputs() string { return g.inputs }
|
|
||||||
|
|
||||||
// Modules returns generted modules section based on the underlying format.
|
|
||||||
func (g *Generator) Modules() string { return g.modules }
|
|
||||||
|
|
||||||
// Outputs returns generted outputs section based on the underlying format.
|
|
||||||
func (g *Generator) Outputs() string { return g.outputs }
|
|
||||||
|
|
||||||
// Providers returns generted providers section based on the underlying format.
|
|
||||||
func (g *Generator) Providers() string { return g.providers }
|
|
||||||
|
|
||||||
// Requirements returns generted resources section based on the underlying format.
|
|
||||||
func (g *Generator) Requirements() string { return g.requirements }
|
|
||||||
|
|
||||||
// Resources returns generted requirements section based on the underlying format.
|
|
||||||
func (g *Generator) Resources() string { return g.resources }
|
|
||||||
|
|
||||||
// Funcs adds GenerateFunc to the list of available functions, for further use
|
|
||||||
// if need be, and then runs them.
|
|
||||||
func (g *Generator) Funcs(fns ...GenerateFunc) {
|
|
||||||
for _, fn := range fns {
|
|
||||||
g.funcs = append(g.funcs, fn)
|
|
||||||
fn(g)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path set path of module's root directory.
|
|
||||||
func (g *Generator) Path(root string) {
|
|
||||||
g.path = root
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteTemplate applies the template with Renderer known items. If template
|
|
||||||
// is empty Renderer.content is returned as is. If template is not empty this
|
|
||||||
// still returns Renderer.content for incompatible formatters.
|
|
||||||
// func (g *Renderer) Render(contentTmpl string) (string, error) {
|
|
||||||
func (g *Generator) ExecuteTemplate(contentTmpl string) (string, error) {
|
|
||||||
if !g.isCompatible() {
|
|
||||||
return g.content, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if contentTmpl == "" {
|
|
||||||
return g.content, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
tmpl := template.New("content")
|
|
||||||
tmpl.Funcs(template.FuncMap{
|
|
||||||
"include": func(s string) string {
|
|
||||||
content, err := os.ReadFile(filepath.Join(g.path, s))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return string(content)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
template.Must(tmpl.Parse(contentTmpl))
|
|
||||||
|
|
||||||
if err := tmpl.ExecuteTemplate(&buf, "content", g); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Generator) isCompatible() bool {
|
|
||||||
switch g.formatter {
|
|
||||||
case "asciidoc document", "asciidoc table", "markdown document", "markdown table":
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// generatorCallback renders a Terraform module and creates a GenerateFunc.
|
|
||||||
type generatorCallback func(string) GenerateFunc
|
|
||||||
|
|
||||||
// ForEach section executes generatorCallback to render the content for that
|
|
||||||
// section and create corresponding GeneratorFunc. If there is any error in
|
|
||||||
// the executing the template for the section ForEach function immediately
|
|
||||||
// returns it and exit.
|
|
||||||
func (g *Generator) ForEach(render func(string) (string, error)) error {
|
|
||||||
mappings := map[string]generatorCallback{
|
|
||||||
"all": WithContent,
|
|
||||||
"header": WithHeader,
|
|
||||||
"footer": WithFooter,
|
|
||||||
"inputs": WithInputs,
|
|
||||||
"modules": WithModules,
|
|
||||||
"outputs": WithOutputs,
|
|
||||||
"providers": WithProviders,
|
|
||||||
"requirements": WithRequirements,
|
|
||||||
"resources": WithResources,
|
|
||||||
}
|
|
||||||
for name, callback := range mappings {
|
|
||||||
result, err := render(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fn := callback(result)
|
|
||||||
g.funcs = append(g.funcs, fn)
|
|
||||||
fn(g)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -74,6 +74,19 @@ func (t *Template) applyCustomFunc() {
|
|||||||
|
|
||||||
// Render template with given Module struct.
|
// Render template with given Module struct.
|
||||||
func (t *Template) Render(name string, module *terraform.Module) (string, error) {
|
func (t *Template) Render(name string, module *terraform.Module) (string, error) {
|
||||||
|
data := struct {
|
||||||
|
Config *print.Config
|
||||||
|
Module *terraform.Module
|
||||||
|
}{
|
||||||
|
Config: t.config,
|
||||||
|
Module: module,
|
||||||
|
}
|
||||||
|
return t.RenderContent(name, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderContent template with given data. It can contain anything but most
|
||||||
|
// probably it will only contain terraform.Module and print.generator.
|
||||||
|
func (t *Template) RenderContent(name string, data interface{}) (string, error) {
|
||||||
if len(t.items) < 1 {
|
if len(t.items) < 1 {
|
||||||
return "", fmt.Errorf("base template not found")
|
return "", fmt.Errorf("base template not found")
|
||||||
}
|
}
|
||||||
@@ -95,13 +108,7 @@ func (t *Template) Render(name string, module *terraform.Module) (string, error)
|
|||||||
gotemplate.Must(tt.Parse(normalize(ii.Text)))
|
gotemplate.Must(tt.Parse(normalize(ii.Text)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tmpl.ExecuteTemplate(&buffer, item.Name, struct {
|
if err := tmpl.ExecuteTemplate(&buffer, item.Name, data); err != nil {
|
||||||
Module *terraform.Module
|
|
||||||
Config *print.Config
|
|
||||||
}{
|
|
||||||
Module: module,
|
|
||||||
Config: t.config,
|
|
||||||
}); err != nil {
|
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user