github.com/fabiomatavelli/terraform@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 }