diff --git a/bundle/deploy/terraform/load.go b/bundle/deploy/terraform/load.go index efd329f4..7f350abf 100644 --- a/bundle/deploy/terraform/load.go +++ b/bundle/deploy/terraform/load.go @@ -2,8 +2,10 @@ package terraform import ( "context" + "fmt" "github.com/databricks/bricks/bundle" + "github.com/hashicorp/terraform-exec/tfexec" ) type load struct{} @@ -13,6 +15,16 @@ func (l *load) Name() string { } func (l *load) Apply(ctx context.Context, b *bundle.Bundle) ([]bundle.Mutator, error) { + tf := b.Terraform + if tf == nil { + return nil, fmt.Errorf("terraform not initialized") + } + + err := tf.Init(ctx, tfexec.Upgrade(true)) + if err != nil { + return nil, fmt.Errorf("terraform init: %w", err) + } + state, err := b.Terraform.Show(ctx) if err != nil { return nil, err diff --git a/bundle/deploy/terraform/pkg.go b/bundle/deploy/terraform/pkg.go new file mode 100644 index 00000000..5e3807be --- /dev/null +++ b/bundle/deploy/terraform/pkg.go @@ -0,0 +1,3 @@ +package terraform + +const TerraformStateFileName = "terraform.tfstate" diff --git a/bundle/deploy/terraform/state_pull.go b/bundle/deploy/terraform/state_pull.go new file mode 100644 index 00000000..04a6b2eb --- /dev/null +++ b/bundle/deploy/terraform/state_pull.go @@ -0,0 +1,62 @@ +package terraform + +import ( + "context" + "io" + "os" + "path/filepath" + + "github.com/databricks/bricks/bundle" + "github.com/databricks/bricks/libs/filer" + "github.com/databricks/bricks/libs/log" + "github.com/databricks/databricks-sdk-go/apierr" +) + +type statePull struct{} + +func (l *statePull) Name() string { + return "terraform:state-pull" +} + +func (l *statePull) Apply(ctx context.Context, b *bundle.Bundle) ([]bundle.Mutator, error) { + f, err := filer.NewWorkspaceFilesClient(b.WorkspaceClient(), b.Config.Workspace.StatePath.Workspace) + if err != nil { + return nil, err + } + + dir, err := Dir(b) + if err != nil { + return nil, err + } + + // Download state file from filer to local cache directory. + log.Infof(ctx, "Opening remote state file") + remote, err := f.Read(ctx, TerraformStateFileName) + if err != nil { + // On first deploy this state file doesn't yet exist. + if apierr.IsMissing(err) { + log.Infof(ctx, "Remote state file does not exist") + return nil, nil + } + return nil, err + } + + // Expect the state file to live under dir. + local, err := os.OpenFile(filepath.Join(dir, TerraformStateFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) + if err != nil { + return nil, err + } + + // Write file to disk. + log.Infof(ctx, "Writing remote state file to local cache directory") + _, err = io.Copy(local, remote) + if err != nil { + return nil, err + } + + return nil, nil +} + +func StatePull() bundle.Mutator { + return &statePull{} +} diff --git a/bundle/deploy/terraform/state_push.go b/bundle/deploy/terraform/state_push.go new file mode 100644 index 00000000..03c1bc4c --- /dev/null +++ b/bundle/deploy/terraform/state_push.go @@ -0,0 +1,48 @@ +package terraform + +import ( + "context" + "os" + "path/filepath" + + "github.com/databricks/bricks/bundle" + "github.com/databricks/bricks/libs/filer" + "github.com/databricks/bricks/libs/log" +) + +type statePush struct{} + +func (l *statePush) Name() string { + return "terraform:state-push" +} + +func (l *statePush) Apply(ctx context.Context, b *bundle.Bundle) ([]bundle.Mutator, error) { + f, err := filer.NewWorkspaceFilesClient(b.WorkspaceClient(), b.Config.Workspace.StatePath.Workspace) + if err != nil { + return nil, err + } + + dir, err := Dir(b) + if err != nil { + return nil, err + } + + // Expect the state file to live under dir. + local, err := os.Open(filepath.Join(dir, TerraformStateFileName)) + if err != nil { + return nil, err + } + + // Upload state file from local cache directory to filer. + log.Infof(ctx, "Writing local state file to remote state directory") + err = f.Write(ctx, TerraformStateFileName, local, filer.CreateParentDirectories, filer.OverwriteIfExists) + if err != nil { + return nil, err + } + + return nil, nil +} + +func StatePush() bundle.Mutator { + return &statePush{} +} diff --git a/bundle/phases/deploy.go b/bundle/phases/deploy.go index 299cd3eb..2b656c8d 100644 --- a/bundle/phases/deploy.go +++ b/bundle/phases/deploy.go @@ -18,7 +18,9 @@ func Deploy() bundle.Mutator { artifacts.UploadAll(), terraform.Interpolate(), terraform.Write(), + terraform.StatePull(), terraform.Apply(), + terraform.StatePush(), lock.Release(), }, ) diff --git a/cmd/bundle/run.go b/cmd/bundle/run.go index 51d81e11..431290d7 100644 --- a/cmd/bundle/run.go +++ b/cmd/bundle/run.go @@ -25,6 +25,9 @@ var runCmd = &cobra.Command{ b := bundle.Get(cmd.Context()) err := bundle.Apply(cmd.Context(), b, []bundle.Mutator{ phases.Initialize(), + terraform.Interpolate(), + terraform.Write(), + terraform.StatePull(), terraform.Load(), }) if err != nil {