github.com/ggriffiths/terraform@v0.9.0-beta1.0.20170222213024-79c4935604cb/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  }