-
Notifications
You must be signed in to change notification settings - Fork 0
/
resourcesUtils.go
473 lines (447 loc) · 14.4 KB
/
resourcesUtils.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
package sewansdk
import (
"encoding/json"
"github.com/hashicorp/terraform/helper/schema"
"net/http"
"strconv"
"strings"
)
// ResourceTooler contains implementation of Resourceer interface
type ResourceTooler struct {
Resource Resourceer
}
// Resourceer interface is responsible of operations on terraform resource
type Resourceer interface {
getResourceCreationURL(api *API,
resourceType string) string
getResourceURL(api *API,
resourceType string,
id string) string
validateResourceType(resourceType string) error
validateStatus(api *API,
resourceType string,
client ClientTooler) error
resourceInstanceCreate(d *schema.ResourceData,
templatesTooler *TemplatesTooler,
resourceType string,
api *API) (interface{}, error)
}
// ResourceResourceer implements Resourceer interface
type ResourceResourceer struct{}
type dynamicFieldStruct struct {
TerraformProvisioned bool `json:"terraform_provisioned"`
CreationTemplate string `json:"creationTemplate"`
TemplateDisksOnCreation []interface{} `json:"TemplateDisksOnCreation"`
}
type vdcResourceStruct struct {
Resource string `json:"vdc_resources"`
Used int `json:"used"`
Total int `json:"total"`
Slug string `json:"slug"`
}
type vdcStruct struct {
Name string `json:"name"`
Enterprise string `json:"enterprise"`
Datacenter string `json:"datacenter"`
VdcResources []interface{} `json:"vdc_resources"`
Slug string `json:"slug"`
DynamicField string `json:"dynamic_field"`
}
// This struct is not used in the code, however it remains here for dev doc purpose
//type vmDisk struct {
// Name string `json:"name"`
// Size int `json:"size"`
// StorageClass string `json:"storage_class"`
// Slug string `json:"slug"`
// VDisk string `json:"v_disk,omitempty"`
//}
// This struct is not used in the code, however it remains here for dev doc purpose
//type vmNic struct {
// Vlan string `json:"vlan"`
// MacAddress string `json:"mac_address"`
// Connected bool `json:"connected"`
//}
type vmStruct struct {
Name string `json:"name"`
Enterprise string `json:"enterprise"`
Template string `json:"template,omitempty"`
State string `json:"state"`
OS string `json:"os,omitempty"`
RAM int `json:"ram"`
CPU int `json:"cpu"`
Disks []interface{} `json:"disks,omitempty"`
Nics []interface{} `json:"nics,omitempty"`
Vdc string `json:"vdc"`
Boot string `json:"boot"`
StorageClass string `json:"storage_class"`
Slug string `json:"slug"`
Token string `json:"token"`
Backup string `json:"backup"`
Iso string `json:"disk_image"`
PlatformName string `json:"platform_name"`
BackupSize int `json:"backup_size"`
Comment string `json:"comment,omitempty"`
DynamicField string `json:"dynamic_field"`
Outsourcing string `json:"outsourcing"`
}
// getDataCenterCos returns the cos field of a data center,
// the cos field represents the resource type of DatCenter :
// * mono or HA (high availability)
func getDataCenterCos(dataCenter string, api *API) string {
for _, listDataCenter := range api.Meta.DataCenterList {
listDataCenterSlug := listDataCenter.(map[string]interface{})[SlugField].(string)
if dataCenter == listDataCenterSlug {
return listDataCenter.(map[string]interface{})[ResourceCosField].(string)
}
}
return ""
}
// validateDatacenter validates datacenter is in available dataCenter list in api.
func validateDatacenter(dataCenter string, api *API) error {
var (
isInSlice bool
sliceElements strings.Builder
)
for _, sliceElem := range api.Meta.DataCenterList {
sliceElemSlug := sliceElem.(map[string]interface{})[SlugField].(string)
sliceElements.WriteString(" \"")
sliceElements.WriteString(sliceElemSlug)
sliceElements.WriteString("\"")
if dataCenter == sliceElemSlug {
isInSlice = true
}
}
if isInSlice {
return nil
}
return errNotInList(dataCenter, sliceElements.String())
}
// validateVdcResources validates VDC resources exists in clouddc environment
// resources list.
func validateVdcResources(d *schema.ResourceData,
api *API, cos string) error {
for _, resource := range d.Get(VdcResourceField).([]interface{}) {
var (
resourceExists bool
availableResourceList strings.Builder
)
for _, apiResource := range api.Meta.EnterpriseResourceList {
isRightCos := (apiResource.(map[string]interface{})[ResourceCosField] == cos)
isResourceExistingInMeta := (apiResource.(map[string]interface{})[NameField] == resource.(map[string]interface{})[ResourceField])
if isRightCos {
availableResourceList.WriteString(" \"")
availableResourceList.WriteString(apiResource.(map[string]interface{})[NameField].(string))
availableResourceList.WriteString("\"")
if isResourceExistingInMeta {
resourceExists = true
}
}
}
if !resourceExists {
return errResourceNotExist(resource.(map[string]interface{})[ResourceField].(string),
availableResourceList.String())
}
}
return nil
}
// validateTemplate validates template is in available template list in api.
func validateTemplate(d *schema.ResourceData,
api *API) error {
var (
isInSlice bool
sliceElements strings.Builder
template = d.Get(TemplateField).(string)
)
if template == "" {
return nil
}
for _, sliceElem := range api.Meta.TemplateList {
sliceElemSlug := sliceElem.(map[string]interface{})[SlugField].(string)
sliceElements.WriteString(" \"")
sliceElements.WriteString(sliceElemSlug)
sliceElements.WriteString("\"")
if template == sliceElemSlug {
isInSlice = true
}
}
if isInSlice {
return nil
}
return errNotInList(template, sliceElements.String())
}
// validateVMsVDC validates the vm's vdc is in available vdc list in api.
func validateVMsVDC(d *schema.ResourceData,
api *API) error {
var (
isInSlice bool
sliceElements strings.Builder
vdc = d.Get(VdcField).(string)
)
for _, sliceElem := range api.Meta.EnterpriseVdcList {
sliceElemSlug := sliceElem.(map[string]interface{})[SlugField].(string)
sliceElements.WriteString(" \"")
sliceElements.WriteString(sliceElemSlug)
sliceElements.WriteString("\"")
if vdc == sliceElemSlug {
isInSlice = true
}
}
if isInSlice {
return nil
}
return errNotInList(vdc, sliceElements.String())
}
// validateNics validates the vm's nics are in available vlan list in api.
func validateNics(d *schema.ResourceData,
api *API) error {
var (
isInSlice bool
sliceElements strings.Builder
nicList = d.Get(NicsField).([]interface{})
)
for _, nic := range nicList {
isInSlice = false
nicVlan := nic.(map[string]interface{})[VlanNameField].(string)
for _, sliceElem := range api.Meta.VlanList {
sliceElemSlug := sliceElem.(map[string]interface{})[SlugField].(string)
sliceElements.WriteString(" \"")
sliceElements.WriteString(sliceElemSlug)
sliceElements.WriteString("\"")
if nicVlan == sliceElemSlug {
isInSlice = true
}
}
if !isInSlice {
return errNotInList(nicVlan, sliceElements.String())
}
}
return nil
}
// validateResourceFieldsValue validates all resources values match on of
// of the available value in clouddc environment resources list
func validateResourceFieldsValue(d *schema.ResourceData,
api *API,
resourceType string) error {
switch resourceType {
case VdcResourceType:
dataCenter := d.Get(DataCenterField).(string)
err1 := validateDatacenter(dataCenter, api)
if err1 != nil {
return err1
}
err2 := validateVdcResources(d, api, getDataCenterCos(dataCenter, api))
if err2 != nil {
return err2
}
case VMResourceType:
err3 := validateTemplate(d, api)
if err3 != nil {
return err3
}
err4 := validateNics(d, api)
if err4 != nil {
return err4
}
err5 := validateVMsVDC(d, api)
if err5 != nil {
return err5
}
}
return nil
}
// resourceInstanceCreate creates a resource structure initialized with
// fields values got from schema.
// Accepted resource types : "vm", "vdc"
func (resource ResourceResourceer) resourceInstanceCreate(d *schema.ResourceData,
templatesTooler *TemplatesTooler,
resourceType string,
api *API) (interface{}, error) {
switch resourceType {
case VdcResourceType:
err1 := validateResourceFieldsValue(d, api, VdcResourceType)
if err1 != nil {
return vdcStruct{}, err1
}
return vdcInstanceCreate(d, api)
case VMResourceType:
err2 := validateResourceFieldsValue(d, api, VMResourceType)
if err2 != nil {
return vmStruct{}, err2
}
return vmInstanceCreate(d,
templatesTooler,
api)
default:
return nil, resource.validateResourceType(resourceType)
}
}
//validateResourceType validates if resource type is in
// list of accepted resource types : "vm", "vdc"
func (resource ResourceResourceer) validateResourceType(resourceType string) error {
switch resourceType {
case VdcResourceType:
return nil
case VMResourceType:
return nil
default:
return errWrongResourceTypeBuilder(resourceType)
}
}
// getResourceCreationURL returns valid urls for resource creation :
// * https://cloud-datacenter.fr/api/clouddc/vm/
// * https://cloud-datacenter.fr/api/clouddc/vdc/
// * etc.
func (resource ResourceResourceer) getResourceCreationURL(api *API,
resourceType string) string {
var resourceURL strings.Builder
resourceURL.WriteString(api.URL)
resourceURL.WriteString(resourceType)
resourceURL.WriteString("/")
return resourceURL.String()
}
// getResourceURL returns valid resources urls :
// * https://cloud-datacenter.fr/api/clouddc/vm/<a resource id number>/
// * https://cloud-datacenter.fr/api/clouddc/vdc/<a resource id number>/
// * etc.
func (resource ResourceResourceer) getResourceURL(api *API,
resourceType string,
resourceID string) string {
var resourceURL strings.Builder
sCreateURL := resource.getResourceCreationURL(api, resourceType)
resourceURL.WriteString(sCreateURL)
resourceURL.WriteString(resourceID)
resourceURL.WriteString("/")
return resourceURL.String()
}
// validateStatus validates api status by sending a test GET request and validating response
func (resource ResourceResourceer) validateStatus(api *API,
resourceType string,
clientTooler ClientTooler) error {
req, _ := http.NewRequest("GET",
resource.getResourceCreationURL(api, resourceType),
nil)
req.Header.Add(httpAuthorization, httpTokenHeader+api.Token)
resp, err1 := clientTooler.Client.do(api, req)
if err1 != nil {
return err1
}
_, err2 := clientTooler.Client.handleResponse(resp,
http.StatusOK,
httpJSONContentType)
return err2
}
func vdcInstanceCreate(d *schema.ResourceData, api *API) (vdcStruct, error) {
vdc := vdcStruct{
Name: d.Get(NameField).(string),
Enterprise: api.Enterprise,
Datacenter: d.Get(DataCenterField).(string),
VdcResources: d.Get(VdcResourceField).([]interface{}),
Slug: d.Get(SlugField).(string),
DynamicField: d.Get(DynamicField).(string),
}
for index, resource := range vdc.VdcResources {
resourceSlug, err := getResourceSlug(resource.(map[string]interface{})[ResourceField].(string),
*api.Meta)
if err != nil {
return vdcStruct{}, err
}
vdc.VdcResources[index].(map[string]interface{})[ResourceField] = resourceSlug
}
return vdc, nil
}
func getResourceSlug(resourceName string, meta APIMeta) (string, error) {
for _, resource := range meta.EnterpriseResourceList {
resourceExistsInMeta := (resource.(map[string]interface{})[NameField] == resourceName)
isResourceMonoTyped := resource.(map[string]interface{})[ResourceCosField] == MonoResourceType
if resourceExistsInMeta && isResourceMonoTyped {
return resource.(map[string]interface{})[SlugField].(string), nil
}
}
return "", errResourceNotExist(resourceName, "")
}
func getTemplateAndUpdateSchema(templateSlug string,
d *schema.ResourceData,
templatesTooler *TemplatesTooler,
api *API) (map[string]interface{}, error) {
template, err1 := templatesTooler.TemplatesTools.FetchTemplateFromList(templateSlug,
api.Meta.TemplateList)
if err1 != nil {
return map[string]interface{}{}, err1
}
err2 := templatesTooler.TemplatesTools.validateTemplate(template)
if err2 != nil {
return map[string]interface{}{}, err2
}
err3 := templatesTooler.TemplatesTools.updateSchemaFromTemplateOnResourceCreation(d,
template)
if err3 != nil {
return map[string]interface{}{}, err3
}
return template, nil
}
func vmInstanceCreate(d *schema.ResourceData,
templatesTooler *TemplatesTooler,
api *API) (vmStruct, error) {
var (
templateError error
template map[string]interface{}
templateSlug = d.Get(TemplateField).(string)
vmName strings.Builder
)
vmName.WriteString(d.Get(NameField).(string))
if templateSlug != "" && d.Id() == "" {
instanceNumber := d.Get(InstanceNumberField).(int)
vmName.WriteString(resourceNameCountSeparator)
vmName.WriteString(strconv.Itoa(instanceNumber))
template,
templateError = getTemplateAndUpdateSchema(templateSlug,
d,
templatesTooler,
api)
}
if templateError != nil {
return vmStruct{}, templateError
}
vm := vmStruct{
Name: vmName.String(),
Enterprise: api.Enterprise,
State: d.Get(StateField).(string),
OS: d.Get(OsField).(string),
RAM: d.Get(RAMField).(int),
CPU: d.Get(CPUField).(int),
Disks: d.Get(DisksField).([]interface{}),
Nics: d.Get(NicsField).([]interface{}),
Vdc: d.Get(VdcField).(string),
Boot: d.Get(BootField).(string),
StorageClass: d.Get(StorageClassField).(string),
Slug: d.Get(SlugField).(string),
Token: d.Get(TokenField).(string),
Backup: d.Get(BackupField).(string),
Iso: d.Get(IsoField).(string),
PlatformName: d.Get(PlatformNameField).(string),
BackupSize: d.Get(BackupSizeField).(int),
DynamicField: d.Get(DynamicField).(string),
}
if d.Id() == "" {
dynamicFieldStruct := dynamicFieldStruct{
TerraformProvisioned: true,
CreationTemplate: d.Get(TemplateField).(string),
TemplateDisksOnCreation: nil,
}
if template != nil {
dynamicFieldStruct.TemplateDisksOnCreation = template[DisksField].([]interface{})
_, err := templatesTooler.TemplatesTools.createVMTemplateOverrideConfig(d,
template)
if err != nil {
return vmStruct{}, err
}
vm.Template = d.Get(TemplateField).(string)
}
dynamicFieldJSON, err2 := json.Marshal(dynamicFieldStruct)
if err2 != nil {
return vmStruct{}, err2
}
vm.DynamicField = string(dynamicFieldJSON)
}
return vm, nil
}