github.com/trawler/terraform@v0.10.8-0.20171106022149-4b1c7a1d9b48/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 // Building a graph may require config module to be present, even if it's 92 // empty. 93 if mod == nil && plan == nil { 94 mod = module.NewEmptyTree() 95 } 96 97 // Build the operation 98 opReq := c.Operation() 99 opReq.Module = mod 100 opReq.Plan = plan 101 102 // Get the context 103 ctx, _, err := local.Context(opReq) 104 if err != nil { 105 c.Ui.Error(err.Error()) 106 return 1 107 } 108 109 // Determine the graph type 110 graphType := terraform.GraphTypePlan 111 if plan != nil { 112 graphType = terraform.GraphTypeApply 113 } 114 115 if graphTypeStr != "" { 116 v, ok := terraform.GraphTypeMap[graphTypeStr] 117 if !ok { 118 c.Ui.Error(fmt.Sprintf("Invalid graph type requested: %s", graphTypeStr)) 119 return 1 120 } 121 122 graphType = v 123 } 124 125 // Skip validation during graph generation - we want to see the graph even if 126 // it is invalid for some reason. 127 g, err := ctx.Graph(graphType, &terraform.ContextGraphOpts{ 128 Verbose: verbose, 129 Validate: false, 130 }) 131 if err != nil { 132 c.Ui.Error(fmt.Sprintf("Error creating graph: %s", err)) 133 return 1 134 } 135 136 graphStr, err := terraform.GraphDot(g, &dag.DotOpts{ 137 DrawCycles: drawCycles, 138 MaxDepth: moduleDepth, 139 Verbose: verbose, 140 }) 141 if err != nil { 142 c.Ui.Error(fmt.Sprintf("Error converting graph: %s", err)) 143 return 1 144 } 145 146 c.Ui.Output(graphStr) 147 148 return 0 149 } 150 151 func (c *GraphCommand) Help() string { 152 helpText := ` 153 Usage: terraform graph [options] [DIR] 154 155 Outputs the visual execution graph of Terraform resources according to 156 configuration files in DIR (or the current directory if omitted). 157 158 The graph is outputted in DOT format. The typical program that can 159 read this format is GraphViz, but many web services are also available 160 to read this format. 161 162 The -type flag can be used to control the type of graph shown. Terraform 163 creates different graphs for different operations. See the options below 164 for the list of types supported. The default type is "plan" if a 165 configuration is given, and "apply" if a plan file is passed as an 166 argument. 167 168 Options: 169 170 -draw-cycles Highlight any cycles in the graph with colored edges. 171 This helps when diagnosing cycle errors. 172 173 -no-color If specified, output won't contain any color. 174 175 -type=plan Type of graph to output. Can be: plan, plan-destroy, apply, 176 validate, input, refresh. 177 178 179 ` 180 return strings.TrimSpace(helpText) 181 } 182 183 func (c *GraphCommand) Synopsis() string { 184 return "Create a visual graph of Terraform resources" 185 }