mirror of https://github.com/databricks/cli.git
Use apply mutator for bundle destroy
This commit is contained in:
parent
cd89e5b1ac
commit
42e4f283b5
|
@ -9,7 +9,16 @@ import (
|
||||||
"github.com/hashicorp/terraform-exec/tfexec"
|
"github.com/hashicorp/terraform-exec/tfexec"
|
||||||
)
|
)
|
||||||
|
|
||||||
type apply struct{}
|
type ApplyGoal string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ApplyGoalDeploy = "deploy"
|
||||||
|
ApplyGoalDestroy = "destroy"
|
||||||
|
)
|
||||||
|
|
||||||
|
type apply struct {
|
||||||
|
goal ApplyGoal
|
||||||
|
}
|
||||||
|
|
||||||
func (w *apply) Name() string {
|
func (w *apply) Name() string {
|
||||||
return "terraform.Apply"
|
return "terraform.Apply"
|
||||||
|
@ -55,7 +64,12 @@ func (w *apply) Apply(ctx context.Context, b *bundle.Bundle) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdio.LogString(ctx, "Starting deployment")
|
switch w.goal {
|
||||||
|
case ApplyGoalDeploy:
|
||||||
|
cmdio.LogString(ctx, "Starting deployment")
|
||||||
|
case ApplyGoalDestroy:
|
||||||
|
cmdio.LogString(ctx, "Starting destruction")
|
||||||
|
}
|
||||||
|
|
||||||
// Apply terraform according to the plan
|
// Apply terraform according to the plan
|
||||||
err = tf.Apply(ctx, tfexec.DirOrPlan(b.Plan.Path))
|
err = tf.Apply(ctx, tfexec.DirOrPlan(b.Plan.Path))
|
||||||
|
@ -63,12 +77,18 @@ func (w *apply) Apply(ctx context.Context, b *bundle.Bundle) error {
|
||||||
return fmt.Errorf("terraform apply: %w", err)
|
return fmt.Errorf("terraform apply: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdio.LogString(ctx, "Resource deployment completed!")
|
switch w.goal {
|
||||||
|
case ApplyGoalDeploy:
|
||||||
|
cmdio.LogString(ctx, "Deployment Successful!")
|
||||||
|
case ApplyGoalDestroy:
|
||||||
|
cmdio.LogString(ctx, "Destruction Successful!")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply returns a [bundle.Mutator] that runs the equivalent of `terraform apply`
|
// Apply returns a [bundle.Mutator] that runs the equivalent of `terraform apply ./plan`
|
||||||
// from the bundle's ephemeral working directory for Terraform.
|
// from the bundle's ephemeral working directory for Terraform.
|
||||||
func Apply() bundle.Mutator {
|
func Apply(goal ApplyGoal) bundle.Mutator {
|
||||||
return &apply{}
|
return &apply{goal}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/databricks/cli/bundle"
|
|
||||||
"github.com/databricks/cli/libs/cmdio"
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/hashicorp/terraform-exec/tfexec"
|
|
||||||
tfjson "github.com/hashicorp/terraform-json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PlanResourceChange struct {
|
|
||||||
ResourceType string `json:"resource_type"`
|
|
||||||
Action string `json:"action"`
|
|
||||||
ResourceName string `json:"resource_name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PlanResourceChange) String() string {
|
|
||||||
result := strings.Builder{}
|
|
||||||
switch c.Action {
|
|
||||||
case "delete":
|
|
||||||
result.WriteString(" delete ")
|
|
||||||
default:
|
|
||||||
result.WriteString(c.Action + " ")
|
|
||||||
}
|
|
||||||
switch c.ResourceType {
|
|
||||||
case "databricks_job":
|
|
||||||
result.WriteString("job ")
|
|
||||||
case "databricks_pipeline":
|
|
||||||
result.WriteString("pipeline ")
|
|
||||||
default:
|
|
||||||
result.WriteString(c.ResourceType + " ")
|
|
||||||
}
|
|
||||||
result.WriteString(c.ResourceName)
|
|
||||||
return result.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PlanResourceChange) IsInplaceSupported() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func logDestroyPlan(ctx context.Context, changes []*tfjson.ResourceChange) error {
|
|
||||||
cmdio.LogString(ctx, "The following resources will be removed:")
|
|
||||||
for _, c := range changes {
|
|
||||||
if c.Change.Actions.Delete() {
|
|
||||||
cmdio.Log(ctx, &PlanResourceChange{
|
|
||||||
ResourceType: c.Type,
|
|
||||||
Action: "delete",
|
|
||||||
ResourceName: c.Name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type destroy struct{}
|
|
||||||
|
|
||||||
func (w *destroy) Name() string {
|
|
||||||
return "terraform.Destroy"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *destroy) Apply(ctx context.Context, b *bundle.Bundle) error {
|
|
||||||
// return early if plan is empty
|
|
||||||
if b.Plan.IsEmpty {
|
|
||||||
cmdio.LogString(ctx, "No resources to destroy in plan. Skipping destroy!")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tf := b.Terraform
|
|
||||||
if tf == nil {
|
|
||||||
return fmt.Errorf("terraform not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
// read plan file
|
|
||||||
plan, err := tf.ShowPlanFile(ctx, b.Plan.Path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// print the resources that will be destroyed
|
|
||||||
err = logDestroyPlan(ctx, plan.ResourceChanges)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask for confirmation, if needed
|
|
||||||
if !b.Plan.ConfirmApply {
|
|
||||||
red := color.New(color.FgRed).SprintFunc()
|
|
||||||
b.Plan.ConfirmApply, err = cmdio.Ask(ctx, fmt.Sprintf("\nThis will permanently %s resources! Proceed? [y/n]: ", red("destroy")))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return if confirmation was not provided
|
|
||||||
if !b.Plan.ConfirmApply {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.Plan.Path == "" {
|
|
||||||
return fmt.Errorf("no plan found")
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdio.LogString(ctx, "Starting to destroy resources")
|
|
||||||
|
|
||||||
// Apply terraform according to the computed destroy plan
|
|
||||||
err = tf.Apply(ctx, tfexec.DirOrPlan(b.Plan.Path))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("terraform destroy: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdio.LogString(ctx, "Successfully destroyed resources!")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy returns a [bundle.Mutator] that runs the conceptual equivalent of
|
|
||||||
// `terraform destroy ./plan` from the bundle's ephemeral working directory for Terraform.
|
|
||||||
func Destroy() bundle.Mutator {
|
|
||||||
return &destroy{}
|
|
||||||
}
|
|
|
@ -44,8 +44,8 @@ func logPlan(ctx context.Context, plan *tfjson.Plan) error {
|
||||||
type PlanGoal string
|
type PlanGoal string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
PlanDeploy = PlanGoal("deploy")
|
PlanGoalDeploy = PlanGoal("deploy")
|
||||||
PlanDestroy = PlanGoal("destroy")
|
PlanGoalDestroy = PlanGoal("destroy")
|
||||||
)
|
)
|
||||||
|
|
||||||
type plan struct {
|
type plan struct {
|
||||||
|
@ -75,7 +75,7 @@ func (p *plan) Apply(ctx context.Context, b *bundle.Bundle) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
planPath := filepath.Join(tfDir, "plan")
|
planPath := filepath.Join(tfDir, "plan")
|
||||||
destroy := p.goal == PlanDestroy
|
destroy := p.goal == PlanGoalDestroy
|
||||||
|
|
||||||
notEmpty, err := tf.Plan(ctx, tfexec.Destroy(destroy), tfexec.Out(planPath))
|
notEmpty, err := tf.Plan(ctx, tfexec.Destroy(destroy), tfexec.Out(planPath))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -19,8 +19,8 @@ func Deploy() bundle.Mutator {
|
||||||
terraform.Interpolate(),
|
terraform.Interpolate(),
|
||||||
terraform.Write(),
|
terraform.Write(),
|
||||||
terraform.StatePull(),
|
terraform.StatePull(),
|
||||||
terraform.Plan(terraform.PlanGoal("deploy")),
|
terraform.Plan(terraform.PlanGoalDeploy),
|
||||||
terraform.Apply(),
|
terraform.Apply(terraform.ApplyGoalDeploy),
|
||||||
terraform.StatePush(),
|
terraform.StatePush(),
|
||||||
),
|
),
|
||||||
lock.Release(lock.GoalDeploy),
|
lock.Release(lock.GoalDeploy),
|
||||||
|
|
|
@ -15,8 +15,8 @@ func Destroy() bundle.Mutator {
|
||||||
bundle.Defer(
|
bundle.Defer(
|
||||||
bundle.Seq(
|
bundle.Seq(
|
||||||
terraform.StatePull(),
|
terraform.StatePull(),
|
||||||
terraform.Plan(terraform.PlanGoal("destroy")),
|
terraform.Plan(terraform.PlanGoalDestroy),
|
||||||
terraform.Destroy(),
|
terraform.Apply(terraform.ApplyGoalDestroy),
|
||||||
terraform.StatePush(),
|
terraform.StatePush(),
|
||||||
files.Delete(),
|
files.Delete(),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue