@@ -14,6 +14,7 @@ import (
14
14
15
15
"github.com/awalterschulze/gographviz"
16
16
"github.com/hashicorp/terraform-exec/tfexec"
17
+ tfjson "github.com/hashicorp/terraform-json"
17
18
"github.com/mitchellh/mapstructure"
18
19
"golang.org/x/xerrors"
19
20
@@ -88,15 +89,24 @@ func (t *terraform) Provision(stream proto.DRPCProvisioner_ProvisionStream) erro
88
89
})
89
90
}
90
91
}()
92
+ terraformEnv := map [string ]string {}
93
+ // Required for "terraform init" to find "git" to
94
+ // clone Terraform modules.
95
+ for _ , env := range os .Environ () {
96
+ parts := strings .SplitN (env , "=" , 2 )
97
+ if len (parts ) < 2 {
98
+ continue
99
+ }
100
+ terraformEnv [parts [0 ]] = parts [1 ]
101
+ }
91
102
// Only Linux reliably works with the Terraform plugin
92
103
// cache directory. It's unknown why this is.
93
104
if t .cachePath != "" && runtime .GOOS == "linux" {
94
- err = terraform .SetEnv (map [string ]string {
95
- "TF_PLUGIN_CACHE_DIR" : t .cachePath ,
96
- })
97
- if err != nil {
98
- return xerrors .Errorf ("set terraform plugin cache dir: %w" , err )
99
- }
105
+ terraformEnv ["TF_PLUGIN_CACHE_DIR" ] = t .cachePath
106
+ }
107
+ err = terraform .SetEnv (terraformEnv )
108
+ if err != nil {
109
+ return xerrors .Errorf ("set terraform env: %w" , err )
100
110
}
101
111
terraform .SetStdout (writer )
102
112
t .logger .Debug (shutdown , "running initialization" )
@@ -320,40 +330,22 @@ func parseTerraformPlan(ctx context.Context, terraform *tfexec.Terraform, planfi
320
330
agent .StartupScript = startupScript
321
331
}
322
332
}
323
- if _ , has := resource .Expressions ["instance_id" ]; has {
324
- // This is a dynamic value. If it's expressed, we know
325
- // it's at least an instance ID, which is better than nothing.
326
- agent .Auth = & proto.Agent_InstanceId {
327
- InstanceId : "" ,
328
- }
329
- }
330
333
331
334
agents [resource .Address ] = agent
332
335
}
336
+
333
337
for _ , resource := range plan .PlannedValues .RootModule .Resources {
334
- if resource .Type == "coder_agent" {
338
+ if resource .Mode == tfjson . DataResourceMode {
335
339
continue
336
340
}
337
- resourceKey := strings .Join ([]string {resource .Type , resource .Name }, "." )
338
- resourceNode , exists := resourceDependencies [resourceKey ]
339
- if ! exists {
341
+ if resource .Type == "coder_agent" || resource .Type == "coder_agent_instance" {
340
342
continue
341
343
}
342
- // Associate resources that depend on an agent.
343
- resourceAgents := make ([]* proto.Agent , 0 )
344
- for _ , dep := range resourceNode {
345
- var has bool
346
- agent , has := agents [dep ]
347
- if ! has {
348
- continue
349
- }
350
- resourceAgents = append (resourceAgents , agent )
351
- }
352
-
344
+ resourceKey := strings .Join ([]string {resource .Type , resource .Name }, "." )
353
345
resources = append (resources , & proto.Resource {
354
346
Name : resource .Name ,
355
347
Type : resource .Type ,
356
- Agents : resourceAgents ,
348
+ Agents : findAgents ( resourceDependencies , agents , resourceKey ) ,
357
349
})
358
350
}
359
351
@@ -460,32 +452,25 @@ func parseTerraformApply(ctx context.Context, terraform *tfexec.Terraform, state
460
452
}
461
453
462
454
for _ , resource := range state .Values .RootModule .Resources {
463
- if resource .Type == "coder_agent" || resource . Type == "coder_agent_instance" {
455
+ if resource .Mode == tfjson . DataResourceMode {
464
456
continue
465
457
}
466
- resourceKey := strings .Join ([]string {resource .Type , resource .Name }, "." )
467
- resourceNode , exists := resourceDependencies [resourceKey ]
468
- if ! exists {
458
+ if resource .Type == "coder_agent" || resource .Type == "coder_agent_instance" {
469
459
continue
470
460
}
471
- // Associate resources that depend on an agent.
472
- resourceAgents := make ([]* proto.Agent , 0 )
473
- for _ , dep := range resourceNode {
474
- var has bool
475
- agent , has := agents [dep ]
476
- if ! has {
477
- continue
478
- }
479
- resourceAgents = append (resourceAgents , agent )
480
-
461
+ resourceKey := strings .Join ([]string {resource .Type , resource .Name }, "." )
462
+ resourceAgents := findAgents (resourceDependencies , agents , resourceKey )
463
+ for _ , agent := range resourceAgents {
481
464
// Didn't use instance identity.
482
465
if agent .GetToken () != "" {
483
466
continue
484
467
}
485
468
486
469
key , isValid := map [string ]string {
487
- "google_compute_instance" : "instance_id" ,
488
- "aws_instance" : "id" ,
470
+ "google_compute_instance" : "instance_id" ,
471
+ "aws_instance" : "id" ,
472
+ "azurerm_linux_virtual_machine" : "id" ,
473
+ "azurerm_windows_virtual_machine" : "id" ,
489
474
}[resource .Type ]
490
475
if ! isValid {
491
476
// The resource type doesn't support
@@ -571,21 +556,50 @@ func findDirectDependencies(rawGraph string) (map[string][]string, error) {
571
556
continue
572
557
}
573
558
label = strings .Trim (label , `"` )
559
+ direct [label ] = findDependenciesWithLabels (graph , node .Name )
560
+ }
574
561
575
- dependencies := make ([]string , 0 )
576
- for destination := range graph .Edges .SrcToDsts [node .Name ] {
577
- dependencyNode , exists := graph .Nodes .Lookup [destination ]
578
- if ! exists {
579
- continue
580
- }
581
- label , exists := dependencyNode .Attrs ["label" ]
582
- if ! exists {
583
- continue
584
- }
585
- label = strings .Trim (label , `"` )
586
- dependencies = append (dependencies , label )
562
+ return direct , nil
563
+ }
564
+
565
+ // findDependenciesWithLabels recursively finds nodes with labels (resource and data nodes)
566
+ // to build a dependency tree.
567
+ func findDependenciesWithLabels (graph * gographviz.Graph , nodeName string ) []string {
568
+ dependencies := make ([]string , 0 )
569
+ for destination := range graph .Edges .SrcToDsts [nodeName ] {
570
+ dependencyNode , exists := graph .Nodes .Lookup [destination ]
571
+ if ! exists {
572
+ continue
587
573
}
588
- direct [label ] = dependencies
574
+ label , exists := dependencyNode .Attrs ["label" ]
575
+ if ! exists {
576
+ dependencies = append (dependencies , findDependenciesWithLabels (graph , dependencyNode .Name )... )
577
+ continue
578
+ }
579
+ label = strings .Trim (label , `"` )
580
+ dependencies = append (dependencies , label )
589
581
}
590
- return direct , nil
582
+ return dependencies
583
+ }
584
+
585
+ // findAgents recursively searches through resource dependencies
586
+ // to find associated agents. Nested is required for indirect
587
+ // dependency matching.
588
+ func findAgents (resourceDependencies map [string ][]string , agents map [string ]* proto.Agent , resourceKey string ) []* proto.Agent {
589
+ resourceNode , exists := resourceDependencies [resourceKey ]
590
+ if ! exists {
591
+ return []* proto.Agent {}
592
+ }
593
+ // Associate resources that depend on an agent.
594
+ resourceAgents := make ([]* proto.Agent , 0 )
595
+ for _ , dep := range resourceNode {
596
+ var has bool
597
+ agent , has := agents [dep ]
598
+ if ! has {
599
+ resourceAgents = append (resourceAgents , findAgents (resourceDependencies , agents , dep )... )
600
+ continue
601
+ }
602
+ resourceAgents = append (resourceAgents , agent )
603
+ }
604
+ return resourceAgents
591
605
}
0 commit comments