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