github.com/hooklift/terraform@v0.11.0-beta1.0.20171117000744-6786c1361ffe/command/console.go (about)

     1  package command
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/terraform/backend"
     9  	"github.com/hashicorp/terraform/config"
    10  	"github.com/hashicorp/terraform/helper/wrappedstreams"
    11  	"github.com/hashicorp/terraform/repl"
    12  
    13  	"github.com/mitchellh/cli"
    14  )
    15  
    16  // ConsoleCommand is a Command implementation that applies a Terraform
    17  // configuration and actually builds or changes infrastructure.
    18  type ConsoleCommand struct {
    19  	Meta
    20  
    21  	// When this channel is closed, the apply will be cancelled.
    22  	ShutdownCh <-chan struct{}
    23  }
    24  
    25  func (c *ConsoleCommand) Run(args []string) int {
    26  	args, err := c.Meta.process(args, true)
    27  	if err != nil {
    28  		return 1
    29  	}
    30  
    31  	cmdFlags := c.Meta.flagSet("console")
    32  	cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
    33  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    34  	if err := cmdFlags.Parse(args); err != nil {
    35  		return 1
    36  	}
    37  
    38  	configPath, err := ModulePath(cmdFlags.Args())
    39  	if err != nil {
    40  		c.Ui.Error(err.Error())
    41  		return 1
    42  	}
    43  
    44  	// Load the module
    45  	mod, err := c.Module(configPath)
    46  	if err != nil {
    47  		c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
    48  		return 1
    49  	}
    50  
    51  	var conf *config.Config
    52  	if mod != nil {
    53  		conf = mod.Config()
    54  	}
    55  
    56  	// Load the backend
    57  	b, err := c.Backend(&BackendOpts{
    58  		Config: conf,
    59  	})
    60  
    61  	if err != nil {
    62  		c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
    63  		return 1
    64  	}
    65  
    66  	// We require a local backend
    67  	local, ok := b.(backend.Local)
    68  	if !ok {
    69  		c.Ui.Error(ErrUnsupportedLocalOp)
    70  		return 1
    71  	}
    72  
    73  	// Build the operation
    74  	opReq := c.Operation()
    75  	opReq.Module = mod
    76  
    77  	// Get the context
    78  	ctx, _, err := local.Context(opReq)
    79  	if err != nil {
    80  		c.Ui.Error(err.Error())
    81  		return 1
    82  	}
    83  
    84  	// Setup the UI so we can output directly to stdout
    85  	ui := &cli.BasicUi{
    86  		Writer:      wrappedstreams.Stdout(),
    87  		ErrorWriter: wrappedstreams.Stderr(),
    88  	}
    89  
    90  	// IO Loop
    91  	session := &repl.Session{
    92  		Interpolater: ctx.Interpolater(),
    93  	}
    94  
    95  	// Determine if stdin is a pipe. If so, we evaluate directly.
    96  	if c.StdinPiped() {
    97  		return c.modePiped(session, ui)
    98  	}
    99  
   100  	return c.modeInteractive(session, ui)
   101  }
   102  
   103  func (c *ConsoleCommand) modePiped(session *repl.Session, ui cli.Ui) int {
   104  	var lastResult string
   105  	scanner := bufio.NewScanner(wrappedstreams.Stdin())
   106  	for scanner.Scan() {
   107  		// Handle it. If there is an error exit immediately
   108  		result, err := session.Handle(strings.TrimSpace(scanner.Text()))
   109  		if err != nil {
   110  			ui.Error(err.Error())
   111  			return 1
   112  		}
   113  
   114  		// Store the last result
   115  		lastResult = result
   116  	}
   117  
   118  	// Output the final result
   119  	ui.Output(lastResult)
   120  
   121  	return 0
   122  }
   123  
   124  func (c *ConsoleCommand) Help() string {
   125  	helpText := `
   126  Usage: terraform console [options] [DIR]
   127  
   128    Starts an interactive console for experimenting with Terraform
   129    interpolations.
   130  
   131    This will open an interactive console that you can use to type
   132    interpolations into and inspect their values. This command loads the
   133    current state. This lets you explore and test interpolations before
   134    using them in future configurations.
   135  
   136    This command will never modify your state.
   137  
   138    DIR can be set to a directory with a Terraform state to load. By
   139    default, this will default to the current working directory.
   140  
   141  Options:
   142  
   143    -state=path            Path to read state. Defaults to "terraform.tfstate"
   144  
   145    -var 'foo=bar'         Set a variable in the Terraform configuration. This
   146                           flag can be set multiple times.
   147  
   148    -var-file=foo          Set variables in the Terraform configuration from
   149                           a file. If "terraform.tfvars" or any ".auto.tfvars"
   150                           files are present, they will be automatically loaded.
   151  
   152  
   153  `
   154  	return strings.TrimSpace(helpText)
   155  }
   156  
   157  func (c *ConsoleCommand) Synopsis() string {
   158  	return "Interactive console for Terraform interpolations"
   159  }