github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/command/output.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "flag" 7 "fmt" 8 "sort" 9 "strings" 10 ) 11 12 // OutputCommand is a Command implementation that reads an output 13 // from a Terraform state and prints it. 14 type OutputCommand struct { 15 Meta 16 } 17 18 func (c *OutputCommand) Run(args []string) int { 19 args = c.Meta.process(args, false) 20 21 var module string 22 var jsonOutput bool 23 cmdFlags := flag.NewFlagSet("output", flag.ContinueOnError) 24 cmdFlags.BoolVar(&jsonOutput, "json", false, "json") 25 cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") 26 cmdFlags.StringVar(&module, "module", "", "module") 27 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 28 if err := cmdFlags.Parse(args); err != nil { 29 return 1 30 } 31 32 args = cmdFlags.Args() 33 if len(args) > 1 { 34 c.Ui.Error( 35 "The output command expects exactly one argument with the name\n" + 36 "of an output variable or no arguments to show all outputs.\n") 37 cmdFlags.Usage() 38 return 1 39 } 40 41 name := "" 42 if len(args) > 0 { 43 name = args[0] 44 } 45 46 // Load the backend 47 b, err := c.Backend(nil) 48 if err != nil { 49 c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) 50 return 1 51 } 52 53 // Get the state 54 stateStore, err := b.State() 55 if err != nil { 56 c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) 57 return 1 58 } 59 60 if module == "" { 61 module = "root" 62 } else { 63 module = "root." + module 64 } 65 66 // Get the proper module we want to get outputs for 67 modPath := strings.Split(module, ".") 68 69 state := stateStore.State() 70 mod := state.ModuleByPath(modPath) 71 if mod == nil { 72 c.Ui.Error(fmt.Sprintf( 73 "The module %s could not be found. There is nothing to output.", 74 module)) 75 return 1 76 } 77 78 if state.Empty() || len(mod.Outputs) == 0 { 79 c.Ui.Error( 80 "The state file either has no outputs defined, or all the defined\n" + 81 "outputs are empty. Please define an output in your configuration\n" + 82 "with the `output` keyword and run `terraform refresh` for it to\n" + 83 "become available. If you are using interpolation, please verify\n" + 84 "the interpolated value is not empty. You can use the \n" + 85 "`terraform console` command to assist.") 86 return 1 87 } 88 89 if name == "" { 90 if jsonOutput { 91 jsonOutputs, err := json.MarshalIndent(mod.Outputs, "", " ") 92 if err != nil { 93 return 1 94 } 95 96 c.Ui.Output(string(jsonOutputs)) 97 return 0 98 } else { 99 c.Ui.Output(outputsAsString(state, modPath, nil, false)) 100 return 0 101 } 102 } 103 104 v, ok := mod.Outputs[name] 105 if !ok { 106 c.Ui.Error(fmt.Sprintf( 107 "The output variable requested could not be found in the state\n" + 108 "file. If you recently added this to your configuration, be\n" + 109 "sure to run `terraform apply`, since the state won't be updated\n" + 110 "with new output variables until that command is run.")) 111 return 1 112 } 113 114 if jsonOutput { 115 jsonOutputs, err := json.MarshalIndent(v, "", " ") 116 if err != nil { 117 return 1 118 } 119 120 c.Ui.Output(string(jsonOutputs)) 121 } else { 122 switch output := v.Value.(type) { 123 case string: 124 c.Ui.Output(output) 125 return 0 126 case []interface{}: 127 c.Ui.Output(formatListOutput("", "", output)) 128 return 0 129 case map[string]interface{}: 130 c.Ui.Output(formatMapOutput("", "", output)) 131 return 0 132 default: 133 c.Ui.Error(fmt.Sprintf("Unknown output type: %T", v.Type)) 134 return 1 135 } 136 } 137 138 return 0 139 } 140 141 func formatNestedList(indent string, outputList []interface{}) string { 142 outputBuf := new(bytes.Buffer) 143 outputBuf.WriteString(fmt.Sprintf("%s[", indent)) 144 145 lastIdx := len(outputList) - 1 146 147 for i, value := range outputList { 148 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, " ", value)) 149 if i != lastIdx { 150 outputBuf.WriteString(",") 151 } 152 } 153 154 outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) 155 return strings.TrimPrefix(outputBuf.String(), "\n") 156 } 157 158 func formatListOutput(indent, outputName string, outputList []interface{}) string { 159 keyIndent := "" 160 161 outputBuf := new(bytes.Buffer) 162 163 if outputName != "" { 164 outputBuf.WriteString(fmt.Sprintf("%s%s = [", indent, outputName)) 165 keyIndent = " " 166 } 167 168 lastIdx := len(outputList) - 1 169 170 for i, value := range outputList { 171 switch typedValue := value.(type) { 172 case string: 173 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, keyIndent, value)) 174 case []interface{}: 175 outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent, 176 formatNestedList(indent+keyIndent, typedValue))) 177 case map[string]interface{}: 178 outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent, 179 formatNestedMap(indent+keyIndent, typedValue))) 180 } 181 182 if lastIdx != i { 183 outputBuf.WriteString(",") 184 } 185 } 186 187 if outputName != "" { 188 if len(outputList) > 0 { 189 outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) 190 } else { 191 outputBuf.WriteString("]") 192 } 193 } 194 195 return strings.TrimPrefix(outputBuf.String(), "\n") 196 } 197 198 func formatNestedMap(indent string, outputMap map[string]interface{}) string { 199 ks := make([]string, 0, len(outputMap)) 200 for k, _ := range outputMap { 201 ks = append(ks, k) 202 } 203 sort.Strings(ks) 204 205 outputBuf := new(bytes.Buffer) 206 outputBuf.WriteString(fmt.Sprintf("%s{", indent)) 207 208 lastIdx := len(outputMap) - 1 209 for i, k := range ks { 210 v := outputMap[k] 211 outputBuf.WriteString(fmt.Sprintf("\n%s%s = %v", indent+" ", k, v)) 212 213 if lastIdx != i { 214 outputBuf.WriteString(",") 215 } 216 } 217 218 outputBuf.WriteString(fmt.Sprintf("\n%s}", indent)) 219 220 return strings.TrimPrefix(outputBuf.String(), "\n") 221 } 222 223 func formatMapOutput(indent, outputName string, outputMap map[string]interface{}) string { 224 ks := make([]string, 0, len(outputMap)) 225 for k, _ := range outputMap { 226 ks = append(ks, k) 227 } 228 sort.Strings(ks) 229 230 keyIndent := "" 231 232 outputBuf := new(bytes.Buffer) 233 if outputName != "" { 234 outputBuf.WriteString(fmt.Sprintf("%s%s = {", indent, outputName)) 235 keyIndent = " " 236 } 237 238 for _, k := range ks { 239 v := outputMap[k] 240 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s = %v", indent, keyIndent, k, v)) 241 } 242 243 if outputName != "" { 244 if len(outputMap) > 0 { 245 outputBuf.WriteString(fmt.Sprintf("\n%s}", indent)) 246 } else { 247 outputBuf.WriteString("}") 248 } 249 } 250 251 return strings.TrimPrefix(outputBuf.String(), "\n") 252 } 253 254 func (c *OutputCommand) Help() string { 255 helpText := ` 256 Usage: terraform output [options] [NAME] 257 258 Reads an output variable from a Terraform state file and prints 259 the value. With no additional arguments, output will display all 260 the outputs for the root module. If NAME is not specified, all 261 outputs are printed. 262 263 Options: 264 265 -state=path Path to the state file to read. Defaults to 266 "terraform.tfstate". 267 268 -no-color If specified, output won't contain any color. 269 270 -module=name If specified, returns the outputs for a 271 specific module 272 273 -json If specified, machine readable output will be 274 printed in JSON format 275 276 ` 277 return strings.TrimSpace(helpText) 278 } 279 280 func (c *OutputCommand) Synopsis() string { 281 return "Read an output from a state file" 282 }