github.com/davebizus/terraform-main@v0.11.12-beta1/command/graph.go (about)

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