1
1
package core
2
2
3
3
import (
4
+ "bytes"
4
5
"encoding/json"
5
6
"fmt"
6
7
"io"
8
+ "os"
9
+ "path/filepath"
7
10
"reflect"
8
11
"strings"
9
12
"text/template"
@@ -12,6 +15,7 @@ import (
12
15
13
16
"github.com/scaleway/scaleway-cli/v2/internal/gofields"
14
17
"github.com/scaleway/scaleway-cli/v2/internal/human"
18
+ "github.com/scaleway/scaleway-cli/v2/internal/terraform"
15
19
)
16
20
17
21
// Type defines an formatter format.
@@ -28,6 +32,9 @@ const (
28
32
// PrinterTypeYAML defines a YAML formatter.
29
33
PrinterTypeYAML = PrinterType ("yaml" )
30
34
35
+ // PrinterTypeYAML defines a Terraform formatter.
36
+ PrinterTypeTerraform = PrinterType ("terraform" )
37
+
31
38
// PrinterTypeHuman defines a human readable formatted formatter.
32
39
PrinterTypeHuman = PrinterType ("human" )
33
40
@@ -39,6 +46,9 @@ const (
39
46
40
47
// Option to enable pretty output on json printer.
41
48
PrinterOptJSONPretty = "pretty"
49
+
50
+ // Option to enable pretty output on json printer.
51
+ PrinterOptTerraformWithChildren = "with-children"
42
52
)
43
53
44
54
type PrinterConfig struct {
@@ -75,6 +85,11 @@ func NewPrinter(config *PrinterConfig) (*Printer, error) {
75
85
}
76
86
case PrinterTypeYAML .String ():
77
87
printer .printerType = PrinterTypeYAML
88
+ case PrinterTypeTerraform .String ():
89
+ err := setupTerraformPrinter (printer , printerOpt )
90
+ if err != nil {
91
+ return nil , err
92
+ }
78
93
case PrinterTypeTemplate .String ():
79
94
err := setupTemplatePrinter (printer , printerOpt )
80
95
if err != nil {
@@ -100,6 +115,28 @@ func setupJSONPrinter(printer *Printer, opts string) error {
100
115
return nil
101
116
}
102
117
118
+ func setupTerraformPrinter (printer * Printer , opts string ) error {
119
+ printer .printerType = PrinterTypeTerraform
120
+ switch opts {
121
+ case PrinterOptTerraformWithChildren :
122
+ printer .terraformWithChildren = true
123
+ case "" :
124
+ default :
125
+ return fmt .Errorf ("invalid option %s for terraform outout. Valid options are: %s" , opts , PrinterOptTerraformWithChildren )
126
+ }
127
+
128
+ terraformVersion , err := terraform .GetVersion ()
129
+ if err != nil {
130
+ return err
131
+ }
132
+
133
+ if terraformVersion .Major < 1 || (terraformVersion .Major == 1 && terraformVersion .Minor < 5 ) {
134
+ return fmt .Errorf ("terraform version %s is not supported. Please upgrade to terraform >= 1.5.0" , terraformVersion .String ())
135
+ }
136
+
137
+ return nil
138
+ }
139
+
103
140
func setupTemplatePrinter (printer * Printer , opts string ) error {
104
141
printer .printerType = PrinterTypeTemplate
105
142
if opts == "" {
@@ -139,6 +176,9 @@ type Printer struct {
139
176
// Enable pretty print on json output
140
177
jsonPretty bool
141
178
179
+ // Enable children fetching on terraform output
180
+ terraformWithChildren bool
181
+
142
182
// go template to use on template output
143
183
template * template.Template
144
184
@@ -163,6 +203,8 @@ func (p *Printer) Print(data interface{}, opt *human.MarshalOpt) error {
163
203
err = p .printJSON (data )
164
204
case PrinterTypeYAML :
165
205
err = p .printYAML (data )
206
+ case PrinterTypeTerraform :
207
+ err = p .printTerraform (data )
166
208
case PrinterTypeTemplate :
167
209
err = p .printTemplate (data )
168
210
default :
@@ -283,6 +325,120 @@ func (p *Printer) printYAML(data interface{}) error {
283
325
return encoder .Encode (data )
284
326
}
285
327
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
+
353
+ func (p * Printer ) printTerraform (data interface {}) error {
354
+ writer := p .stdout
355
+ if _ , isError := data .(error ); isError {
356
+ return p .printHuman (data , nil )
357
+ }
358
+
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 )
434
+ return err
435
+ }
436
+
437
+ return p .printHuman (& CliError {
438
+ Err : fmt .Errorf ("no terraform association found for this resource type (%s)" , dataType ),
439
+ }, nil )
440
+ }
441
+
286
442
func (p * Printer ) printTemplate (data interface {}) error {
287
443
writer := p .stdout
288
444
if _ , isError := data .(error ); isError {
0 commit comments