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