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