github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/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 "github.com/hashicorp/terraform/tfdiags" 13 14 "github.com/mitchellh/cli" 15 ) 16 17 // ConsoleCommand is a Command implementation that applies a Terraform 18 // configuration and actually builds or changes infrastructure. 19 type ConsoleCommand struct { 20 Meta 21 } 22 23 func (c *ConsoleCommand) Run(args []string) int { 24 args, err := c.Meta.process(args, true) 25 if err != nil { 26 return 1 27 } 28 29 cmdFlags := c.Meta.flagSet("console") 30 cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") 31 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 32 if err := cmdFlags.Parse(args); err != nil { 33 return 1 34 } 35 36 configPath, err := ModulePath(cmdFlags.Args()) 37 if err != nil { 38 c.Ui.Error(err.Error()) 39 return 1 40 } 41 42 var diags tfdiags.Diagnostics 43 44 // Load the module 45 mod, diags := c.Module(configPath) 46 if diags.HasErrors() { 47 c.showDiagnostics(diags) 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 }