github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/command/meta_new.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  
    10  	"github.com/hashicorp/errwrap"
    11  	"github.com/hashicorp/terraform/config"
    12  	"github.com/hashicorp/terraform/config/module"
    13  	"github.com/hashicorp/terraform/terraform"
    14  	"github.com/hashicorp/terraform/tfdiags"
    15  )
    16  
    17  // NOTE: Temporary file until this branch is cleaned up.
    18  
    19  // Input returns whether or not input asking is enabled.
    20  func (m *Meta) Input() bool {
    21  	if test || !m.input {
    22  		return false
    23  	}
    24  
    25  	if envVar := os.Getenv(InputModeEnvVar); envVar != "" {
    26  		if v, err := strconv.ParseBool(envVar); err == nil && !v {
    27  			return false
    28  		}
    29  	}
    30  
    31  	return true
    32  }
    33  
    34  // Module loads the module tree for the given root path.
    35  //
    36  // It expects the modules to already be downloaded. This will never
    37  // download any modules.
    38  //
    39  // The configuration is validated before returning, so the returned diagnostics
    40  // may contain warnings and/or errors. If the diagnostics contains only
    41  // warnings, the caller may treat the returned module.Tree as valid after
    42  // presenting the warnings to the user.
    43  func (m *Meta) Module(path string) (*module.Tree, tfdiags.Diagnostics) {
    44  	var diags tfdiags.Diagnostics
    45  
    46  	mod, err := module.NewTreeModule("", path)
    47  	if err != nil {
    48  		// Check for the error where we have no config files
    49  		if errwrap.ContainsType(err, new(config.ErrNoConfigsFound)) {
    50  			return nil, nil
    51  		}
    52  
    53  		diags = diags.Append(err)
    54  		return nil, diags
    55  	}
    56  
    57  	err = mod.Load(m.moduleStorage(m.DataDir(), module.GetModeNone))
    58  	if err != nil {
    59  		diags = diags.Append(errwrap.Wrapf("Error loading modules: {{err}}", err))
    60  		return nil, diags
    61  	}
    62  
    63  	diags = diags.Append(mod.Validate())
    64  
    65  	return mod, diags
    66  }
    67  
    68  // Config loads the root config for the path specified. Path may be a directory
    69  // or file. The absence of configuration is not an error and returns a nil Config.
    70  func (m *Meta) Config(path string) (*config.Config, error) {
    71  	// If no explicit path was given then it is okay for there to be
    72  	// no backend configuration found.
    73  	emptyOk := path == ""
    74  
    75  	// If we had no path set, it is an error. We can't initialize unset
    76  	if path == "" {
    77  		path = "."
    78  	}
    79  
    80  	// Expand the path
    81  	if !filepath.IsAbs(path) {
    82  		var err error
    83  		path, err = filepath.Abs(path)
    84  		if err != nil {
    85  			return nil, fmt.Errorf(
    86  				"Error expanding path to backend config %q: %s", path, err)
    87  		}
    88  	}
    89  
    90  	log.Printf("[DEBUG] command: loading backend config file: %s", path)
    91  
    92  	// We first need to determine if we're loading a file or a directory.
    93  	fi, err := os.Stat(path)
    94  	if err != nil {
    95  		if os.IsNotExist(err) && emptyOk {
    96  			log.Printf(
    97  				"[INFO] command: backend config not found, returning nil: %s",
    98  				path)
    99  			return nil, nil
   100  		}
   101  
   102  		return nil, err
   103  	}
   104  
   105  	var f func(string) (*config.Config, error) = config.LoadFile
   106  	if fi.IsDir() {
   107  		f = config.LoadDir
   108  	}
   109  
   110  	// Load the configuration
   111  	c, err := f(path)
   112  	if err != nil {
   113  		// Check for the error where we have no config files and return nil
   114  		// as the configuration type.
   115  		if errwrap.ContainsType(err, new(config.ErrNoConfigsFound)) {
   116  			log.Printf(
   117  				"[INFO] command: backend config not found, returning nil: %s",
   118  				path)
   119  			return nil, nil
   120  		}
   121  
   122  		return nil, err
   123  	}
   124  
   125  	return c, nil
   126  }
   127  
   128  // Plan returns the plan for the given path.
   129  //
   130  // This only has an effect if the path itself looks like a plan.
   131  // If error is nil and the plan is nil, then the path didn't look like
   132  // a plan.
   133  //
   134  // Error will be non-nil if path looks like a plan and loading the plan
   135  // failed.
   136  func (m *Meta) Plan(path string) (*terraform.Plan, error) {
   137  	// Open the path no matter if its a directory or file
   138  	f, err := os.Open(path)
   139  	defer f.Close()
   140  	if err != nil {
   141  		return nil, fmt.Errorf(
   142  			"Failed to load Terraform configuration or plan: %s", err)
   143  	}
   144  
   145  	// Stat it so we can check if its a directory
   146  	fi, err := f.Stat()
   147  	if err != nil {
   148  		return nil, fmt.Errorf(
   149  			"Failed to load Terraform configuration or plan: %s", err)
   150  	}
   151  
   152  	// If this path is a directory, then it can't be a plan. Not an error.
   153  	if fi.IsDir() {
   154  		return nil, nil
   155  	}
   156  
   157  	// Read the plan
   158  	p, err := terraform.ReadPlan(f)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	// We do a validation here that seems odd but if any plan is given,
   164  	// we must not have set any extra variables. The plan itself contains
   165  	// the variables and those aren't overwritten.
   166  	if len(m.variables) > 0 {
   167  		return nil, fmt.Errorf(
   168  			"You can't set variables with the '-var' or '-var-file' flag\n" +
   169  				"when you're applying a plan file. The variables used when\n" +
   170  				"the plan was created will be used. If you wish to use different\n" +
   171  				"variable values, create a new plan file.")
   172  	}
   173  
   174  	return p, nil
   175  }