mirror of https://github.com/hashicorp/terraform
Merge pull request #7803 from hashicorp/jbardin/tf_vars-push
Add tf_vars to the variables sent in pushpull/7855/head
commit
9dec28bccf
@ -0,0 +1,190 @@
|
||||
package command
|
||||
|
||||
// Marshal an object as an hcl value.
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/hashicorp/hcl/hcl/printer"
|
||||
)
|
||||
|
||||
// This will only work operate on []interface{}, map[string]interface{}, and
|
||||
// primitive types.
|
||||
func encodeHCL(i interface{}) ([]byte, error) {
|
||||
state := &encodeState{}
|
||||
err := state.encode(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hcl := state.Bytes()
|
||||
if len(hcl) == 0 {
|
||||
return hcl, nil
|
||||
}
|
||||
|
||||
// the HCL parser requires an assignment. Strip it off again later
|
||||
fakeAssignment := append([]byte("X = "), hcl...)
|
||||
|
||||
// use the real hcl parser to verify our output, and format it canonically
|
||||
hcl, err = printer.Format(fakeAssignment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// now strip that first assignment off
|
||||
eq := regexp.MustCompile(`=\s+`).FindIndex(hcl)
|
||||
|
||||
return hcl[eq[1]:], nil
|
||||
}
|
||||
|
||||
type encodeState struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (e *encodeState) encode(i interface{}) error {
|
||||
switch v := i.(type) {
|
||||
case []interface{}:
|
||||
return e.encodeList(v)
|
||||
|
||||
case map[string]interface{}:
|
||||
return e.encodeMap(v)
|
||||
|
||||
case int, int8, int32, int64, uint8, uint32, uint64:
|
||||
return e.encodeInt(i)
|
||||
|
||||
case float32, float64:
|
||||
return e.encodeFloat(i)
|
||||
|
||||
case string:
|
||||
return e.encodeString(v)
|
||||
|
||||
case nil:
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid type %T", i)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (e *encodeState) encodeList(l []interface{}) error {
|
||||
e.WriteString("[")
|
||||
for i, v := range l {
|
||||
err := e.encode(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i < len(l)-1 {
|
||||
e.WriteString(", ")
|
||||
}
|
||||
}
|
||||
e.WriteString("]")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *encodeState) encodeMap(m map[string]interface{}) error {
|
||||
e.WriteString("{\n")
|
||||
for i, k := range sortedKeys(m) {
|
||||
v := m[k]
|
||||
|
||||
e.WriteString(k + " = ")
|
||||
err := e.encode(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i < len(m)-1 {
|
||||
e.WriteString("\n")
|
||||
}
|
||||
}
|
||||
e.WriteString("}")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *encodeState) encodeInt(i interface{}) error {
|
||||
_, err := fmt.Fprintf(e, "%d", i)
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *encodeState) encodeFloat(f interface{}) error {
|
||||
_, err := fmt.Fprintf(e, "%f", f)
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *encodeState) encodeString(s string) error {
|
||||
e.Write(quoteHCLString(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Quote an HCL string, which may contain interpolations.
|
||||
// Since the string was already parsed from HCL, we have to assume the
|
||||
// required characters are sanely escaped. All we need to do is escape double
|
||||
// quotes in the string, unless they are in an interpolation block.
|
||||
func quoteHCLString(s string) []byte {
|
||||
out := make([]byte, 0, len(s))
|
||||
out = append(out, '"')
|
||||
|
||||
// our parse states
|
||||
var (
|
||||
outer = 1 // the starting state for the string
|
||||
dollar = 2 // look for '{' in the next character
|
||||
interp = 3 // inside an interpolation block
|
||||
escape = 4 // take the next character and pop back to prev state
|
||||
)
|
||||
|
||||
// we could have nested interpolations
|
||||
state := stack{}
|
||||
state.push(outer)
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch state.peek() {
|
||||
case outer:
|
||||
switch s[i] {
|
||||
case '"':
|
||||
out = append(out, '\\')
|
||||
case '$':
|
||||
state.push(dollar)
|
||||
case '\\':
|
||||
state.push(escape)
|
||||
}
|
||||
case dollar:
|
||||
state.pop()
|
||||
switch s[i] {
|
||||
case '{':
|
||||
state.push(interp)
|
||||
case '\\':
|
||||
state.push(escape)
|
||||
}
|
||||
case interp:
|
||||
switch s[i] {
|
||||
case '}':
|
||||
state.pop()
|
||||
}
|
||||
case escape:
|
||||
state.pop()
|
||||
}
|
||||
|
||||
out = append(out, s[i])
|
||||
}
|
||||
|
||||
out = append(out, '"')
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
type stack []int
|
||||
|
||||
func (s *stack) push(i int) {
|
||||
*s = append(*s, i)
|
||||
}
|
||||
|
||||
func (s *stack) pop() int {
|
||||
last := len(*s) - 1
|
||||
i := (*s)[last]
|
||||
*s = (*s)[:last]
|
||||
return i
|
||||
}
|
||||
|
||||
func (s *stack) peek() int {
|
||||
return (*s)[len(*s)-1]
|
||||
}
|
||||
@ -1,8 +1,23 @@
|
||||
variable "foo" {}
|
||||
|
||||
variable "bar" {}
|
||||
|
||||
variable "baz" {
|
||||
type = "map"
|
||||
|
||||
default = {
|
||||
"A" = "a"
|
||||
interp = "${file("t.txt")}"
|
||||
}
|
||||
}
|
||||
|
||||
variable "fob" {
|
||||
type = "list"
|
||||
default = ["a", "quotes \"in\" quotes"]
|
||||
}
|
||||
|
||||
resource "test_instance" "foo" {}
|
||||
|
||||
atlas {
|
||||
name = "foo"
|
||||
name = "foo"
|
||||
}
|
||||
|
||||
Loading…
Reference in new issue