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