|  | package tfexec | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "fmt" | 
|  | "io" | 
|  | "io/ioutil" | 
|  | "log" | 
|  | "os" | 
|  | "sync" | 
|  |  | 
|  | "github.com/hashicorp/go-version" | 
|  | ) | 
|  |  | 
|  | type printfer interface { | 
|  | Printf(format string, v ...interface{}) | 
|  | } | 
|  |  | 
|  | // Terraform represents the Terraform CLI executable and working directory. | 
|  | // | 
|  | // Typically this is constructed against the root module of a Terraform configuration | 
|  | // but you can override paths used in some commands depending on the available | 
|  | // options. | 
|  | // | 
|  | // All functions that execute CLI commands take a context.Context. It should be noted that | 
|  | // exec.Cmd.Run will not return context.DeadlineExceeded or context.Canceled by default, we | 
|  | // have augmented our wrapped errors to respond true to errors.Is for context.DeadlineExceeded | 
|  | // and context.Canceled if those are present on the context when the error is parsed. See | 
|  | // https://github.com/golang/go/issues/21880 for more about the Go limitations. | 
|  | // | 
|  | // By default, the instance inherits the environment from the calling code (using os.Environ) | 
|  | // but it ignores certain environment variables that are managed within the code and prohibits | 
|  | // setting them through SetEnv: | 
|  | // | 
|  | //  - TF_APPEND_USER_AGENT | 
|  | //  - TF_IN_AUTOMATION | 
|  | //  - TF_INPUT | 
|  | //  - TF_LOG | 
|  | //  - TF_LOG_PATH | 
|  | //  - TF_REATTACH_PROVIDERS | 
|  | //  - TF_DISABLE_PLUGIN_TLS | 
|  | //  - TF_SKIP_PROVIDER_VERIFY | 
|  | type Terraform struct { | 
|  | execPath           string | 
|  | workingDir         string | 
|  | appendUserAgent    string | 
|  | disablePluginTLS   bool | 
|  | skipProviderVerify bool | 
|  | env                map[string]string | 
|  |  | 
|  | stdout io.Writer | 
|  | stderr io.Writer | 
|  | logger printfer | 
|  |  | 
|  | // TF_LOG environment variable, defaults to TRACE if logPath is set. | 
|  | log string | 
|  |  | 
|  | // TF_LOG_CORE environment variable | 
|  | logCore string | 
|  |  | 
|  | // TF_LOG_PATH environment variable | 
|  | logPath string | 
|  |  | 
|  | // TF_LOG_PROVIDER environment variable | 
|  | logProvider string | 
|  |  | 
|  | versionLock  sync.Mutex | 
|  | execVersion  *version.Version | 
|  | provVersions map[string]*version.Version | 
|  | } | 
|  |  | 
|  | // NewTerraform returns a Terraform struct with default values for all fields. | 
|  | // If a blank execPath is supplied, NewTerraform will error. | 
|  | // Use hc-install or output from os.LookPath to get a desirable execPath. | 
|  | func NewTerraform(workingDir string, execPath string) (*Terraform, error) { | 
|  | if workingDir == "" { | 
|  | return nil, fmt.Errorf("Terraform cannot be initialised with empty workdir") | 
|  | } | 
|  |  | 
|  | if _, err := os.Stat(workingDir); err != nil { | 
|  | return nil, fmt.Errorf("error initialising Terraform with workdir %s: %s", workingDir, err) | 
|  | } | 
|  |  | 
|  | if execPath == "" { | 
|  | err := fmt.Errorf("NewTerraform: please supply the path to a Terraform executable using execPath, e.g. using the github.com/hashicorp/hc-install module.") | 
|  | return nil, &ErrNoSuitableBinary{ | 
|  | err: err, | 
|  | } | 
|  | } | 
|  | tf := Terraform{ | 
|  | execPath:   execPath, | 
|  | workingDir: workingDir, | 
|  | env:        nil, // explicit nil means copy os.Environ | 
|  | logger:     log.New(ioutil.Discard, "", 0), | 
|  | } | 
|  |  | 
|  | return &tf, nil | 
|  | } | 
|  |  | 
|  | // SetEnv allows you to override environment variables, this should not be used for any well known | 
|  | // Terraform environment variables that are already covered in options. Pass nil to copy the values | 
|  | // from os.Environ. Attempting to set environment variables that should be managed manually will | 
|  | // result in ErrManualEnvVar being returned. | 
|  | func (tf *Terraform) SetEnv(env map[string]string) error { | 
|  | prohibited := ProhibitedEnv(env) | 
|  | if len(prohibited) > 0 { | 
|  | // just error on the first instance | 
|  | return &ErrManualEnvVar{prohibited[0]} | 
|  | } | 
|  |  | 
|  | tf.env = env | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // SetLogger specifies a logger for tfexec to use. | 
|  | func (tf *Terraform) SetLogger(logger printfer) { | 
|  | tf.logger = logger | 
|  | } | 
|  |  | 
|  | // SetStdout specifies a writer to stream stdout to for every command. | 
|  | // | 
|  | // This should be used for information or logging purposes only, not control | 
|  | // flow. Any parsing necessary should be added as functionality to this package. | 
|  | func (tf *Terraform) SetStdout(w io.Writer) { | 
|  | tf.stdout = w | 
|  | } | 
|  |  | 
|  | // SetStderr specifies a writer to stream stderr to for every command. | 
|  | // | 
|  | // This should be used for information or logging purposes only, not control | 
|  | // flow. Any parsing necessary should be added as functionality to this package. | 
|  | func (tf *Terraform) SetStderr(w io.Writer) { | 
|  | tf.stderr = w | 
|  | } | 
|  |  | 
|  | // SetLog sets the TF_LOG environment variable for Terraform CLI execution. | 
|  | // This must be combined with a call to SetLogPath to take effect. | 
|  | // | 
|  | // This is only compatible with Terraform CLI 0.15.0 or later as setting the | 
|  | // log level was unreliable in earlier versions. It will default to TRACE when | 
|  | // SetLogPath is called on versions 0.14.11 and earlier, or if SetLogCore and | 
|  | // SetLogProvider have not been called before SetLogPath on versions 0.15.0 and | 
|  | // later. | 
|  | func (tf *Terraform) SetLog(log string) error { | 
|  | err := tf.compatible(context.Background(), tf0_15_0, nil) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | tf.log = log | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // SetLogCore sets the TF_LOG_CORE environment variable for Terraform CLI | 
|  | // execution. This must be combined with a call to SetLogPath to take effect. | 
|  | // | 
|  | // This is only compatible with Terraform CLI 0.15.0 or later. | 
|  | func (tf *Terraform) SetLogCore(logCore string) error { | 
|  | err := tf.compatible(context.Background(), tf0_15_0, nil) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | tf.logCore = logCore | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // SetLogPath sets the TF_LOG_PATH environment variable for Terraform CLI | 
|  | // execution. | 
|  | func (tf *Terraform) SetLogPath(path string) error { | 
|  | tf.logPath = path | 
|  | // Prevent setting the log path without enabling logging | 
|  | if tf.log == "" && tf.logCore == "" && tf.logProvider == "" { | 
|  | tf.log = "TRACE" | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // SetLogProvider sets the TF_LOG_PROVIDER environment variable for Terraform | 
|  | // CLI execution. This must be combined with a call to SetLogPath to take | 
|  | // effect. | 
|  | // | 
|  | // This is only compatible with Terraform CLI 0.15.0 or later. | 
|  | func (tf *Terraform) SetLogProvider(logProvider string) error { | 
|  | err := tf.compatible(context.Background(), tf0_15_0, nil) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | tf.logProvider = logProvider | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // SetAppendUserAgent sets the TF_APPEND_USER_AGENT environment variable for | 
|  | // Terraform CLI execution. | 
|  | func (tf *Terraform) SetAppendUserAgent(ua string) error { | 
|  | tf.appendUserAgent = ua | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // SetDisablePluginTLS sets the TF_DISABLE_PLUGIN_TLS environment variable for | 
|  | // Terraform CLI execution. | 
|  | func (tf *Terraform) SetDisablePluginTLS(disabled bool) error { | 
|  | tf.disablePluginTLS = disabled | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // SetSkipProviderVerify sets the TF_SKIP_PROVIDER_VERIFY environment variable | 
|  | // for Terraform CLI execution. This is no longer used in 0.13.0 and greater. | 
|  | func (tf *Terraform) SetSkipProviderVerify(skip bool) error { | 
|  | err := tf.compatible(context.Background(), nil, tf0_13_0) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | tf.skipProviderVerify = skip | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // WorkingDir returns the working directory for Terraform. | 
|  | func (tf *Terraform) WorkingDir() string { | 
|  | return tf.workingDir | 
|  | } | 
|  |  | 
|  | // ExecPath returns the path to the Terraform executable. | 
|  | func (tf *Terraform) ExecPath() string { | 
|  | return tf.execPath | 
|  | } |