github.com/kevholditch/terraform@v0.9.7-0.20170613192930-9706042ddd51/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 }