github.com/spirius/terraform@v0.10.0-beta2.0.20170714185654-87b2c0cf8fea/command/graph.go (about)

     1  package command
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/terraform/backend"
     9  	"github.com/hashicorp/terraform/config"
    10  	"github.com/hashicorp/terraform/config/module"
    11  	"github.com/hashicorp/terraform/dag"
    12  	"github.com/hashicorp/terraform/terraform"
    13  )
    14  
    15  // GraphCommand is a Command implementation that takes a Terraform
    16  // configuration and outputs the dependency tree in graphical form.
    17  type GraphCommand struct {
    18  	Meta
    19  }
    20  
    21  func (c *GraphCommand) Run(args []string) int {
    22  	var moduleDepth int
    23  	var verbose bool
    24  	var drawCycles bool
    25  	var graphTypeStr string
    26  
    27  	args, err := c.Meta.process(args, false)
    28  	if err != nil {
    29  		return 1
    30  	}
    31  
    32  	cmdFlags := flag.NewFlagSet("graph", flag.ContinueOnError)
    33  	c.addModuleDepthFlag(cmdFlags, &moduleDepth)
    34  	cmdFlags.BoolVar(&verbose, "verbose", false, "verbose")
    35  	cmdFlags.BoolVar(&drawCycles, "draw-cycles", false, "draw-cycles")
    36  	cmdFlags.StringVar(&graphTypeStr, "type", "", "type")
    37  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    38  	if err := cmdFlags.Parse(args); err != nil {
    39  		return 1
    40  	}
    41  
    42  	configPath, err := ModulePath(cmdFlags.Args())
    43  	if err != nil {
    44  		c.Ui.Error(err.Error())
    45  		return 1
    46  	}
    47  
    48  	// Check if the path is a plan
    49  	plan, err := c.Plan(configPath)
    50  	if err != nil {
    51  		c.Ui.Error(err.Error())
    52  		return 1
    53  	}
    54  	if plan != nil {
    55  		// Reset for backend loading
    56  		configPath = ""
    57  	}
    58  
    59  	// Load the module
    60  	var mod *module.Tree
    61  	if plan == nil {
    62  		mod, err = c.Module(configPath)
    63  		if err != nil {
    64  			c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
    65  			return 1
    66  		}
    67  	}
    68  
    69  	var conf *config.Config
    70  	if mod != nil {
    71  		conf = mod.Config()
    72  	}
    73  
    74  	// Load the backend
    75  	b, err := c.Backend(&BackendOpts{
    76  		Config: conf,
    77  		Plan:   plan,
    78  	})
    79  	if err != nil {
    80  		c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
    81  		return 1
    82  	}
    83  
    84  	// We require a local backend
    85  	local, ok := b.(backend.Local)
    86  	if !ok {
    87  		c.Ui.Error(ErrUnsupportedLocalOp)
    88  		return 1
    89  	}
    90  
    91  	// Build the operation
    92  	opReq := c.Operation()
    93  	opReq.Module = mod
    94  	opReq.Plan = plan
    95  
    96  	// Get the context
    97  	ctx, _, err := local.Context(opReq)
    98  	if err != nil {
    99  		c.Ui.Error(err.Error())
   100  		return 1
   101  	}
   102  
   103  	// Determine the graph type
   104  	graphType := terraform.GraphTypePlan
   105  	if plan != nil {
   106  		graphType = terraform.GraphTypeApply
   107  	}
   108  
   109  	if graphTypeStr != "" {
   110  		v, ok := terraform.GraphTypeMap[graphTypeStr]
   111  		if !ok {
   112  			c.Ui.Error(fmt.Sprintf("Invalid graph type requested: %s", graphTypeStr))
   113  			return 1
   114  		}
   115  
   116  		graphType = v
   117  	}
   118  
   119  	// Skip validation during graph generation - we want to see the graph even if
   120  	// it is invalid for some reason.
   121  	g, err := ctx.Graph(graphType, &terraform.ContextGraphOpts{
   122  		Verbose:  verbose,
   123  		Validate: false,
   124  	})
   125  	if err != nil {
   126  		c.Ui.Error(fmt.Sprintf("Error creating graph: %s", err))
   127  		return 1
   128  	}
   129  
   130  	graphStr, err := terraform.GraphDot(g, &dag.DotOpts{
   131  		DrawCycles: drawCycles,
   132  		MaxDepth:   moduleDepth,
   133  		Verbose:    verbose,
   134  	})
   135  	if err != nil {
   136  		c.Ui.Error(fmt.Sprintf("Error converting graph: %s", err))
   137  		return 1
   138  	}
   139  
   140  	c.Ui.Output(graphStr)
   141  
   142  	return 0
   143  }
   144  
   145  func (c *GraphCommand) Help() string {
   146  	helpText := `
   147  Usage: terraform graph [options] [DIR]
   148  
   149    Outputs the visual execution graph of Terraform resources according to
   150    configuration files in DIR (or the current directory if omitted).
   151  
   152    The graph is outputted in DOT format. The typical program that can
   153    read this format is GraphViz, but many web services are also available
   154    to read this format.
   155  
   156    The -type flag can be used to control the type of graph shown. Terraform
   157    creates different graphs for different operations. See the options below
   158    for the list of types supported. The default type is "plan" if a
   159    configuration is given, and "apply" if a plan file is passed as an
   160    argument.
   161  
   162  Options:
   163  
   164    -draw-cycles   Highlight any cycles in the graph with colored edges.
   165                   This helps when diagnosing cycle errors.
   166  
   167    -no-color      If specified, output won't contain any color.
   168  
   169    -type=plan     Type of graph to output. Can be: plan, plan-destroy, apply,
   170                   validate, input, refresh.
   171  
   172  
   173  `
   174  	return strings.TrimSpace(helpText)
   175  }
   176  
   177  func (c *GraphCommand) Synopsis() string {
   178  	return "Create a visual graph of Terraform resources"
   179  }