2023-04-06 10:54:58 +00:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/databricks/bricks/bundle"
|
|
|
|
"github.com/databricks/bricks/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()
|
|
|
|
}
|
|
|
|
|
2023-04-18 12:20:35 +00:00
|
|
|
func (c *PlanResourceChange) IsInplaceSupported() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-04-18 14:55:06 +00:00
|
|
|
func logDestroyPlan(ctx context.Context, changes []*tfjson.ResourceChange) error {
|
|
|
|
cmdio.LogString(ctx, "The following resources will be removed:")
|
2023-04-06 10:54:58 +00:00
|
|
|
for _, c := range changes {
|
|
|
|
if c.Change.Actions.Delete() {
|
2023-04-18 14:55:06 +00:00
|
|
|
cmdio.Log(ctx, &PlanResourceChange{
|
2023-04-06 10:54:58 +00:00
|
|
|
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) ([]bundle.Mutator, error) {
|
2023-04-18 14:55:06 +00:00
|
|
|
// return early if plan is empty
|
2023-04-06 10:54:58 +00:00
|
|
|
if b.Plan.IsEmpty {
|
2023-04-18 14:55:06 +00:00
|
|
|
cmdio.LogString(ctx, "No resources to destroy in plan. Skipping destroy!")
|
2023-04-06 10:54:58 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
tf := b.Terraform
|
|
|
|
if tf == nil {
|
|
|
|
return nil, fmt.Errorf("terraform not initialized")
|
|
|
|
}
|
|
|
|
|
|
|
|
// read plan file
|
|
|
|
plan, err := tf.ShowPlanFile(ctx, b.Plan.Path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// print the resources that will be destroyed
|
2023-04-18 14:55:06 +00:00
|
|
|
err = logDestroyPlan(ctx, plan.ResourceChanges)
|
2023-04-06 10:54:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ask for confirmation, if needed
|
|
|
|
if !b.Plan.ConfirmApply {
|
|
|
|
red := color.New(color.FgRed).SprintFunc()
|
2023-04-18 14:55:06 +00:00
|
|
|
b.Plan.ConfirmApply, err = cmdio.Ask(ctx, fmt.Sprintf("\nThis will permanently %s resources! Proceed? [y/n]: ", red("destroy")))
|
2023-04-06 10:54:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// return if confirmation was not provided
|
|
|
|
if !b.Plan.ConfirmApply {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.Plan.Path == "" {
|
|
|
|
return nil, fmt.Errorf("no plan found")
|
|
|
|
}
|
|
|
|
|
2023-04-18 14:55:06 +00:00
|
|
|
cmdio.LogString(ctx, "Starting to destroy resources")
|
|
|
|
|
2023-04-06 10:54:58 +00:00
|
|
|
// Apply terraform according to the computed destroy plan
|
|
|
|
err = tf.Apply(ctx, tfexec.DirOrPlan(b.Plan.Path))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("terraform destroy: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-04-18 14:55:06 +00:00
|
|
|
cmdio.LogString(ctx, "Successfully destroyed resources!")
|
2023-04-06 10:54:58 +00:00
|
|
|
return nil, 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{}
|
|
|
|
}
|