mirror of https://github.com/databricks/cli.git
Remove unused package bundle/deployer (#1607)
## Changes This has been superseded by individual mutators under `bundle/deploy/terraform`. ## Tests n/a
This commit is contained in:
parent
6953a5d5af
commit
2aeea5e384
|
@ -1,192 +0,0 @@
|
|||
package deployer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/databricks/cli/libs/locker"
|
||||
"github.com/databricks/cli/libs/log"
|
||||
"github.com/databricks/databricks-sdk-go"
|
||||
"github.com/hashicorp/terraform-exec/tfexec"
|
||||
)
|
||||
|
||||
type DeploymentStatus int
|
||||
|
||||
const (
|
||||
// Empty plan produced on terraform plan. No changes need to be applied
|
||||
NoChanges DeploymentStatus = iota
|
||||
|
||||
// Deployment failed. No databricks assets were deployed
|
||||
Failed
|
||||
|
||||
// Deployment failed/partially succeeded. failed to update remote terraform
|
||||
// state file.
|
||||
// The partially deployed resources are thus untracked and in most cases
|
||||
// will need to be cleaned up manually
|
||||
PartialButUntracked
|
||||
|
||||
// Deployment failed/partially succeeded. Remote terraform state file is
|
||||
// updated with any partially deployed resources
|
||||
Partial
|
||||
|
||||
// Deployment succeeded however the remote terraform state was not updated.
|
||||
// The deployed resources are thus untracked and in most cases will need to
|
||||
// be cleaned up manually
|
||||
CompleteButUntracked
|
||||
|
||||
// Deployment succeeeded with remote terraform state file updated
|
||||
Complete
|
||||
)
|
||||
|
||||
// Deployer is a struct to deploy a DAB to a databricks workspace
|
||||
//
|
||||
// Here's a high level description of what a deploy looks like:
|
||||
//
|
||||
// 1. Client compiles the bundle configuration to a terraform HCL config file
|
||||
//
|
||||
// 2. Client tries to acquire a lock on the remote root of the project.
|
||||
// -- If FAIL: print details about current holder of the deployment lock on
|
||||
// remote root and terminate deployment
|
||||
//
|
||||
// 3. Client reads terraform state from remote root
|
||||
//
|
||||
// 4. Client applies the diff in terraform config to the databricks workspace
|
||||
//
|
||||
// 5. Client updates terraform state file in remote root
|
||||
//
|
||||
// 6. Client releases the deploy lock on remote root
|
||||
type Deployer struct {
|
||||
localRoot string
|
||||
remoteRoot string
|
||||
env string
|
||||
locker *locker.Locker
|
||||
wsc *databricks.WorkspaceClient
|
||||
}
|
||||
|
||||
func Create(ctx context.Context, env, localRoot, remoteRoot string, wsc *databricks.WorkspaceClient) (*Deployer, error) {
|
||||
user, err := wsc.CurrentUser.Me(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newLocker, err := locker.CreateLocker(user.UserName, remoteRoot, wsc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Deployer{
|
||||
localRoot: localRoot,
|
||||
remoteRoot: remoteRoot,
|
||||
env: env,
|
||||
locker: newLocker,
|
||||
wsc: wsc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *Deployer) DefaultTerraformRoot() string {
|
||||
return filepath.Join(b.localRoot, ".databricks/bundle", b.env)
|
||||
}
|
||||
|
||||
func (b *Deployer) tfStateRemotePath() string {
|
||||
// Note: remote paths are scoped to `remoteRoot` through the locker. Also see [Create].
|
||||
return ".bundle/terraform.tfstate"
|
||||
}
|
||||
|
||||
func (b *Deployer) tfStateLocalPath() string {
|
||||
return filepath.Join(b.DefaultTerraformRoot(), "terraform.tfstate")
|
||||
}
|
||||
|
||||
func (d *Deployer) LoadTerraformState(ctx context.Context) error {
|
||||
r, err := d.locker.Read(ctx, d.tfStateRemotePath())
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
// If remote tf state is absent, use local tf state
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
err = os.MkdirAll(d.DefaultTerraformRoot(), os.ModeDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(d.tfStateLocalPath(), b, os.ModePerm)
|
||||
}
|
||||
|
||||
func (b *Deployer) SaveTerraformState(ctx context.Context) error {
|
||||
bytes, err := os.ReadFile(b.tfStateLocalPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.locker.Write(ctx, b.tfStateRemotePath(), bytes)
|
||||
}
|
||||
|
||||
func (d *Deployer) Lock(ctx context.Context, isForced bool) error {
|
||||
return d.locker.Lock(ctx, isForced)
|
||||
}
|
||||
|
||||
func (d *Deployer) Unlock(ctx context.Context) error {
|
||||
return d.locker.Unlock(ctx)
|
||||
}
|
||||
|
||||
func (d *Deployer) ApplyTerraformConfig(ctx context.Context, configPath, terraformBinaryPath string, isForced bool) (DeploymentStatus, error) {
|
||||
applyErr := d.Lock(ctx, isForced)
|
||||
if applyErr != nil {
|
||||
return Failed, applyErr
|
||||
}
|
||||
defer func() {
|
||||
applyErr = d.Unlock(ctx)
|
||||
if applyErr != nil {
|
||||
log.Errorf(ctx, "failed to unlock deployment mutex: %s", applyErr)
|
||||
}
|
||||
}()
|
||||
|
||||
applyErr = d.LoadTerraformState(ctx)
|
||||
if applyErr != nil {
|
||||
log.Debugf(ctx, "failed to load terraform state from workspace: %s", applyErr)
|
||||
return Failed, applyErr
|
||||
}
|
||||
|
||||
tf, applyErr := tfexec.NewTerraform(configPath, terraformBinaryPath)
|
||||
if applyErr != nil {
|
||||
log.Debugf(ctx, "failed to construct terraform object: %s", applyErr)
|
||||
return Failed, applyErr
|
||||
}
|
||||
|
||||
isPlanNotEmpty, applyErr := tf.Plan(ctx)
|
||||
if applyErr != nil {
|
||||
log.Debugf(ctx, "failed to compute terraform plan: %s", applyErr)
|
||||
return Failed, applyErr
|
||||
}
|
||||
|
||||
if !isPlanNotEmpty {
|
||||
log.Debugf(ctx, "terraform plan returned a empty diff")
|
||||
return NoChanges, nil
|
||||
}
|
||||
|
||||
applyErr = tf.Apply(ctx)
|
||||
// upload state even if apply fails to handle partial deployments
|
||||
saveStateErr := d.SaveTerraformState(ctx)
|
||||
|
||||
if applyErr != nil && saveStateErr != nil {
|
||||
log.Errorf(ctx, "terraform apply failed: %s", applyErr)
|
||||
log.Errorf(ctx, "failed to upload terraform state after partial terraform apply: %s", saveStateErr)
|
||||
return PartialButUntracked, fmt.Errorf("deploymented failed: %s", applyErr)
|
||||
}
|
||||
if applyErr != nil {
|
||||
log.Errorf(ctx, "terraform apply failed: %s", applyErr)
|
||||
return Partial, fmt.Errorf("deploymented failed: %s", applyErr)
|
||||
}
|
||||
if saveStateErr != nil {
|
||||
log.Errorf(ctx, "failed to upload terraform state after completing terraform apply: %s", saveStateErr)
|
||||
return CompleteButUntracked, fmt.Errorf("failed to upload terraform state file: %s", saveStateErr)
|
||||
}
|
||||
return Complete, nil
|
||||
}
|
Loading…
Reference in New Issue