github.com/miquella/terraform@v0.6.17-0.20160517195040-40db82f25ec0/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.(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 panic(fmt.Errorf("Unknown output type: %T", output)) 141 } 142 143 return 0 144 } 145 146 func formatListOutput(indent, outputName string, outputList []interface{}) string { 147 keyIndent := "" 148 149 outputBuf := new(bytes.Buffer) 150 if outputName != "" { 151 outputBuf.WriteString(fmt.Sprintf("%s%s = [", indent, outputName)) 152 keyIndent = " " 153 } 154 155 for _, value := range outputList { 156 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, keyIndent, value)) 157 } 158 159 if outputName != "" { 160 outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) 161 } 162 163 return strings.TrimPrefix(outputBuf.String(), "\n") 164 } 165 166 func formatMapOutput(indent, outputName string, outputMap map[string]interface{}) string { 167 ks := make([]string, 0, len(outputMap)) 168 for k, _ := range outputMap { 169 ks = append(ks, k) 170 } 171 sort.Strings(ks) 172 173 keyIndent := "" 174 175 outputBuf := new(bytes.Buffer) 176 if outputName != "" { 177 outputBuf.WriteString(fmt.Sprintf("%s%s = {", indent, outputName)) 178 keyIndent = " " 179 } 180 181 for _, k := range ks { 182 v := outputMap[k] 183 outputBuf.WriteString(fmt.Sprintf("\n%s%s%s = %v", indent, keyIndent, k, v)) 184 } 185 186 if outputName != "" { 187 outputBuf.WriteString(fmt.Sprintf("\n%s}", indent)) 188 } 189 190 return strings.TrimPrefix(outputBuf.String(), "\n") 191 } 192 193 func (c *OutputCommand) Help() string { 194 helpText := ` 195 Usage: terraform output [options] [NAME] 196 197 Reads an output variable from a Terraform state file and prints 198 the value. If NAME is not specified, all outputs are printed. 199 200 Options: 201 202 -state=path Path to the state file to read. Defaults to 203 "terraform.tfstate". 204 205 -no-color If specified, output won't contain any color. 206 207 -module=name If specified, returns the outputs for a 208 specific module 209 210 ` 211 return strings.TrimSpace(helpText) 212 } 213 214 func (c *OutputCommand) Synopsis() string { 215 return "Read an output from a state file" 216 }