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