github.com/mkuzmin/terraform@v0.3.7-0.20161118171027-ec4c00ff92a9/command/init.go (about)

     1  package command
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/go-getter"
    11  	"github.com/hashicorp/terraform/config"
    12  	"github.com/hashicorp/terraform/config/module"
    13  	"github.com/hashicorp/terraform/terraform"
    14  )
    15  
    16  // InitCommand is a Command implementation that takes a Terraform
    17  // module and clones it to the working directory.
    18  type InitCommand struct {
    19  	Meta
    20  }
    21  
    22  func (c *InitCommand) Run(args []string) int {
    23  	var remoteBackend string
    24  	args = c.Meta.process(args, false)
    25  	remoteConfig := make(map[string]string)
    26  	cmdFlags := flag.NewFlagSet("init", flag.ContinueOnError)
    27  	cmdFlags.StringVar(&remoteBackend, "backend", "", "")
    28  	cmdFlags.Var((*FlagStringKV)(&remoteConfig), "backend-config", "config")
    29  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    30  	if err := cmdFlags.Parse(args); err != nil {
    31  		return 1
    32  	}
    33  
    34  	remoteBackend = strings.ToLower(remoteBackend)
    35  
    36  	var path string
    37  	args = cmdFlags.Args()
    38  	if len(args) > 2 {
    39  		c.Ui.Error("The init command expects at most two arguments.\n")
    40  		cmdFlags.Usage()
    41  		return 1
    42  	} else if len(args) < 1 {
    43  		c.Ui.Error("The init command expects at least one arguments.\n")
    44  		cmdFlags.Usage()
    45  		return 1
    46  	}
    47  
    48  	if len(args) == 2 {
    49  		path = args[1]
    50  	} else {
    51  		var err error
    52  		path, err = os.Getwd()
    53  		if err != nil {
    54  			c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err))
    55  		}
    56  	}
    57  
    58  	// Set the state out path to be the path requested for the module
    59  	// to be copied. This ensures any remote states gets setup in the
    60  	// proper directory.
    61  	c.Meta.dataDir = filepath.Join(path, DefaultDataDir)
    62  
    63  	source := args[0]
    64  
    65  	// Get our pwd since we need it
    66  	pwd, err := os.Getwd()
    67  	if err != nil {
    68  		c.Ui.Error(fmt.Sprintf(
    69  			"Error reading working directory: %s", err))
    70  		return 1
    71  	}
    72  
    73  	// Verify the directory is empty
    74  	if empty, err := config.IsEmptyDir(path); err != nil {
    75  		c.Ui.Error(fmt.Sprintf(
    76  			"Error checking on destination path: %s", err))
    77  		return 1
    78  	} else if !empty {
    79  		c.Ui.Error(
    80  			"The destination path has Terraform configuration files. The\n" +
    81  				"init command can only be used on a directory without existing Terraform\n" +
    82  				"files.")
    83  		return 1
    84  	}
    85  
    86  	// Detect
    87  	source, err = getter.Detect(source, pwd, getter.Detectors)
    88  	if err != nil {
    89  		c.Ui.Error(fmt.Sprintf(
    90  			"Error with module source: %s", err))
    91  		return 1
    92  	}
    93  
    94  	// Get it!
    95  	if err := module.GetCopy(path, source); err != nil {
    96  		c.Ui.Error(err.Error())
    97  		return 1
    98  	}
    99  
   100  	// Handle remote state if configured
   101  	if remoteBackend != "" {
   102  		var remoteConf terraform.RemoteState
   103  		remoteConf.Type = remoteBackend
   104  		remoteConf.Config = remoteConfig
   105  
   106  		state, err := c.State()
   107  		if err != nil {
   108  			c.Ui.Error(fmt.Sprintf("Error checking for state: %s", err))
   109  			return 1
   110  		}
   111  		if state != nil {
   112  			s := state.State()
   113  			if !s.Empty() {
   114  				c.Ui.Error(fmt.Sprintf(
   115  					"State file already exists and is not empty! Please remove this\n" +
   116  						"state file before initializing. Note that removing the state file\n" +
   117  						"may result in a loss of information since Terraform uses this\n" +
   118  						"to track your infrastructure."))
   119  				return 1
   120  			}
   121  			if s.IsRemote() {
   122  				c.Ui.Error(fmt.Sprintf(
   123  					"State file already exists with remote state enabled! Please remove this\n" +
   124  						"state file before initializing. Note that removing the state file\n" +
   125  						"may result in a loss of information since Terraform uses this\n" +
   126  						"to track your infrastructure."))
   127  				return 1
   128  			}
   129  		}
   130  
   131  		// Initialize a blank state file with remote enabled
   132  		remoteCmd := &RemoteConfigCommand{
   133  			Meta:       c.Meta,
   134  			remoteConf: &remoteConf,
   135  		}
   136  		return remoteCmd.initBlankState()
   137  	}
   138  	return 0
   139  }
   140  
   141  func (c *InitCommand) Help() string {
   142  	helpText := `
   143  Usage: terraform init [options] SOURCE [PATH]
   144  
   145    Downloads the module given by SOURCE into the PATH. The PATH defaults
   146    to the working directory. PATH must be empty of any Terraform files.
   147    Any conflicting non-Terraform files will be overwritten.
   148  
   149    The module downloaded is a copy. If you're downloading a module from
   150    Git, it will not preserve the Git history, it will only copy the
   151    latest files.
   152  
   153  Options:
   154  
   155    -backend=atlas         Specifies the type of remote backend. If not
   156                           specified, local storage will be used.
   157  
   158    -backend-config="k=v"  Specifies configuration for the remote storage
   159                           backend. This can be specified multiple times.
   160  
   161    -no-color           If specified, output won't contain any color.
   162  
   163  `
   164  	return strings.TrimSpace(helpText)
   165  }
   166  
   167  func (c *InitCommand) Synopsis() string {
   168  	return "Initializes Terraform configuration from a module"
   169  }