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