forked from kubernetes/kops
/
target.go
168 lines (133 loc) · 3.99 KB
/
target.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package terraform
import (
"encoding/json"
"fmt"
"github.com/golang/glog"
hcl_parser "github.com/hashicorp/hcl/json/parser"
"io/ioutil"
"k8s.io/kops/upup/pkg/fi"
"os"
"path"
"strings"
"sync"
)
type TerraformTarget struct {
Cloud fi.Cloud
Region string
Project string
outDir string
// mutex protects the following items (resources & files)
mutex sync.Mutex
// resources is a list of TF items that should be created
resources []*terraformResource
// files is a map of TF resource files that should be created
files map[string][]byte
}
func NewTerraformTarget(cloud fi.Cloud, region, project string, outDir string) *TerraformTarget {
return &TerraformTarget{
Cloud: cloud,
Region: region,
Project: project,
outDir: outDir,
files: make(map[string][]byte),
}
}
var _ fi.Target = &TerraformTarget{}
type terraformResource struct {
ResourceType string
ResourceName string
Item interface{}
}
// A TF name can't have dots in it (if we want to refer to it from a literal),
// so we replace them
func tfSanitize(name string) string {
name = strings.Replace(name, ".", "-", -1)
name = strings.Replace(name, "/", "--", -1)
return name
}
func (t *TerraformTarget) AddFile(resourceType string, resourceName string, key string, r fi.Resource) (*Literal, error) {
id := resourceType + "_" + resourceName + "_" + key
d, err := fi.ResourceAsBytes(r)
if err != nil {
return nil, fmt.Errorf("error rending resource %s %v", id, err)
}
t.mutex.Lock()
defer t.mutex.Unlock()
p := path.Join("data", id)
t.files[p] = d
l := LiteralExpression(fmt.Sprintf("${file(%q)}", p))
return l, nil
}
func (t *TerraformTarget) RenderResource(resourceType string, resourceName string, e interface{}) error {
res := &terraformResource{
ResourceType: resourceType,
ResourceName: resourceName,
Item: e,
}
t.mutex.Lock()
defer t.mutex.Unlock()
t.resources = append(t.resources, res)
return nil
}
func (t *TerraformTarget) Finish(taskMap map[string]fi.Task) error {
resourcesByType := make(map[string]map[string]interface{})
for _, res := range t.resources {
resources := resourcesByType[res.ResourceType]
if resources == nil {
resources = make(map[string]interface{})
resourcesByType[res.ResourceType] = resources
}
tfName := tfSanitize(res.ResourceName)
if resources[tfName] != nil {
return fmt.Errorf("duplicate resource found: %s.%s", res.ResourceType, tfName)
}
resources[tfName] = res.Item
}
providersByName := make(map[string]map[string]interface{})
if t.Cloud.ProviderID() == fi.CloudProviderGCE {
providerGoogle := make(map[string]interface{})
providerGoogle["project"] = t.Project
providerGoogle["region"] = t.Region
providersByName["google"] = providerGoogle
} else if t.Cloud.ProviderID() == fi.CloudProviderAWS {
providerAWS := make(map[string]interface{})
providerAWS["region"] = t.Region
providersByName["aws"] = providerAWS
}
data := make(map[string]interface{})
data["resource"] = resourcesByType
if len(providersByName) != 0 {
data["provider"] = providersByName
}
jsonBytes, err := json.MarshalIndent(data, "", " ")
if err != nil {
return fmt.Errorf("error marshalling terraform data to json: %v", err)
}
useJson := false
if useJson {
t.files["kubernetes.tf"] = jsonBytes
} else {
f, err := hcl_parser.Parse(jsonBytes)
if err != nil {
return fmt.Errorf("error parsing terraform json: %v", err)
}
b, err := hclPrint(f)
if err != nil {
return fmt.Errorf("error writing terraform data to output: %v", err)
}
t.files["kubernetes.tf"] = b
}
for relativePath, contents := range t.files {
p := path.Join(t.outDir, relativePath)
err = os.MkdirAll(path.Dir(p), os.FileMode(0755))
if err != nil {
return fmt.Errorf("error creating output directory %q: %v", path.Dir(p), err)
}
err = ioutil.WriteFile(p, contents, os.FileMode(0644))
if err != nil {
return fmt.Errorf("error writing terraform data to output file %q: %v", p, err)
}
}
glog.Infof("Terraform output is in %s", t.outDir)
return nil
}