diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 99f99335ed..3bb3e01288 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ Due to their public nature, GitHub and mailing lists are not appropriate places - Go get a couple of projects nesessary for updating docs and examples: ```shell $ go get github.com/segmentio/terraform-docs - $ go get github.com/s-urbaniak/terraform-examples + $ go get github.com/coreos/tectonic-installer/contrib/terraform-examples ``` ### Contribution Flow @@ -104,4 +104,4 @@ git tools. [modify-installer]: Documentation/contrib/modify-installer.md [tf-doc]: https://www.terraform.io/docs/index.html [coreos-golang]: https://github.com/coreos/docs/tree/master/golang -[disclosure]: https://coreos.com/security/disclosure/ \ No newline at end of file +[disclosure]: https://coreos.com/security/disclosure/ diff --git a/Makefile b/Makefile index 5ef5f79393..a679224d41 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ destroy: terraform-init cd $(BUILD_DIR) && $(TF_CMD) destroy $(TF_DESTROY_OPTIONS) -force $(TOP_DIR)/platforms/$(PLATFORM) define terraform-docs - $(if $(TF_DOCS),,$(error "terraform-docs revision >= a8b59f8 is required (https://github.com/segmentio/terraform-docs)")) + $(if $(TF_DOCS),,$(error terraform-docs revision >= a8b59f8 is required (https://github.com/segmentio/terraform-docs))) @echo '' > $1 @echo '# Terraform variables' >> $1 @@ -62,7 +62,7 @@ define terraform-docs endef define terraform-examples - $(if $(TF_EXAMPLES),,$(error "terraform-examples revision >= 83d7ad6 is required (https://github.com/s-urbaniak/terraform-examples)")) + $(if $(TF_EXAMPLES),,$(error terraform-examples is required. Execute "go get github.com/coreos/tectonic-installer/contrib/terraform-examples" to install it.)) terraform-examples $2 $3 $4 $5 > $1 endef diff --git a/contrib/terraform-examples/Gopkg.toml b/contrib/terraform-examples/Gopkg.toml new file mode 100644 index 0000000000..54d7a43c6d --- /dev/null +++ b/contrib/terraform-examples/Gopkg.toml @@ -0,0 +1,3 @@ +[[constraint]] + branch = "master" + name = "github.com/hashicorp/hcl" diff --git a/contrib/terraform-examples/README.md b/contrib/terraform-examples/README.md new file mode 100644 index 0000000000..0fd9efb9bb --- /dev/null +++ b/contrib/terraform-examples/README.md @@ -0,0 +1,8 @@ +## terraform-examples + +This is a very simple (and most probably incomplete) tool to generate example terraform.tfvars from files or stdin with variable declarations. + +Usage: +``` +$ usage: terraform-example [-|...] +``` diff --git a/contrib/terraform-examples/main.go b/contrib/terraform-examples/main.go new file mode 100644 index 0000000000..69b9e11e26 --- /dev/null +++ b/contrib/terraform-examples/main.go @@ -0,0 +1,213 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "io/ioutil" + "os" + "sort" + "strings" + + "github.com/hashicorp/hcl" + "github.com/hashicorp/hcl/hcl/ast" + "github.com/hashicorp/hcl/hcl/token" +) + +type Variable struct { + Name string + Description string + Default string +} + +type Index struct { + Variables []Variable +} + +type ByName []Variable + +func (s ByName) Len() int { return len(s) } +func (s ByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s ByName) Less(i, j int) bool { return s[i].Name < s[j].Name } + +type stateFunc func(n ast.Node, v *Variable) stateFunc + +func newIndex(c []byte) (*Index, error) { + file, err := hcl.ParseBytes(c) + if err != nil { + return nil, err + } + + v := &Variable{} + idx := &Index{} + state := idx.content + + ast.Walk(file.Node, func(n ast.Node) (ast.Node, bool) { + state = state(n, v) + return n, true + }) + + idx.Variables = append(idx.Variables, *v) + idx.Variables = idx.Variables[1:] + + return idx, nil +} + +func (i *Index) content(n ast.Node, v *Variable) stateFunc { + key, ok := n.(*ast.ObjectKey) + if !ok { + return i.content + } + + if key.Token.Type != token.IDENT { + return i.content + } + + switch key.Token.Text { + case "default": + return i.defaultValue + case "description": + return i.description + case "variable": + i.Variables = append(i.Variables, *v) + *v = Variable{} + return i.variableName + } + + return i.content +} + +func (i *Index) variableName(n ast.Node, v *Variable) stateFunc { + key, ok := n.(*ast.ObjectKey) + if !ok { + return i.variableName + } + + if key.Token.Type != token.STRING { + return i.variableName + } + + v.Name = key.Token.Text + + return i.content +} + +func (i *Index) defaultValue(n ast.Node, v *Variable) stateFunc { + if _, ok := n.(*ast.ListType); ok { + return i.content + } + + if _, ok := n.(*ast.ObjectType); ok { + return i.content + } + + key, ok := n.(*ast.LiteralType) + if !ok { + return i.defaultValue + } + + switch key.Token.Type { + case token.BOOL: + v.Default = key.Token.Text + return i.content + case token.STRING: + v.Default = key.Token.Text + return i.content + } + + return i.defaultValue +} + +func (i *Index) description(n ast.Node, v *Variable) stateFunc { + key, ok := n.(*ast.LiteralType) + if !ok { + return i.description + } + + switch key.Token.Type { + case token.HEREDOC: + v.Description = key.Token.Value().(string) + return i.content + case token.STRING: + v.Description = key.Token.Value().(string) + return i.content + } + + return i.description +} + +func contents(args []string) ([]byte, error) { + if args[0] == "-" { + if len(args) > 1 { + return nil, errors.New("invalid stdin qualifier: multiple files provided") + } + + return ioutil.ReadAll(os.Stdin) + } + + var contents []byte + + for _, path := range flag.Args() { + c, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + contents = append(contents, c...) + contents = append(contents, []byte("\n")...) + } + + return contents, nil +} + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "usage: terraform-example [-|...]\n") + flag.PrintDefaults() + } + flag.Parse() + + if len(flag.Args()) == 0 { + flag.Usage() + os.Exit(1) + } + + var c []byte + c, err := contents(flag.Args()) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + idx, err := newIndex(c) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + sort.Sort(ByName(idx.Variables)) + + for _, v := range idx.Variables { + if strings.HasPrefix(v.Description, "(internal)") { + continue + } + + fmt.Printf("\n") + + v.Name = strings.Trim(v.Name, `"`) + + if strings.HasPrefix(v.Description, "(optional)") { + v.Name = `// ` + v.Name + } + + for _, l := range strings.Split(strings.TrimSpace(v.Description), "\n") { + fmt.Printf("// %s\n", l) + } + + if v.Default == "" { + fmt.Printf("%s = \"\"\n", v.Name) + } else { + fmt.Printf("%s = %s\n", v.Name, v.Default) + } + } +} diff --git a/images/tectonic-builder/Dockerfile b/images/tectonic-builder/Dockerfile index 16062f880b..c221bbe916 100644 --- a/images/tectonic-builder/Dockerfile +++ b/images/tectonic-builder/Dockerfile @@ -24,7 +24,7 @@ RUN go get github.com/jstemmer/go-junit-report ### Tools used by 'make structure-check' RUN go get github.com/segmentio/terraform-docs -RUN go get github.com/s-urbaniak/terraform-examples +RUN go get github.com/coreos/tectonic-installer/contrib/terraform-examples RUN go get github.com/bronze1man/yaml2json ### License parser