Skip to content

Commit 1e98790

Browse files
committed
refactor: cleaning
1 parent 079dc09 commit 1e98790

File tree

5 files changed

+248
-187
lines changed

5 files changed

+248
-187
lines changed

internal/core/printer.go

+5-107
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package core
22

33
import (
4-
"bytes"
54
"encoding/json"
65
"fmt"
76
"io"
8-
"os"
9-
"path/filepath"
107
"reflect"
118
"strings"
129
"text/template"
@@ -125,7 +122,7 @@ func setupTerraformPrinter(printer *Printer, opts string) error {
125122
return fmt.Errorf("invalid option %s for terraform outout. Valid options are: %s", opts, PrinterOptTerraformWithChildren)
126123
}
127124

128-
terraformVersion, err := terraform.GetVersion()
125+
terraformVersion, err := terraform.GetLocalClientVersion()
129126
if err != nil {
130127
return err
131128
}
@@ -325,118 +322,19 @@ func (p *Printer) printYAML(data interface{}) error {
325322
return encoder.Encode(data)
326323
}
327324

328-
type TerraformImportTemplateData struct {
329-
ResourceID string
330-
ResourceName string
331-
}
332-
333-
const terraformImportTemplate = `
334-
terraform {
335-
required_providers {
336-
scaleway = {
337-
source = "scaleway/scaleway"
338-
}
339-
}
340-
required_version = ">= 0.13"
341-
}
342-
343-
import {
344-
# ID of the cloud resource
345-
# Check provider documentation for importable resources and format
346-
id = "{{ .ResourceID }}"
347-
348-
# Resource address
349-
to = {{ .ResourceName }}.main
350-
}
351-
`
352-
353325
func (p *Printer) printTerraform(data interface{}) error {
354326
writer := p.stdout
355327
if _, isError := data.(error); isError {
356328
return p.printHuman(data, nil)
357329
}
358330

359-
dataValue := reflect.ValueOf(data)
360-
dataType := dataValue.Type().Elem()
361-
362-
for i, association := range terraform.Associations {
363-
iValue := reflect.ValueOf(i)
364-
iType := iValue.Type().Elem()
365-
if dataType != iType {
366-
continue
367-
}
368-
369-
tmpl, err := template.New("terraform").Parse(association.ImportFormat)
370-
if err != nil {
371-
return err
372-
}
373-
374-
var resourceID bytes.Buffer
375-
err = tmpl.Execute(&resourceID, data)
376-
if err != nil {
377-
return err
378-
}
379-
380-
// Create temporary directory
381-
tmpDir, err := os.MkdirTemp("", "scw-*")
382-
if err != nil {
383-
return err
384-
}
385-
386-
tmplFile, err := os.CreateTemp(tmpDir, "*.tf")
387-
if err != nil {
388-
return err
389-
}
390-
defer os.Remove(tmplFile.Name())
391-
392-
tmpl, err = template.New("terraform").Parse(terraformImportTemplate)
393-
if err != nil {
394-
return err
395-
}
396-
// Write the terraform file
397-
err = tmpl.Execute(tmplFile, TerraformImportTemplateData{
398-
ResourceID: resourceID.String(),
399-
ResourceName: association.ResourceName,
400-
})
401-
if err != nil {
402-
return err
403-
}
404-
405-
// Close the file
406-
err = tmplFile.Close()
407-
if err != nil {
408-
return err
409-
}
410-
411-
res, err := terraform.Init(tmpDir)
412-
if err != nil {
413-
return err
414-
}
415-
if res.ExitCode != 0 {
416-
return fmt.Errorf("terraform init failed: %s", res.Stderr)
417-
}
418-
419-
res, err = terraform.GenerateConfig(tmpDir, "output.tf")
420-
if err != nil {
421-
return err
422-
}
423-
if res.ExitCode != 0 {
424-
return fmt.Errorf("terraform generate failed: %s", res.Stderr)
425-
}
426-
427-
// Print the generated config
428-
data, err := os.ReadFile(filepath.Join(tmpDir, "output.tf"))
429-
if err != nil {
430-
return err
431-
}
432-
433-
_, err = writer.Write(data)
331+
hcl, err := terraform.GetHCL(data)
332+
if err != nil {
434333
return err
435334
}
436335

437-
return p.printHuman(&CliError{
438-
Err: fmt.Errorf("no terraform association found for this resource type (%s)", dataType),
439-
}, nil)
336+
_, err = writer.Write([]byte(hcl))
337+
return err
440338
}
441339

442340
func (p *Printer) printTemplate(data interface{}) error {

internal/terraform/association.go

+46-9
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,69 @@
11
package terraform
22

33
import (
4+
"reflect"
5+
46
"github.com/scaleway/scaleway-sdk-go/api/baremetal/v1"
57
container "github.com/scaleway/scaleway-sdk-go/api/container/v1beta1"
68
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
79
)
810

9-
type Association struct {
11+
type associationSubResource struct {
12+
TerraformAttributeName string
13+
Command string
14+
AsDataSource bool
15+
}
16+
17+
type association struct {
1018
ResourceName string
1119
ImportFormat string
12-
SubResources []string
20+
SubResources map[string]*associationSubResource
1321
}
1422

15-
const ImportFormatID = "{{ .Region }}/{{ .ID }}"
16-
const ImportFormatZoneID = "{{ .Zone }}/{{ .ID }}"
17-
const ImportFormatRegionID = "{{ .Region }}/{{ .ID }}"
23+
// const importFormatID = "{{ .Region }}/{{ .ID }}"
24+
const importFormatZoneID = "{{ .Zone }}/{{ .ID }}"
25+
const importFormatRegionID = "{{ .Region }}/{{ .ID }}"
1826

19-
var Associations = map[interface{}]Association{
27+
var associations = map[interface{}]*association{
2028
&baremetal.Server{}: {
2129
ResourceName: "scaleway_baremetal_server",
22-
ImportFormat: ImportFormatZoneID,
30+
ImportFormat: importFormatZoneID,
2331
},
2432
&instance.Server{}: {
2533
ResourceName: "scaleway_instance_server",
26-
ImportFormat: ImportFormatZoneID,
34+
ImportFormat: importFormatZoneID,
2735
},
2836
&container.Container{}: {
2937
ResourceName: "scaleway_container",
30-
ImportFormat: ImportFormatRegionID,
38+
ImportFormat: importFormatRegionID,
39+
SubResources: map[string]*associationSubResource{
40+
"NamespaceID": {
41+
TerraformAttributeName: "namespace_id",
42+
Command: "container namespace get {{ .NamespaceID }}",
43+
},
44+
},
45+
},
46+
&container.Namespace{}: {
47+
ResourceName: "scaleway_container_namespace",
48+
ImportFormat: importFormatRegionID,
49+
SubResources: map[string]*associationSubResource{
50+
"ProjectID": {
51+
TerraformAttributeName: "project_id",
52+
Command: "container project get project-id={{ .ProjectID }}",
53+
AsDataSource: true,
54+
},
55+
},
3156
},
3257
}
58+
59+
func getAssociation(data interface{}) (*association, bool) {
60+
dataType := reflect.TypeOf(data)
61+
62+
for i, association := range associations {
63+
if dataType == reflect.TypeOf(i) {
64+
return association, true
65+
}
66+
}
67+
68+
return nil, false
69+
}

internal/terraform/hcl.go

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package terraform
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"html/template"
7+
"os"
8+
"path/filepath"
9+
"reflect"
10+
)
11+
12+
func getResourceID(format string, data interface{}) (string, error) {
13+
tmpl, err := template.New("terraform").Parse(format)
14+
if err != nil {
15+
return "", err
16+
}
17+
18+
var resourceID bytes.Buffer
19+
err = tmpl.Execute(&resourceID, data)
20+
if err != nil {
21+
return "", err
22+
}
23+
24+
return resourceID.String(), nil
25+
}
26+
27+
type hclImportTemplateData struct {
28+
ResourceID string
29+
ResourceName string
30+
}
31+
32+
const hclImportTemplate = `
33+
terraform {
34+
required_providers {
35+
scaleway = {
36+
source = "scaleway/scaleway"
37+
}
38+
}
39+
required_version = ">= 0.13"
40+
}
41+
42+
import {
43+
# ID of the cloud resource
44+
# Check provider documentation for importable resources and format
45+
id = "{{ .ResourceID }}"
46+
47+
# Resource address
48+
to = {{ .ResourceName }}.main
49+
}
50+
`
51+
52+
func createImportFile(directory string, association *association, data interface{}) error {
53+
importFile, err := os.CreateTemp(directory, "*.tf")
54+
if err != nil {
55+
return err
56+
}
57+
defer importFile.Close()
58+
59+
resourceID, err := getResourceID(association.ImportFormat, data)
60+
if err != nil {
61+
return err
62+
}
63+
64+
tmpl, err := template.New("").Parse(hclImportTemplate)
65+
if err != nil {
66+
return err
67+
}
68+
// Write the terraform file
69+
err = tmpl.Execute(importFile, hclImportTemplateData{
70+
ResourceID: resourceID,
71+
ResourceName: association.ResourceName,
72+
})
73+
if err != nil {
74+
return err
75+
}
76+
77+
return nil
78+
}
79+
80+
func GetHCL(data interface{}) (string, error) {
81+
association, ok := getAssociation(data)
82+
if !ok {
83+
return "", fmt.Errorf("no terraform association found for this resource type (%s)", reflect.TypeOf(data).Name())
84+
}
85+
86+
// Create temporary directory
87+
tmpDir, err := os.MkdirTemp("", "scw-*")
88+
if err != nil {
89+
return "", err
90+
}
91+
defer os.RemoveAll(tmpDir)
92+
93+
err = createImportFile(tmpDir, association, data)
94+
if err != nil {
95+
return "", err
96+
}
97+
98+
res, err := runInitCommand(tmpDir)
99+
if err != nil {
100+
return "", err
101+
}
102+
if res.ExitCode != 0 {
103+
return "", fmt.Errorf("terraform init failed: %s", res.Stderr)
104+
}
105+
106+
res, err = runGenerateConfigCommand(tmpDir, "output.tf")
107+
if err != nil {
108+
return "", err
109+
}
110+
if res.ExitCode != 0 {
111+
return "", fmt.Errorf("terraform generate failed: %s", res.Stderr)
112+
}
113+
114+
// Read the generated output
115+
output, err := os.ReadFile(filepath.Join(tmpDir, "output.tf"))
116+
if err != nil {
117+
return "", err
118+
}
119+
120+
return string(output), nil
121+
}

0 commit comments

Comments
 (0)