github.com/profects/terraform@v0.9.0-beta1.0.20170227135739-92d4809db30d/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 err := stateStore.RefreshState(); err != nil { 61 c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) 62 return 1 63 } 64 65 if module == "" { 66 module = "root" 67 } else { 68 module = "root." + module 69 } 70 71 // Get the proper module we want to get outputs for 72 modPath := strings.Split(module, ".") 73 74 state := stateStore.State() 75 mod := state.ModuleByPath(modPath) 76 if mod == nil { 77 c.Ui.Error(fmt.Sprintf( 78 "The module %s could not be found. There is nothing to output.", 79 module)) 80 return 1 81 } 82 83 if state.Empty() || len(mod.Outputs) == 0 { 84 c.Ui.Error( 85 "The state file either has no outputs defined, or all the defined\n" + 86 "outputs are empty. Please define an output in your configuration\n" + 87 "with the `output` keyword and run `terraform refresh` for it to\n" + 88 "become available. If you are using interpolation, please verify\n" + 89 "the interpolated value is not empty. You can use the \n" + 90 "`terraform console` command to assist.") 91 return 1 92 } 93 94 if name == "" { 95 if jsonOutput { 96 jsonOutputs, err := json.MarshalIndent(mod.Outputs, "", " ") 97 if err != nil { 98 return 1 99 } 100 101 c.Ui.Output(string(jsonOutputs)) 102 return 0 103 } else { 104 c.Ui.Output(outputsAsString(state, modPath, nil, false)) 105 return 0 106 } 107 } 108 109 v, ok := mod.Outputs[name] 110 if !ok { 111 c.Ui.Error(fmt.Sprintf( 112 "The output variable requested could not be found in the state\n" + 113 "file. If you recently added this to your configuration, be\n" + 114 "sure to run `terraform apply`, since the state won't be updated\n" + 115 "with new output variables until that command is run.")) 116 return 1 117 } 118 119 if jsonOutput { 120 jsonOutputs, err := json.MarshalIndent(v, "", " ") 121 if err != nil { 122 return 1 123 } 124 125 c.Ui.Output(string(jsonOutputs)) 126 } else { 127 switch output := v.Value.(type) { 128 case string: 129 c.Ui.Output(output) 130 return 0 131 case []interface{}: 132 c.Ui.Output(formatListOutput("", "", output)) 133 return 0 134 case map[string]interface{}: 135 c.Ui.Output(formatMapOutput("", "", output)) 136 return 0 137 default: 138 c.Ui.Error(fmt.Sprintf("Unknown output type: %T", v.Type)) 139 return 1 140 } 141 } 142 143 return 0 144 } 145 146 func formatNestedList(indent string, outputList []interface{}) string { 147 outputBuf := new(bytes.Buffer) 148 outputBuf.WriteString(fmt.Sprintf("%s[", indent)) 149 150 lastIdx := len(outputList) - 1 151 152 for i, value := range outputList { 153 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, " ", value)) 154 if i != lastIdx { 155 outputBuf.WriteString(",") 156 } 157 } 158 159 outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) 160 return strings.TrimPrefix(outputBuf.String(), "\n") 161 } 162 163 func formatListOutput(indent, outputName string, outputList []interface{}) string { 164 keyIndent := "" 165 166 outputBuf := new(bytes.Buffer) 167 168 if outputName != "" { 169 outputBuf.WriteString(fmt.Sprintf("%s%s = [", indent, outputName)) 170 keyIndent = " " 171 } 172 173 lastIdx := len(outputList) - 1 174 175 for i, value := range outputList { 176 switch typedValue := value.(type) { 177 case string: 178 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, keyIndent, value)) 179 case []interface{}: 180 outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent, 181 formatNestedList(indent+keyIndent, typedValue))) 182 case map[string]interface{}: 183 outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent, 184 formatNestedMap(indent+keyIndent, typedValue))) 185 } 186 187 if lastIdx != i { 188 outputBuf.WriteString(",") 189 } 190 } 191 192 if outputName != "" { 193 if len(outputList) > 0 { 194 outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) 195 } else { 196 outputBuf.WriteString("]") 197 } 198 } 199 200 return strings.TrimPrefix(outputBuf.String(), "\n") 201 } 202 203 func formatNestedMap(indent string, outputMap map[string]interface{}) string { 204 ks := make([]string, 0, len(outputMap)) 205 for k, _ := range outputMap { 206 ks = append(ks, k) 207 } 208 sort.Strings(ks) 209 210 outputBuf := new(bytes.Buffer) 211 outputBuf.WriteString(fmt.Sprintf("%s{", indent)) 212 213 lastIdx := len(outputMap) - 1 214 for i, k := range ks { 215 v := outputMap[k] 216 outputBuf.WriteString(fmt.Sprintf("\n%s%s = %v", indent+" ", k, v)) 217 218 if lastIdx != i { 219 outputBuf.WriteString(",") 220 } 221 } 222 223 outputBuf.WriteString(fmt.Sprintf("\n%s}", indent)) 224 225 return strings.TrimPrefix(outputBuf.String(), "\n") 226 } 227 228 func formatMapOutput(indent, outputName string, outputMap map[string]interface{}) string { 229 ks := make([]string, 0, len(outputMap)) 230 for k, _ := range outputMap { 231 ks = append(ks, k) 232 } 233 sort.Strings(ks) 234 235 keyIndent := "" 236 237 outputBuf := new(bytes.Buffer) 238 if outputName != "" { 239 outputBuf.WriteString(fmt.Sprintf("%s%s = {", indent, outputName)) 240 keyIndent = " " 241 } 242 243 for _, k := range ks { 244 v := outputMap[k] 245 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s = %v", indent, keyIndent, k, v)) 246 } 247 248 if outputName != "" { 249 if len(outputMap) > 0 { 250 outputBuf.WriteString(fmt.Sprintf("\n%s}", indent)) 251 } else { 252 outputBuf.WriteString("}") 253 } 254 } 255 256 return strings.TrimPrefix(outputBuf.String(), "\n") 257 } 258 259 func (c *OutputCommand) Help() string { 260 helpText := ` 261 Usage: terraform output [options] [NAME] 262 263 Reads an output variable from a Terraform state file and prints 264 the value. With no additional arguments, output will display all 265 the outputs for the root module. If NAME is not specified, all 266 outputs are printed. 267 268 Options: 269 270 -state=path Path to the state file to read. Defaults to 271 "terraform.tfstate". 272 273 -no-color If specified, output won't contain any color. 274 275 -module=name If specified, returns the outputs for a 276 specific module 277 278 -json If specified, machine readable output will be 279 printed in JSON format 280 281 ` 282 return strings.TrimSpace(helpText) 283 } 284 285 func (c *OutputCommand) Synopsis() string { 286 return "Read an output from a state file" 287 }