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