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