2024-02-14 18:04:45 +00:00
package terraform
import (
"bytes"
"context"
"fmt"
"io"
"os"
"path/filepath"
"github.com/databricks/cli/bundle"
"github.com/databricks/cli/libs/cmdio"
2024-03-25 14:18:47 +00:00
"github.com/databricks/cli/libs/diag"
2024-02-14 18:04:45 +00:00
"github.com/hashicorp/terraform-exec/tfexec"
)
type BindOptions struct {
AutoApprove bool
ResourceType string
ResourceKey string
ResourceId string
}
type importResource struct {
opts * BindOptions
}
// Apply implements bundle.Mutator.
2024-03-25 14:18:47 +00:00
func ( m * importResource ) Apply ( ctx context . Context , b * bundle . Bundle ) diag . Diagnostics {
2024-02-14 18:04:45 +00:00
dir , err := Dir ( ctx , b )
if err != nil {
2024-03-25 14:18:47 +00:00
return diag . FromErr ( err )
2024-02-14 18:04:45 +00:00
}
tf := b . Terraform
if tf == nil {
2024-03-25 14:18:47 +00:00
return diag . Errorf ( "terraform not initialized" )
2024-02-14 18:04:45 +00:00
}
err = tf . Init ( ctx , tfexec . Upgrade ( true ) )
if err != nil {
2024-03-25 14:18:47 +00:00
return diag . Errorf ( "terraform init: %v" , err )
2024-02-14 18:04:45 +00:00
}
tmpDir , err := os . MkdirTemp ( "" , "state-*" )
if err != nil {
2024-03-25 14:18:47 +00:00
return diag . Errorf ( "terraform init: %v" , err )
2024-02-14 18:04:45 +00:00
}
tmpState := filepath . Join ( tmpDir , TerraformStateFileName )
importAddress := fmt . Sprintf ( "%s.%s" , m . opts . ResourceType , m . opts . ResourceKey )
err = tf . Import ( ctx , importAddress , m . opts . ResourceId , tfexec . StateOut ( tmpState ) )
if err != nil {
2024-03-25 14:18:47 +00:00
return diag . Errorf ( "terraform import: %v" , err )
2024-02-14 18:04:45 +00:00
}
buf := bytes . NewBuffer ( nil )
tf . SetStdout ( buf )
//lint:ignore SA1019 We use legacy -state flag for now to plan the import changes based on temporary state file
changed , err := tf . Plan ( ctx , tfexec . State ( tmpState ) , tfexec . Target ( importAddress ) )
if err != nil {
2024-03-25 14:18:47 +00:00
return diag . Errorf ( "terraform plan: %v" , err )
2024-02-14 18:04:45 +00:00
}
defer os . RemoveAll ( tmpDir )
if changed && ! m . opts . AutoApprove {
output := buf . String ( )
// Remove output starting from Warning until end of output
output = output [ : bytes . Index ( [ ] byte ( output ) , [ ] byte ( "Warning:" ) ) ]
cmdio . LogString ( ctx , output )
2024-09-02 13:43:17 +00:00
if ! cmdio . IsPromptSupported ( ctx ) {
return diag . Errorf ( "This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed." )
}
2024-02-14 18:04:45 +00:00
ans , err := cmdio . AskYesOrNo ( ctx , "Confirm import changes? Changes will be remotely applied only after running 'bundle deploy'." )
if err != nil {
2024-03-25 14:18:47 +00:00
return diag . FromErr ( err )
2024-02-14 18:04:45 +00:00
}
if ! ans {
2024-03-25 14:18:47 +00:00
return diag . Errorf ( "import aborted" )
2024-02-14 18:04:45 +00:00
}
}
// If user confirmed changes, move the state file from temp dir to state location
f , err := os . Create ( filepath . Join ( dir , TerraformStateFileName ) )
if err != nil {
2024-03-25 14:18:47 +00:00
return diag . FromErr ( err )
2024-02-14 18:04:45 +00:00
}
defer f . Close ( )
tmpF , err := os . Open ( tmpState )
if err != nil {
2024-03-25 14:18:47 +00:00
return diag . FromErr ( err )
2024-02-14 18:04:45 +00:00
}
defer tmpF . Close ( )
_ , err = io . Copy ( f , tmpF )
if err != nil {
2024-03-25 14:18:47 +00:00
return diag . FromErr ( err )
2024-02-14 18:04:45 +00:00
}
return nil
}
// Name implements bundle.Mutator.
func ( * importResource ) Name ( ) string {
return "terraform.Import"
}
func Import ( opts * BindOptions ) bundle . Mutator {
return & importResource { opts : opts }
}