github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/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 // Determine the graph type 116 graphType := terraform.GraphTypePlan 117 if plan != nil { 118 graphType = terraform.GraphTypeApply 119 } 120 121 if graphTypeStr != "" { 122 v, ok := terraform.GraphTypeMap[graphTypeStr] 123 if !ok { 124 c.Ui.Error(fmt.Sprintf("Invalid graph type requested: %s", graphTypeStr)) 125 return 1 126 } 127 128 graphType = v 129 } 130 131 // Skip validation during graph generation - we want to see the graph even if 132 // it is invalid for some reason. 133 g, err := ctx.Graph(graphType, &terraform.ContextGraphOpts{ 134 Verbose: verbose, 135 Validate: false, 136 }) 137 if err != nil { 138 c.Ui.Error(fmt.Sprintf("Error creating graph: %s", err)) 139 return 1 140 } 141 142 graphStr, err := terraform.GraphDot(g, &dag.DotOpts{ 143 DrawCycles: drawCycles, 144 MaxDepth: moduleDepth, 145 Verbose: verbose, 146 }) 147 if err != nil { 148 c.Ui.Error(fmt.Sprintf("Error converting graph: %s", err)) 149 return 1 150 } 151 152 if diags.HasErrors() { 153 // For this command we only show diagnostics if there are errors, 154 // because printing out naked warnings could upset a naive program 155 // consuming our dot output. 156 c.showDiagnostics(diags) 157 return 1 158 } 159 160 c.Ui.Output(graphStr) 161 162 return 0 163 } 164 165 func (c *GraphCommand) Help() string { 166 helpText := ` 167 Usage: terraform graph [options] [DIR] 168 169 Outputs the visual execution graph of Terraform resources according to 170 configuration files in DIR (or the current directory if omitted). 171 172 The graph is outputted in DOT format. The typical program that can 173 read this format is GraphViz, but many web services are also available 174 to read this format. 175 176 The -type flag can be used to control the type of graph shown. Terraform 177 creates different graphs for different operations. See the options below 178 for the list of types supported. The default type is "plan" if a 179 configuration is given, and "apply" if a plan file is passed as an 180 argument. 181 182 Options: 183 184 -draw-cycles Highlight any cycles in the graph with colored edges. 185 This helps when diagnosing cycle errors. 186 187 -no-color If specified, output won't contain any color. 188 189 -type=plan Type of graph to output. Can be: plan, plan-destroy, apply, 190 validate, input, refresh. 191 192 193 ` 194 return strings.TrimSpace(helpText) 195 } 196 197 func (c *GraphCommand) Synopsis() string { 198 return "Create a visual graph of Terraform resources" 199 }