github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/command/output.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "sort" 8 "strconv" 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 cmdFlags := flag.NewFlagSet("output", flag.ContinueOnError) 23 cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") 24 cmdFlags.StringVar(&module, "module", "", "module") 25 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 26 27 if err := cmdFlags.Parse(args); err != nil { 28 return 1 29 } 30 31 args = cmdFlags.Args() 32 if len(args) > 2 { 33 c.Ui.Error( 34 "The output command expects exactly one argument with the name\n" + 35 "of an output variable or no arguments to show all outputs.\n") 36 cmdFlags.Usage() 37 return 1 38 } 39 40 name := "" 41 if len(args) > 0 { 42 name = args[0] 43 } 44 45 index := "" 46 if len(args) > 1 { 47 index = args[1] 48 } 49 50 stateStore, err := c.Meta.State() 51 if err != nil { 52 c.Ui.Error(fmt.Sprintf("Error reading state: %s", err)) 53 return 1 54 } 55 56 if module == "" { 57 module = "root" 58 } else { 59 module = "root." + module 60 } 61 62 // Get the proper module we want to get outputs for 63 modPath := strings.Split(module, ".") 64 65 state := stateStore.State() 66 mod := state.ModuleByPath(modPath) 67 68 if mod == nil { 69 c.Ui.Error(fmt.Sprintf( 70 "The module %s could not be found. There is nothing to output.", 71 module)) 72 return 1 73 } 74 75 if state.Empty() || len(mod.Outputs) == 0 { 76 c.Ui.Error(fmt.Sprintf( 77 "The state file has no outputs defined. Define an output\n" + 78 "in your configuration with the `output` directive and re-run\n" + 79 "`terraform apply` for it to become available.")) 80 return 1 81 } 82 83 if name == "" { 84 c.Ui.Output(outputsAsString(state, nil, false)) 85 return 0 86 } 87 88 v, ok := mod.Outputs[name] 89 if !ok { 90 c.Ui.Error(fmt.Sprintf( 91 "The output variable requested could not be found in the state\n" + 92 "file. If you recently added this to your configuration, be\n" + 93 "sure to run `terraform apply`, since the state won't be updated\n" + 94 "with new output variables until that command is run.")) 95 return 1 96 } 97 98 switch output := v.Value.(type) { 99 case string: 100 c.Ui.Output(output) 101 return 0 102 case []interface{}: 103 if index == "" { 104 c.Ui.Output(formatListOutput("", "", output)) 105 break 106 } 107 108 indexInt, err := strconv.Atoi(index) 109 if err != nil { 110 c.Ui.Error(fmt.Sprintf( 111 "The index %q requested is not valid for the list output\n"+ 112 "%q - indices must be numeric, and in the range 0-%d", index, name, 113 len(output)-1)) 114 break 115 } 116 117 if indexInt < 0 || indexInt >= len(output) { 118 c.Ui.Error(fmt.Sprintf( 119 "The index %d requested is not valid for the list output\n"+ 120 "%q - indices must be in the range 0-%d", indexInt, name, 121 len(output)-1)) 122 break 123 } 124 125 c.Ui.Output(fmt.Sprintf("%s", output[indexInt])) 126 return 0 127 case map[string]interface{}: 128 if index == "" { 129 c.Ui.Output(formatMapOutput("", "", output)) 130 break 131 } 132 133 if value, ok := output[index]; ok { 134 c.Ui.Output(fmt.Sprintf("%s", value)) 135 return 0 136 } else { 137 return 1 138 } 139 default: 140 c.Ui.Error(fmt.Sprintf("Unknown output type: %T", v.Type)) 141 return 1 142 } 143 144 return 0 145 } 146 147 func formatListOutput(indent, outputName string, outputList []interface{}) string { 148 keyIndent := "" 149 150 outputBuf := new(bytes.Buffer) 151 152 if outputName != "" { 153 outputBuf.WriteString(fmt.Sprintf("%s%s = [", indent, outputName)) 154 keyIndent = " " 155 } 156 157 for _, value := range outputList { 158 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, keyIndent, value)) 159 } 160 161 if outputName != "" { 162 if len(outputList) > 0 { 163 outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) 164 } else { 165 outputBuf.WriteString("]") 166 } 167 } 168 169 return strings.TrimPrefix(outputBuf.String(), "\n") 170 } 171 172 func formatMapOutput(indent, outputName string, outputMap map[string]interface{}) string { 173 ks := make([]string, 0, len(outputMap)) 174 for k, _ := range outputMap { 175 ks = append(ks, k) 176 } 177 sort.Strings(ks) 178 179 keyIndent := "" 180 181 outputBuf := new(bytes.Buffer) 182 if outputName != "" { 183 outputBuf.WriteString(fmt.Sprintf("%s%s = {", indent, outputName)) 184 keyIndent = " " 185 } 186 187 for _, k := range ks { 188 v := outputMap[k] 189 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s = %v", indent, keyIndent, k, v)) 190 } 191 192 if outputName != "" { 193 if len(outputMap) > 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 (c *OutputCommand) Help() string { 204 helpText := ` 205 Usage: terraform output [options] [NAME] 206 207 Reads an output variable from a Terraform state file and prints 208 the value. If NAME is not specified, all outputs are printed. 209 210 Options: 211 212 -state=path Path to the state file to read. Defaults to 213 "terraform.tfstate". 214 215 -no-color If specified, output won't contain any color. 216 217 -module=name If specified, returns the outputs for a 218 specific module 219 220 ` 221 return strings.TrimSpace(helpText) 222 } 223 224 func (c *OutputCommand) Synopsis() string { 225 return "Read an output from a state file" 226 }