Skip to content

Commit

Permalink
Move plugin-sdk to in-tree in core repository
Browse files Browse the repository at this point in the history
Moving terraform-docs/plugin-sdk standalone module to in-tree, because
maintaining both of them, specifically if anything needs to be added to
Config, or terraform will required dual effort on both repository. As
such now everything is consolidated under one repository. Example usage
for plugin developer after this move is as follow:

```go
package main

import (
	_ "embed" //nolint

	"github.com/terraform-docs/terraform-docs/plugin"
	"github.com/terraform-docs/terraform-docs/print"
	"github.com/terraform-docs/terraform-docs/template"
	"github.com/terraform-docs/terraform-docs/terraform"
)

func main() {
	plugin.Serve(&plugin.ServeOpts{
		Name:    "template",
		Version: "0.1.0",
		Printer: printer,
	})
}

//go:embed sections.tmpl
var tplCustom []byte

// Print the custom format template. You have all the flexibility to generate
// the output however you choose to.
func printer(config *print.Config, module *terraform.Module) (string, error) {
	tpl := template.New(config,
		&template.Item{Name: "custom", Text: string(tplCustom)},
	)

	rendered, err := tpl.Render("custom", module)
	if err != nil {
		return "", err
	}

	return rendered, nil
}
```

Signed-off-by: Khosrow Moossavi <khos2ow@gmail.com>
  • Loading branch information
khos2ow committed Oct 5, 2021
1 parent 40f20c5 commit 4a9ffe7
Show file tree
Hide file tree
Showing 21 changed files with 450 additions and 168 deletions.
80 changes: 80 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ when formatter is set to `markdown table`.
Note that sections visibility (i.e. `sections.show` and `sections.hide`) takes
precedence over the `content`.

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].

