1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package main
17
18 import (
19 "bytes"
20 "fmt"
21 "strings"
22 "text/template"
23
24 "github.com/iancoleman/strcase"
25 )
26
27
28 type workflowCodeGenerator struct {
29
30 chaosTypes []string
31 }
32
33 func newWorkflowCodeGenerator(types []string) *workflowCodeGenerator {
34 return &workflowCodeGenerator{chaosTypes: types}
35 }
36
37 func (it *workflowCodeGenerator) AppendTypes(typeName string) {
38 it.chaosTypes = append(it.chaosTypes, typeName)
39 }
40
41 func (it *workflowCodeGenerator) Render() string {
42
43 workflowTemplateTypesEntries := ""
44 for _, item := range it.chaosTypes {
45 workflowTemplateTypesEntries += generateTemplateTypes(item)
46 }
47
48 embedChaosEntries := ""
49 for _, item := range it.chaosTypes {
50 embedChaosEntries += generateEmbedChaos(item)
51 }
52
53 spawnObjectMethod := ""
54 for _, item := range it.chaosTypes {
55 spawnObjectMethod += generateMethodItem(item, spawnObjectEntryTemplate)
56 }
57 restoreObjectMethod := ""
58 for _, item := range it.chaosTypes {
59 restoreObjectMethod += generateMethodItem(item, restoreObjectEntryTemplate)
60 }
61 spawnListMethod := ""
62 for _, item := range it.chaosTypes {
63 spawnListMethod += generateSpawnListMethodItem(item)
64 }
65 allChaosTemplateTypeEntries := ""
66 for _, item := range it.chaosTypes {
67 allChaosTemplateTypeEntries += fmt.Sprintf(` Type%s,
68 `, item)
69 }
70
71 genericChaosListImplementations := ""
72 for _, item := range it.chaosTypes {
73 genericChaosListImplementations += generateGenericChaosList(item)
74 }
75
76 imports := `import (
77 "github.com/pkg/errors"
78 )
79 `
80
81 workflowTemplateTypesCodes := fmt.Sprintf(`%s
82
83 %s
84
85 const (
86 %s
87 )
88
89 var allChaosTemplateType = []TemplateType{
90 TypeSchedule,
91 %s
92 }
93
94 type EmbedChaos struct {
95 %s
96 }
97
98 func (it *EmbedChaos) SpawnNewObject(templateType TemplateType) (GenericChaos, error) {
99 switch templateType {
100 %s
101 default:
102 return nil, errors.Wrapf(errInvalidValue, "unknown template type %%s", templateType)
103 }
104 }
105
106 func (it *EmbedChaos) RestoreChaosSpec(root interface{}) error {
107 switch chaos := root.(type) {
108 %s
109 default:
110 return errors.Wrapf(errInvalidValue, "unknown chaos %%#v", root)
111 }
112 }
113
114 func (it *EmbedChaos) SpawnNewList(templateType TemplateType) (GenericChaosList, error) {
115 switch templateType {
116 %s
117 default:
118 return nil, errors.Wrapf(errInvalidValue, "unknown template type %%s", templateType)
119 }
120 }
121
122 %s
123 `,
124 boilerplate,
125 imports,
126 workflowTemplateTypesEntries,
127 allChaosTemplateTypeEntries,
128 embedChaosEntries,
129 spawnObjectMethod,
130 restoreObjectMethod,
131 spawnListMethod,
132 genericChaosListImplementations,
133 )
134
135 return workflowTemplateTypesCodes
136 }
137
138 const workflowTemplateTypeEntryTemplate = ` Type{{.Type}} TemplateType = "{{.Type}}"
139 `
140
141 func generateTemplateTypes(typeName string) string {
142 tmpl, err := template.New("workflowTemplates").Parse(workflowTemplateTypeEntryTemplate)
143 if err != nil {
144 log.Error(err, "fail to build template")
145 return ""
146 }
147
148 buf := new(bytes.Buffer)
149 err = tmpl.Execute(buf, &metadata{
150 Type: typeName,
151 })
152 if err != nil {
153 log.Error(err, "fail to execute template")
154 return ""
155 }
156
157 return buf.String()
158 }
159
160 const embedChaosEntryTemplate = ` // +optional
161 {{.Type}} *{{.Type}}Spec ` + "`" + `json:"{{.JsonField}},omitempty"` + "`" + `
162 `
163
164 func generateEmbedChaos(typeName string) string {
165 value := struct {
166 Type string
167 JsonField string
168 }{
169 Type: typeName,
170 JsonField: lowercaseCamelCase(typeName),
171 }
172 tmpl, err := template.New("workflowTemplates").Parse(embedChaosEntryTemplate)
173 if err != nil {
174 log.Error(err, "fail to build template")
175 return ""
176 }
177
178 buf := new(bytes.Buffer)
179 err = tmpl.Execute(buf, &value)
180 if err != nil {
181 log.Error(err, "fail to execute template")
182 return ""
183 }
184
185 return buf.String()
186 }
187
188 func lowercaseCamelCase(str string) string {
189
190
191 if strings.Contains(str, "Chaos") {
192 position := strings.Index(str, "Chaos")
193 return strings.ToLower(str[:position]) + str[position:]
194 }
195 return strcase.ToLowerCamel(str)
196 }
197
198 const spawnObjectEntryTemplate = ` case Type{{.Type}}:
199 result := {{.Type}}{}
200 result.Spec = *it.{{.Type}}
201 return &result, nil
202 `
203
204 const restoreObjectEntryTemplate = ` case *{{.Type}}:
205 *it.{{.Type}} = chaos.Spec
206 return nil
207 `
208
209 func generateMethodItem(typeName, methodTemplate string) string {
210 tmpl, err := template.New("fillMethodEntry").Parse(methodTemplate)
211 if err != nil {
212 log.Error(err, "fail to build template")
213 return ""
214 }
215
216 buf := new(bytes.Buffer)
217 err = tmpl.Execute(buf, &metadata{
218 Type: typeName,
219 })
220 if err != nil {
221 log.Error(err, "fail to execute template")
222 return ""
223 }
224
225 return buf.String()
226 }
227
228 const spawnListEntryTemplate = ` case Type{{.Type}}:
229 result := {{.Type}}List{}
230 return &result, nil
231 `
232
233 func generateSpawnListMethodItem(typeName string) string {
234 tmpl, err := template.New("fillingMethod").Parse(spawnListEntryTemplate)
235 if err != nil {
236 log.Error(err, "fail to build template")
237 return ""
238 }
239
240 buf := new(bytes.Buffer)
241 err = tmpl.Execute(buf, &metadata{
242 Type: typeName,
243 })
244 if err != nil {
245 log.Error(err, "fail to execute template")
246 return ""
247 }
248
249 return buf.String()
250 }
251
252 const genericChaosList = `func (in *{{.Type}}List) GetItems() []GenericChaos {
253 var result []GenericChaos
254 for _, item := range in.Items {
255 item := item
256 result = append(result, &item)
257 }
258 return result
259 }
260 `
261
262 func generateGenericChaosList(typeName string) string {
263 tmpl, err := template.New("genericChaosList").Parse(genericChaosList)
264 if err != nil {
265 log.Error(err, "fail to build template")
266 return ""
267 }
268
269 buf := new(bytes.Buffer)
270 err = tmpl.Execute(buf, &metadata{
271 Type: typeName,
272 })
273 if err != nil {
274 log.Error(err, "fail to execute template")
275 return ""
276 }
277
278 return buf.String()
279 }
280