github.com/alexissmirnov/terraform@v0.4.3-0.20150423153700-1ef9731a2f14/command/plan.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/terraform/terraform"
    10  )
    11  
    12  // PlanCommand is a Command implementation that compares a Terraform
    13  // configuration to an actual infrastructure and shows the differences.
    14  type PlanCommand struct {
    15  	Meta
    16  }
    17  
    18  func (c *PlanCommand) Run(args []string) int {
    19  	var destroy, refresh, detailed bool
    20  	var outPath string
    21  	var moduleDepth int
    22  
    23  	args = c.Meta.process(args, true)
    24  
    25  	cmdFlags := c.Meta.flagSet("plan")
    26  	cmdFlags.BoolVar(&destroy, "destroy", false, "destroy")
    27  	cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
    28  	cmdFlags.IntVar(&moduleDepth, "module-depth", 0, "module-depth")
    29  	cmdFlags.StringVar(&outPath, "out", "", "path")
    30  	cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
    31  	cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
    32  	cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode")
    33  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    34  	if err := cmdFlags.Parse(args); err != nil {
    35  		return 1
    36  	}
    37  
    38  	var path string
    39  	args = cmdFlags.Args()
    40  	if len(args) > 1 {
    41  		c.Ui.Error(
    42  			"The plan command expects at most one argument with the path\n" +
    43  				"to a Terraform configuration.\n")
    44  		cmdFlags.Usage()
    45  		return 1
    46  	} else if len(args) == 1 {
    47  		path = args[0]
    48  	} else {
    49  		var err error
    50  		path, err = os.Getwd()
    51  		if err != nil {
    52  			c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err))
    53  		}
    54  	}
    55  
    56  	ctx, _, err := c.Context(contextOpts{
    57  		Destroy:   destroy,
    58  		Path:      path,
    59  		StatePath: c.Meta.statePath,
    60  	})
    61  	if err != nil {
    62  		c.Ui.Error(err.Error())
    63  		return 1
    64  	}
    65  	if !validateContext(ctx, c.Ui) {
    66  		return 1
    67  	}
    68  	if err := ctx.Input(c.InputMode()); err != nil {
    69  		c.Ui.Error(fmt.Sprintf("Error configuring: %s", err))
    70  		return 1
    71  	}
    72  
    73  	if refresh {
    74  		c.Ui.Output("Refreshing Terraform state prior to plan...\n")
    75  		state, err := ctx.Refresh()
    76  		if err != nil {
    77  			c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err))
    78  			return 1
    79  		}
    80  		c.Ui.Output("")
    81  
    82  		if state != nil {
    83  			log.Printf("[INFO] Writing state output to: %s", c.Meta.StateOutPath())
    84  			if err := c.Meta.PersistState(state); err != nil {
    85  				c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err))
    86  				return 1
    87  			}
    88  		}
    89  	}
    90  
    91  	plan, err := ctx.Plan()
    92  	if err != nil {
    93  		c.Ui.Error(fmt.Sprintf("Error running plan: %s", err))
    94  		return 1
    95  	}
    96  
    97  	if plan.Diff.Empty() {
    98  		c.Ui.Output(
    99  			"No changes. Infrastructure is up-to-date. This means that Terraform\n" +
   100  				"could not detect any differences between your configuration and\n" +
   101  				"the real physical resources that exist. As a result, Terraform\n" +
   102  				"doesn't need to do anything.")
   103  		return 0
   104  	}
   105  
   106  	if outPath != "" {
   107  		log.Printf("[INFO] Writing plan output to: %s", outPath)
   108  		f, err := os.Create(outPath)
   109  		if err == nil {
   110  			defer f.Close()
   111  			err = terraform.WritePlan(plan, f)
   112  		}
   113  		if err != nil {
   114  			c.Ui.Error(fmt.Sprintf("Error writing plan file: %s", err))
   115  			return 1
   116  		}
   117  	}
   118  
   119  	if outPath == "" {
   120  		c.Ui.Output(strings.TrimSpace(planHeaderNoOutput) + "\n")
   121  	} else {
   122  		c.Ui.Output(fmt.Sprintf(
   123  			strings.TrimSpace(planHeaderYesOutput)+"\n",
   124  			outPath))
   125  	}
   126  
   127  	c.Ui.Output(FormatPlan(&FormatPlanOpts{
   128  		Plan:        plan,
   129  		Color:       c.Colorize(),
   130  		ModuleDepth: moduleDepth,
   131  	}))
   132  
   133  	if detailed {
   134  		return 2
   135  	}
   136  	return 0
   137  }
   138  
   139  func (c *PlanCommand) Help() string {
   140  	helpText := `
   141  Usage: terraform plan [options] [dir]
   142  
   143    Generates an execution plan for Terraform.
   144  
   145    This execution plan can be reviewed prior to running apply to get a
   146    sense for what Terraform will do. Optionally, the plan can be saved to
   147    a Terraform plan file, and apply can take this plan file to execute
   148    this plan exactly.
   149  
   150  Options:
   151  
   152    -backup=path        Path to backup the existing state file before
   153                        modifying. Defaults to the "-state-out" path with
   154                        ".backup" extension. Set to "-" to disable backup.
   155  
   156    -destroy            If set, a plan will be generated to destroy all resources
   157                        managed by the given configuration and state.
   158  
   159    -detailed-exitcode  Return detailed exit codes when the command exits. This
   160                        will change the meaning of exit codes to:
   161                        0 - Succeeded, diff is empty (no changes)
   162                        1 - Errored
   163                        2 - Succeeded, there is a diff
   164  
   165    -input=true         Ask for input for variables if not directly set.
   166  
   167    -module-depth=n     Specifies the depth of modules to show in the output.
   168                        This does not affect the plan itself, only the output
   169                        shown. By default, this is zero. -1 will expand all.
   170  
   171    -no-color           If specified, output won't contain any color.
   172  
   173    -out=path           Write a plan file to the given path. This can be used as
   174                        input to the "apply" command.
   175  
   176    -refresh=true       Update state prior to checking for differences.
   177  
   178    -state=statefile    Path to a Terraform state file to use to look
   179                        up Terraform-managed resources. By default it will
   180                        use the state "terraform.tfstate" if it exists.
   181  
   182    -target=resource    Resource to target. Operation will be limited to this
   183                        resource and its dependencies. This flag can be used
   184                        multiple times.
   185  
   186    -var 'foo=bar'      Set a variable in the Terraform configuration. This
   187                        flag can be set multiple times.
   188  
   189    -var-file=foo       Set variables in the Terraform configuration from
   190                        a file. If "terraform.tfvars" is present, it will be
   191                        automatically loaded if this flag is not specified.
   192  
   193  `
   194  	return strings.TrimSpace(helpText)
   195  }
   196  
   197  func (c *PlanCommand) Synopsis() string {
   198  	return "Generate and show an execution plan"
   199  }
   200  
   201  const planHeaderNoOutput = `
   202  The Terraform execution plan has been generated and is shown below.
   203  Resources are shown in alphabetical order for quick scanning. Green resources
   204  will be created (or destroyed and then created if an existing resource
   205  exists), yellow resources are being changed in-place, and red resources
   206  will be destroyed.
   207  
   208  Note: You didn't specify an "-out" parameter to save this plan, so when
   209  "apply" is called, Terraform can't guarantee this is what will execute.
   210  `
   211  
   212  const planHeaderYesOutput = `
   213  The Terraform execution plan has been generated and is shown below.
   214  Resources are shown in alphabetical order for quick scanning. Green resources
   215  will be created (or destroyed and then created if an existing resource
   216  exists), yellow resources are being changed in-place, and red resources
   217  will be destroyed.
   218  
   219  Your plan was also saved to the path below. Call the "apply" subcommand
   220  with this plan file and Terraform will exactly execute this execution
   221  plan.
   222  
   223  Path: %s
   224  `