github.com/wtlangford/terraform@v0.7.0-rc2.0.20160709103016-0e07a2776833/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  
   152  	if outputName != "" {
   153  		outputBuf.WriteString(fmt.Sprintf("%s%s = [", indent, outputName))
   154  		keyIndent = "  "
   155  	}
   156  
   157  	for _, value := range outputList {
   158  		outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, keyIndent, value))
   159  	}
   160  
   161  	if outputName != "" {
   162  		if len(outputList) > 0 {
   163  			outputBuf.WriteString(fmt.Sprintf("\n%s]", indent))
   164  		} else {
   165  			outputBuf.WriteString("]")
   166  		}
   167  	}
   168  
   169  	return strings.TrimPrefix(outputBuf.String(), "\n")
   170  }
   171  
   172  func formatMapOutput(indent, outputName string, outputMap map[string]interface{}) string {
   173  	ks := make([]string, 0, len(outputMap))
   174  	for k, _ := range outputMap {
   175  		ks = append(ks, k)
   176  	}
   177  	sort.Strings(ks)
   178  
   179  	keyIndent := ""
   180  
   181  	outputBuf := new(bytes.Buffer)
   182  	if outputName != "" {
   183  		outputBuf.WriteString(fmt.Sprintf("%s%s = {", indent, outputName))
   184  		keyIndent = "  "
   185  	}
   186  
   187  	for _, k := range ks {
   188  		v := outputMap[k]
   189  		outputBuf.WriteString(fmt.Sprintf("\n%s%s%s = %v", indent, keyIndent, k, v))
   190  	}
   191  
   192  	if outputName != "" {
   193  		if len(outputMap) > 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 (c *OutputCommand) Help() string {
   204  	helpText := `
   205  Usage: terraform output [options] [NAME]
   206  
   207    Reads an output variable from a Terraform state file and prints
   208    the value.  If NAME is not specified, all outputs are printed.
   209  
   210  Options:
   211  
   212    -state=path      Path to the state file to read. Defaults to
   213                     "terraform.tfstate".
   214  
   215    -no-color        If specified, output won't contain any color.
   216  
   217    -module=name     If specified, returns the outputs for a
   218                     specific module
   219  
   220  `
   221  	return strings.TrimSpace(helpText)
   222  }
   223  
   224  func (c *OutputCommand) Synopsis() string {
   225  	return "Read an output from a state file"
   226  }