````yaml
content: |-
Any arbitrary text can be placed anywhere in the content
Expand All @@ -278,6 +286,12 @@ content: |-
```hcl
{{ include "examples/foo/main.tf" }}
```
## Resources
{{ range .Module.Resources }}
- {{ .GetMode }}.{{ .Spec }} ({{ .Position.Filename }}#{{ .Position.Line }})
{{- end }}
````

## Build on top of terraform-docs
Expand Down Expand Up @@ -320,6 +334,68 @@ func buildTerraformDocs(path string, tmpl string) (string, error) {
}
```

## Plugin

Generated output can be heavily customized with [`content`], but if using that
is not enough for your use-case, you can write your own plugin.

In order to install a plugin the following steps are needed:

- download the plugin and place it in `~/.tfdocs.d/plugins` (or `./.tfdocs.d/plugins`)
- make sure the plugin file name is `tfdocs-format-<NAME>`
- modify [`formatter`] of `.terraform-docs.yml` file to be `<NAME>`

**Important notes:**

- if the plugin file name is different than the example above, terraform-docs won't
be able to to pick it up nor register it properly
- you can only use plugin thorough `.terraform-docs.yml` file and it cannot be used
with CLI arguments

To create a new plugin create a new repository called `tfdocs-format-<NAME>` with
following `main.go`:

```go
package main

import (
_ "embed" //nolint

"github.com/terraform-docs/terraform-docs/plugin"
"github.com/terraform-docs/terraform-docs/print"
"github.com/terraform-docs/terraform-docs/template"
"github.com/terraform-docs/terraform-docs/terraform"
)

func main() {
plugin.Serve(&plugin.ServeOpts{
Name: "<NAME>",
Version: "0.1.0",
Printer: printerFunc,
})
}

//go:embed sections.tmpl
var tplCustom []byte

// printerFunc the function being executed by the plugin client.
func printerFunc(config *print.Config, module *terraform.Module) (string, error) {
tpl := template.New(config,
&template.Item{Name: "custom", Text: string(tplCustom)},
)

rendered, err := tpl.Render("custom", module)
if err != nil {
return "", err
}

return rendered, nil
}
```

Please refer to [tfdocs-format-template] for more details. You can create a new
repository from it by clicking on `Use this template` button.

## Documentation

- **Users**
Expand All @@ -341,8 +417,10 @@ MIT License - Copyright (c) 2021 The terraform-docs Authors.

[Chocolatey]: https://www.chocolatey.org
[Config File Reference]: https://terraform-docs.io/user-guide/configuration/
[`content`]: https://terraform-docs.io/user-guide/configuration/content/
[Contributing Guide]: CONTRIBUTING.md
[Formats Guide]: https://terraform-docs.io/reference/terraform-docs/
[`formatter`]: https://terraform-docs.io/user-guide/configuration/formatter/
[here]: https://golang.org/doc/code.html#GOPATH
[Homebrew]: https://brew.sh
[install pre-commit]: https://pre-commit.com/#install
Expand All @@ -351,5 +429,7 @@ MIT License - Copyright (c) 2021 The terraform-docs Authors.
[Scoop]: https://scoop.sh/
[Slack]: https://slack.terraform-docs.io/
[terraform-docs GitHub Action]: https://github.com/terraform-docs/gh-actions
[Terraform module]: https://pkg.go.dev/github.com/terraform-docs/terraform-docs/terraform#Module
[tfdocs-format-template]: https://github.com/terraform-docs/tfdocs-format-template
[our website]: https://terraform-docs.io/
[User Guide]: https://terraform-docs.io/user-guide/introduction/
67 changes: 60 additions & 7 deletions docs/developer-guide/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,66 @@ weight: 310
toc: false
---

If you want to add or change formatter, you need to write plugins. When changing
plugins, refer to the repository of each plugin and refer to how to build and
install.
Generated output can be heavily customized with [`content`], but if using that
is not enough for your use-case, you can write your own plugin.

If you want to create a new plugin, please refer to [tfdocs-format-template]. The
plugin can use [plugin-sdk] to communicate with the host process. You can create a
new repository from `Use this template`.
In order to install a plugin the following steps are needed:

[plugin-sdk]: https://github.com/terraform-docs/plugin-sdk
- download the plugin and place it in `~/.tfdocs.d/plugins` (or `./.tfdocs.d/plugins`)
- make sure the plugin file name is `tfdocs-format-<NAME>`
- modify [`formatter`] of `.terraform-docs.yml` file to be `<NAME>`

**Important notes:**

- if the plugin file name is different than the example above, terraform-docs won't
be able to to pick it up nor register it properly
- you can only use plugin thorough `.terraform-docs.yml` file and it cannot be used
with CLI arguments

To create a new plugin create a new repository called `tfdocs-format-<NAME>` with
following `main.go`:

```go
package main

import (
_ "embed" //nolint

"github.com/terraform-docs/terraform-docs/plugin"
"github.com/terraform-docs/terraform-docs/print"
"github.com/terraform-docs/terraform-docs/template"
"github.com/terraform-docs/terraform-docs/terraform"
)

func main() {
plugin.Serve(&plugin.ServeOpts{
Name: "<NAME>",
Version: "0.1.0",
Printer: printerFunc,
})
}

//go:embed sections.tmpl
var tplCustom []byte

// printerFunc the function being executed by the plugin client.
func printerFunc(config *print.Config, module *terraform.Module) (string, error) {
tpl := template.New(config,
&template.Item{Name: "custom", Text: string(tplCustom)},
)

rendered, err := tpl.Render("custom", module)
if err != nil {
return "", err
}

return rendered, nil
}
```

Please refer to [tfdocs-format-template] for more details. You can create a new
repository from it by clicking on `Use this template` button.

[`content`]: {{< ref "content" >}}
[`formatter`]: {{< ref "formatter" >}}
[tfdocs-format-template]: https://github.com/terraform-docs/tfdocs-format-template
3 changes: 2 additions & 1 deletion examples/.terraform-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
# version: ">= 0.10, < 0.12"

# see: https://terraform-docs.io/user-guide/configuration/formatter
formatter: markdown table
# formatter: markdown table
formatter: template

# see: https://terraform-docs.io/user-guide/configuration/header-from
header-from: doc.txt
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.16
require (
github.com/BurntSushi/toml v0.3.1
github.com/Masterminds/sprig/v3 v3.2.2
github.com/hashicorp/go-hclog v0.15.0
github.com/hashicorp/go-plugin v1.4.2
github.com/hashicorp/go-version v1.3.0
github.com/hashicorp/hcl/v2 v2.10.1
Expand All @@ -15,7 +16,6 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
github.com/terraform-docs/plugin-sdk v0.3.1-0.20210825180722-c52e9860f575
github.com/terraform-docs/terraform-config-inspect v0.0.0-20210728164355-9c1f178932fa
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
honnef.co/go/tools v0.2.0
Expand Down
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ github.com/hashicorp/go-hclog v0.15.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-plugin v1.4.0/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
github.com/hashicorp/go-plugin v1.4.2 h1:yFvG3ufXXpqiMiZx9HLcaK3XbIqQ1WJFR/F1a2CuVw0=
github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
Expand Down Expand Up @@ -310,8 +309,6 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/terraform-docs/plugin-sdk v0.3.1-0.20210825180722-c52e9860f575 h1:pwOvvonyymFBE2YE5Yx1M236lH4WT9i8sgVYC4KMsgE=
github.com/terraform-docs/plugin-sdk v0.3.1-0.20210825180722-c52e9860f575/go.mod h1:3G+0nZTeaMF1c5CZh8cOEYeNq0kUL6+DlQOVcxK7eCQ=
github.com/terraform-docs/terraform-config-inspect v0.0.0-20210728164355-9c1f178932fa h1:wdyf3TobwYFwsqnUGJcjdNHxKfwHPFbaOknBJehnF1M=
github.com/terraform-docs/terraform-config-inspect v0.0.0-20210728164355-9c1f178932fa/go.mod h1:GtanFwTsRRXScYHOMb5h4K18XQBFeS2tXat9/LrPtPc=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
Expand Down Expand Up @@ -691,7 +688,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.1.2/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
honnef.co/go/tools v0.2.0 h1:ws8AfbgTX3oIczLPNPCu5166oBg9ST2vNs0rcht+mDE=
honnef.co/go/tools v0.2.0/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
mvdan.cc/xurls/v2 v2.3.0 h1:59Olnbt67UKpxF1EwVBopJvkSUBmgtb468E4GVWIZ1I=
Expand Down
8 changes: 4 additions & 4 deletions internal/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import (
"github.com/spf13/pflag"
"github.com/spf13/viper"

pluginsdk "github.com/terraform-docs/plugin-sdk/plugin"
"github.com/terraform-docs/terraform-docs/format"
"github.com/terraform-docs/terraform-docs/internal/plugin"
"github.com/terraform-docs/terraform-docs/internal/version"
pluginsdk "github.com/terraform-docs/terraform-docs/plugin"
"github.com/terraform-docs/terraform-docs/print"
"github.com/terraform-docs/terraform-docs/terraform"
)
Expand Down Expand Up @@ -344,9 +344,9 @@ func generateContent(config *print.Config) error {
return fmt.Errorf("formatter '%s' not found", config.Formatter)
}

content, cerr := client.Execute(pluginsdk.ExecuteArgs{
Module: module.Convert(),
Settings: nil, // TODO settings.Convert(),
content, cerr := client.Execute(&pluginsdk.ExecuteArgs{
Module: module,
Config: config,
})
if cerr != nil {
return cerr
Expand Down
2 changes: 1 addition & 1 deletion internal/plugin/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
goplugin "github.com/hashicorp/go-plugin"
"github.com/mitchellh/go-homedir"

pluginsdk "github.com/terraform-docs/plugin-sdk/plugin"
pluginsdk "github.com/terraform-docs/terraform-docs/plugin"
)

// Discover plugins and registers them. The lookup priority of plugins is as
Expand Down
2 changes: 1 addition & 1 deletion internal/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ package plugin
import (
goplugin "github.com/hashicorp/go-plugin"

pluginsdk "github.com/terraform-docs/plugin-sdk/plugin"
pluginsdk "github.com/terraform-docs/terraform-docs/plugin"
)

// namePrefix is the mandatory prefix for name of the plugin file. What
Expand Down
3 changes: 1 addition & 2 deletions internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"bytes"
"encoding/json"
"encoding/xml"
"go/types"
"reflect"
"sort"
)
Expand Down Expand Up @@ -94,7 +93,7 @@ func TypeOf(t string, v interface{}) String {
}

// Nil represents a 'nil' value which is marshaled to `null` when empty for JSON and YAML
type Nil types.Nil
type Nil struct{}

// HasDefault return false for Nil, because there's no value set for the variable
func (n Nil) HasDefault() bool {
Expand Down
84 changes: 84 additions & 0 deletions plugin/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
Copyright 2021 The terraform-docs Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package plugin

import (
"net/rpc"
"os"
"os/exec"

"github.com/hashicorp/go-hclog"
goplugin "github.com/hashicorp/go-plugin"

"github.com/terraform-docs/terraform-docs/print"
"github.com/terraform-docs/terraform-docs/terraform"
)

// Client is an RPC Client for the host.
type Client struct {
rpcClient *rpc.Client
broker *goplugin.MuxBroker
}

// ClientOpts is an option for initializing a Client.
type ClientOpts struct {
Cmd *exec.Cmd
}

// ExecuteArgs is the collection of arguments being sent by terraform-docs
// core while executing the plugin command.
type ExecuteArgs struct {
Module *terraform.Module
Config *print.Config
}

// NewClient is a wrapper of plugin.NewClient.
func NewClient(opts *ClientOpts) *goplugin.Client {
return goplugin.NewClient(&goplugin.ClientConfig{
HandshakeConfig: handshakeConfig,
Plugins: map[string]goplugin.Plugin{
"formatter": &formatter{},
},
Cmd: opts.Cmd,
Logger: hclog.New(&hclog.LoggerOptions{
Name: "plugin",
Output: os.Stderr,
Level: hclog.LevelFromString(os.Getenv("TFDOCS_LOG")),
}),
})
}

// Name calls the server-side Name method and returns its version.
func (c *Client) Name() (string, error) {
var resp string
err := c.rpcClient.Call("Plugin.Name", new(interface{}), &resp)
return resp, err
}

// Version calls the server-side Version method and returns its version.
func (c *Client) Version() (string, error) {
var resp string
err := c.rpcClient.Call("Plugin.Version", new(interface{}), &resp)
return resp, err
}

// Execute calls the server-side Execute method and returns generated output.
func (c *Client) Execute(args *ExecuteArgs) (string, error) {
var resp string
err := c.rpcClient.Call("Plugin.Execute", args, &resp)
return resp, err
}

0 comments on commit 4a9ffe7

Please sign in to comment.