11package core
22
33import (
4+ "bytes"
45 "encoding/json"
56 "fmt"
67 "io"
8+ "os"
9+ "path/filepath"
710 "reflect"
811 "strings"
912 "text/template"
@@ -12,6 +15,7 @@ import (
1215
1316 "github.com/scaleway/scaleway-cli/v2/internal/gofields"
1417 "github.com/scaleway/scaleway-cli/v2/internal/human"
18+ "github.com/scaleway/scaleway-cli/v2/internal/terraform"
1519)
1620
1721// Type defines an formatter format.
@@ -28,6 +32,9 @@ const (
2832 // PrinterTypeYAML defines a YAML formatter.
2933 PrinterTypeYAML = PrinterType ("yaml" )
3034
35+ // PrinterTypeYAML defines a Terraform formatter.
36+ PrinterTypeTerraform = PrinterType ("terraform" )
37+
3138 // PrinterTypeHuman defines a human readable formatted formatter.
3239 PrinterTypeHuman = PrinterType ("human" )
3340
@@ -39,6 +46,9 @@ const (
3946
4047 // Option to enable pretty output on json printer.
4148 PrinterOptJSONPretty = "pretty"
49+
50+ // Option to enable pretty output on json printer.
51+ PrinterOptTerraformWithChildren = "with-children"
4252)
4353
4454type PrinterConfig struct {
@@ -75,6 +85,11 @@ func NewPrinter(config *PrinterConfig) (*Printer, error) {
7585 }
7686 case PrinterTypeYAML .String ():
7787 printer .printerType = PrinterTypeYAML
88+ case PrinterTypeTerraform .String ():
89+ err := setupTerraformPrinter (printer , printerOpt )
90+ if err != nil {
91+ return nil , err
92+ }
7893 case PrinterTypeTemplate .String ():
7994 err := setupTemplatePrinter (printer , printerOpt )
8095 if err != nil {
@@ -100,6 +115,28 @@ func setupJSONPrinter(printer *Printer, opts string) error {
100115 return nil
101116}
102117
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+
103140func setupTemplatePrinter (printer * Printer , opts string ) error {
104141 printer .printerType = PrinterTypeTemplate
105142 if opts == "" {
@@ -139,6 +176,9 @@ type Printer struct {
139176 // Enable pretty print on json output
140177 jsonPretty bool
141178
179+ // Enable children fetching on terraform output
180+ terraformWithChildren bool
181+
142182 // go template to use on template output
143183 template * template.Template
144184
@@ -163,6 +203,8 @@ func (p *Printer) Print(data interface{}, opt *human.MarshalOpt) error {
163203 err = p .printJSON (data )
164204 case PrinterTypeYAML :
165205 err = p .printYAML (data )
206+ case PrinterTypeTerraform :
207+ err = p .printTerraform (data )
166208 case PrinterTypeTemplate :
167209 err = p .printTemplate (data )
168210 default :
@@ -283,6 +325,120 @@ func (p *Printer) printYAML(data interface{}) error {
283325 return encoder .Encode (data )
284326}
285327
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+
286442func (p * Printer ) printTemplate (data interface {}) error {
287443 writer := p .stdout
288444 if _ , isError := data .(error ); isError {
0 commit comments