This repository has been archived by the owner on Mar 24, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
interpolater.go
127 lines (108 loc) · 3.04 KB
/
interpolater.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
package interpolater
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net"
"text/template"
"github.com/Masterminds/sprig"
goCidr "github.com/apparentlymart/go-cidr/cidr"
yamlConverter "github.com/ghodss/yaml"
yaml "gopkg.in/yaml.v2"
)
const (
FormatPreserve = "preserve"
FormatJSON = "json"
FormatYAML = "yaml"
)
type Interpolater struct {
Writer io.Writer
OutputFormat string
}
func (i Interpolater) Execute(basePath string, inputPaths []string) error {
baseContents, err := ioutil.ReadFile(basePath)
if err != nil {
return fmt.Errorf("unable to read template file at '%s': %s", basePath, err)
}
inputVariables, err := i.readInputVars(inputPaths)
if err != nil {
return err
}
customHelpers := map[string]interface{}{
"cidrhost": func(cidr string, hostIndex int) (string, error) {
// adapted from https://github.com/hashicorp/terraform/blob/fe0cc3b0db0d1a5676c3d1a92ea8c5ff829b4233/config/interpolate_funcs.go#L253-L264
_, network, err := net.ParseCIDR(cidr)
if err != nil {
return "", fmt.Errorf("invalid CIDR expression: %s", err)
}
ip, err := goCidr.Host(network, hostIndex)
if err != nil {
return "", err
}
return ip.String(), nil
},
}
t, err := template.New("template").
Funcs(sprig.TxtFuncMap()).
Funcs(customHelpers).
Option("missingkey=error").
Parse(string(baseContents))
if err != nil {
return fmt.Errorf("template '%s' is not valid text/template format: %s", basePath, err)
}
var buffer bytes.Buffer
err = t.Execute(&buffer, inputVariables)
if err != nil {
return fmt.Errorf("failed to render template '%s': %s", basePath, err)
}
return i.writeOutput(buffer.Bytes(), basePath)
}
func (i Interpolater) readInputVars(inputPaths []string) (map[string]interface{}, error) {
inputVariables := map[string]interface{}{}
for _, file := range inputPaths {
fileContents, err := ioutil.ReadFile(file)
if err != nil {
return nil, fmt.Errorf("unable to input file at '%s': %s", file, err)
}
var fileVars map[string]interface{}
err = yamlConverter.Unmarshal(fileContents, &fileVars)
if err != nil {
return nil, fmt.Errorf("expected '%s' to be a valid YAML file: %s", file, err)
}
for k, v := range fileVars {
inputVariables[k] = v
}
}
return inputVariables, nil
}
func (i Interpolater) writeOutput(interpolatedContents []byte, basePath string) error {
var output []byte
var err error
switch format := i.OutputFormat; format {
case FormatPreserve:
output = interpolatedContents
case FormatJSON:
output, err = yamlConverter.YAMLToJSON(interpolatedContents)
if err != nil {
return fmt.Errorf("template '%s' is not valid YAML/JSON: %s", basePath, err)
}
case FormatYAML:
var v interface{}
err = yaml.Unmarshal(interpolatedContents, &v)
if err != nil {
return fmt.Errorf("template '%s' is not valid YAML/JSON: %s", basePath, err)
}
output, err = yaml.Marshal(v)
if err != nil {
return err
}
default:
return fmt.Errorf("unsupported output type '%s'", format)
}
_, err = i.Writer.Write(output)
if err != nil {
return err
}
return nil
}