-
Notifications
You must be signed in to change notification settings - Fork 3
/
main.go
151 lines (126 loc) · 3.53 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package main
import (
"bufio"
"embed"
"encoding/json"
"errors"
"flag"
"fmt"
"html/template"
"io"
"os"
"strings"
tfjson "github.com/hashicorp/terraform-json"
)
var (
// Embed the entire templates directory in the compiled binary.
//go:embed templates
templates embed.FS
// passed in at build time
version string
)
const (
stateDesc string = "Optional; the state JSON output by 'terraform show -json'. Read from stdin if omitted"
stateFileDesc string = "Optional; the path to a local file containing 'terraform show -json' output"
headingDesc string = "Optional; the heading text for use in the printed output"
outputDesc string = "Optional; the output format. Supported values: md, html"
descriptionDesc string = "Optional; a contextual description preceding the outputs"
versionDesc string = "Print the current version and exit"
defaultHeading string = "Outputs"
defaultDescription string = "Terraform state outputs."
defaultOutput string = "md"
sensitive string = "sensitive; redacted"
)
type data struct {
Heading string
Description string
Outputs map[string]*tfjson.StateOutput
}
func value(output tfjson.StateOutput) string {
if output.Sensitive {
return sensitive
}
return fmt.Sprintf("%v", output.Value)
}
func prettyPrintValue(output tfjson.StateOutput) template.HTML {
if output.Sensitive {
return template.HTML(sensitive)
}
pretty, err := json.MarshalIndent(output.Value, "", " ")
if err != nil {
exit(err)
}
return template.HTML(string(pretty))
}
func dataType(output tfjson.StateOutput) string {
return fmt.Sprintf("%T", output.Value)
}
func main() {
stateJSON := flag.String("state", "", stateDesc)
stateFile := flag.String("state-file", "", stateFileDesc)
heading := flag.String("heading", defaultHeading, headingDesc)
output := flag.String("output", defaultOutput, outputDesc)
description := flag.String("description", defaultDescription, descriptionDesc)
flag.Parse()
args := flag.Args()
if len(args) > 0 && strings.ToLower(strings.TrimSpace(args[0])) == "version" {
fmt.Println(version)
return
}
var stateReader io.Reader = bufio.NewReader(os.Stdin)
if *stateJSON != "" {
stateReader = strings.NewReader(*stateJSON)
}
if *stateJSON != "" && *stateFile != "" {
exit(errors.New("'-state' and '-state-file' are mutually exclusive; specify just one"))
}
if *stateFile != "" {
b, err := os.ReadFile(*stateFile)
if err != nil {
exit(err)
}
stateReader = strings.NewReader(string(b))
}
var state *tfjson.State
if err := json.NewDecoder(stateReader).Decode(&state); err != nil {
exit(err)
}
tmpl, err := getTemplatePath(*output)
if err != nil {
exit(err)
}
t, err := template.New(strings.Split(tmpl, "/")[1]).Funcs(template.FuncMap{
"value": value,
"dataType": dataType,
"prettyPrint": prettyPrintValue,
}).ParseFS(templates, tmpl)
if err != nil {
exit(err)
}
outputs := map[string]*tfjson.StateOutput{}
if state.Values != nil {
outputs = state.Values.Outputs
}
err = t.Execute(os.Stdout, data{
Outputs: outputs,
Heading: *heading,
Description: *description,
})
if err != nil {
exit(err)
}
}
func getTemplatePath(output string) (string, error) {
switch output {
case "html":
return "templates/html.tmpl", nil
case "md":
return "templates/markdown.tmpl", nil
default:
return "", fmt.Errorf("'%s' is not a supported output format. Supported formats: 'md' (default), 'html'", output)
}
}
func exit(err error) {
fmt.Fprintf(os.Stderr, "%s", err.Error())
os.Exit(1)
}