github.com/ffrizzo/terraform@v0.8.2-0.20161219200057-992e12335f3d/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. With no additional arguments, output will display all
   251    the outputs for the root module.  If NAME is not specified, all
   252    outputs are printed.
   253  
   254  Options:
   255  
   256    -state=path      Path to the state file to read. Defaults to
   257                     "terraform.tfstate".
   258  
   259    -no-color        If specified, output won't contain any color.
   260  
   261    -module=name     If specified, returns the outputs for a
   262                     specific module
   263  
   264    -json            If specified, machine readable output will be
   265                     printed in JSON format
   266  
   267  `
   268  	return strings.TrimSpace(helpText)
   269  }
   270  
   271  func (c *OutputCommand) Synopsis() string {
   272  	return "Read an output from a state file"
   273  }