github.com/pbthorste/terraform@v0.8.6-0.20170127005045-deb56bd93da2/backend/local/backend_plan.go (about)

     1  package local
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/errwrap"
    11  	"github.com/hashicorp/terraform/backend"
    12  	"github.com/hashicorp/terraform/command/format"
    13  	"github.com/hashicorp/terraform/terraform"
    14  )
    15  
    16  func (b *Local) opPlan(
    17  	ctx context.Context,
    18  	op *backend.Operation,
    19  	runningOp *backend.RunningOperation) {
    20  	log.Printf("[INFO] backend/local: starting Plan operation")
    21  
    22  	if b.CLI != nil && op.Plan != nil {
    23  		b.CLI.Output(b.Colorize().Color(
    24  			"[reset][bold][yellow]" +
    25  				"The plan command received a saved plan file as input. This command\n" +
    26  				"will output the saved plan. This will not modify the already-existing\n" +
    27  				"plan. If you wish to generate a new plan, please pass in a configuration\n" +
    28  				"directory as an argument.\n\n"))
    29  	}
    30  
    31  	// Setup our count hook that keeps track of resource changes
    32  	countHook := new(CountHook)
    33  	if b.ContextOpts == nil {
    34  		b.ContextOpts = new(terraform.ContextOpts)
    35  	}
    36  	old := b.ContextOpts.Hooks
    37  	defer func() { b.ContextOpts.Hooks = old }()
    38  	b.ContextOpts.Hooks = append(b.ContextOpts.Hooks, countHook)
    39  
    40  	// Get our context
    41  	tfCtx, _, err := b.context(op)
    42  	if err != nil {
    43  		runningOp.Err = err
    44  		return
    45  	}
    46  
    47  	// Setup the state
    48  	runningOp.State = tfCtx.State()
    49  
    50  	// If we're refreshing before plan, perform that
    51  	if op.PlanRefresh {
    52  		log.Printf("[INFO] backend/local: plan calling Refresh")
    53  
    54  		if b.CLI != nil {
    55  			b.CLI.Output(b.Colorize().Color(strings.TrimSpace(planRefreshing) + "\n"))
    56  		}
    57  
    58  		_, err := tfCtx.Refresh()
    59  		if err != nil {
    60  			runningOp.Err = errwrap.Wrapf("Error refreshing state: {{err}}", err)
    61  			return
    62  		}
    63  	}
    64  
    65  	// Perform the plan
    66  	log.Printf("[INFO] backend/local: plan calling Plan")
    67  	plan, err := tfCtx.Plan()
    68  	if err != nil {
    69  		runningOp.Err = errwrap.Wrapf("Error running plan: {{err}}", err)
    70  		return
    71  	}
    72  
    73  	// Record state
    74  	runningOp.PlanEmpty = plan.Diff.Empty()
    75  
    76  	// Save the plan to disk
    77  	if path := op.PlanOutPath; path != "" {
    78  		// Write the backend if we have one
    79  		plan.Backend = op.PlanOutBackend
    80  
    81  		log.Printf("[INFO] backend/local: writing plan output to: %s", path)
    82  		f, err := os.Create(path)
    83  		if err == nil {
    84  			err = terraform.WritePlan(plan, f)
    85  		}
    86  		f.Close()
    87  		if err != nil {
    88  			runningOp.Err = fmt.Errorf("Error writing plan file: %s", err)
    89  			return
    90  		}
    91  	}
    92  
    93  	// Perform some output tasks if we have a CLI to output to.
    94  	if b.CLI != nil {
    95  		if plan.Diff.Empty() {
    96  			b.CLI.Output(b.Colorize().Color(strings.TrimSpace(planNoChanges)))
    97  			return
    98  		}
    99  
   100  		if path := op.PlanOutPath; path == "" {
   101  			b.CLI.Output(strings.TrimSpace(planHeaderNoOutput) + "\n")
   102  		} else {
   103  			b.CLI.Output(fmt.Sprintf(
   104  				strings.TrimSpace(planHeaderYesOutput)+"\n",
   105  				path))
   106  		}
   107  
   108  		b.CLI.Output(format.Plan(&format.PlanOpts{
   109  			Plan:        plan,
   110  			Color:       b.Colorize(),
   111  			ModuleDepth: -1,
   112  		}))
   113  
   114  		b.CLI.Output(b.Colorize().Color(fmt.Sprintf(
   115  			"[reset][bold]Plan:[reset] "+
   116  				"%d to add, %d to change, %d to destroy.",
   117  			countHook.ToAdd+countHook.ToRemoveAndAdd,
   118  			countHook.ToChange,
   119  			countHook.ToRemove+countHook.ToRemoveAndAdd)))
   120  	}
   121  }
   122  
   123  const planHeaderNoOutput = `
   124  The Terraform execution plan has been generated and is shown below.
   125  Resources are shown in alphabetical order for quick scanning. Green resources
   126  will be created (or destroyed and then created if an existing resource
   127  exists), yellow resources are being changed in-place, and red resources
   128  will be destroyed. Cyan entries are data sources to be read.
   129  
   130  Note: You didn't specify an "-out" parameter to save this plan, so when
   131  "apply" is called, Terraform can't guarantee this is what will execute.
   132  `
   133  
   134  const planHeaderYesOutput = `
   135  The Terraform execution plan has been generated and is shown below.
   136  Resources are shown in alphabetical order for quick scanning. Green resources
   137  will be created (or destroyed and then created if an existing resource
   138  exists), yellow resources are being changed in-place, and red resources
   139  will be destroyed. Cyan entries are data sources to be read.
   140  
   141  Your plan was also saved to the path below. Call the "apply" subcommand
   142  with this plan file and Terraform will exactly execute this execution
   143  plan.
   144  
   145  Path: %s
   146  `
   147  
   148  const planNoChanges = `
   149  [reset][bold][green]No changes. Infrastructure is up-to-date.[reset][green]
   150  
   151  This means that Terraform could not detect any differences between your
   152  configuration and real physical resources that exist. As a result, Terraform
   153  doesn't need to do anything.
   154  `
   155  
   156  const planRefreshing = `
   157  [reset][bold]Refreshing Terraform state in-memory prior to plan...[reset]
   158  The refreshed state will be used to calculate this plan, but will not be
   159  persisted to local or remote state storage.
   160  `