Skip to content

Commit 443bae9

Browse files
committed
fix: Properly manage tree relationship state
1 parent 8141ee0 commit 443bae9

54 files changed

Lines changed: 1716 additions & 5 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pkg/tree/aws/aws.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,35 @@ type AWS struct {
123123
}
124124

125125
func (aws *AWS) PostProcess() {
126+
// Reset relationships this method writes to so PostProcess is idempotent.
127+
for i := range aws.MSK.Clusters {
128+
aws.MSK.Clusters[i].Relationships.AppAutoscalingTargets = nil
129+
}
130+
for i := range aws.DynamoDB.Tables {
131+
aws.DynamoDB.Tables[i].Relationships.AppAutoscalingTargets = nil
132+
}
133+
for i := range aws.ElastiCache.ReplicationGroups {
134+
aws.ElastiCache.ReplicationGroups[i].Relationships.AppAutoscalingTargets = nil
135+
}
136+
for i := range aws.EKS.NodeGroups {
137+
aws.EKS.NodeGroups[i].Relationships.LaunchTemplate = nil
138+
}
139+
for i := range aws.Scheduler.Schedules {
140+
aws.Scheduler.Schedules[i].Relationships.TaskDefinition = nil
141+
}
142+
for i := range aws.CloudWatch.EventTargets {
143+
aws.CloudWatch.EventTargets[i].Relationships.TaskDefinition = nil
144+
}
145+
for i := range aws.Pipes.Pipes {
146+
aws.Pipes.Pipes[i].Relationships.TaskDefinition = nil
147+
}
148+
for i := range aws.DirectConnect.GatewayAssociations {
149+
aws.DirectConnect.GatewayAssociations[i].Relationships.TransitGateway = nil
150+
}
151+
for i := range aws.ECS.Services {
152+
aws.ECS.Services[i].Relationships.Subnets = nil
153+
}
154+
126155
// acm - link cert authorities to certificate manager
127156
aws.CertificateManager.AddCertificateAuthorities(&aws.PCACertificateAuthority)
128157

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package aws
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
8+
"github.com/infracost/go-proto/pkg/tree/aws/appautoscaling"
9+
"github.com/infracost/go-proto/pkg/tree/aws/cloudwatch"
10+
"github.com/infracost/go-proto/pkg/tree/aws/directconnect"
11+
"github.com/infracost/go-proto/pkg/tree/aws/dynamodb"
12+
"github.com/infracost/go-proto/pkg/tree/aws/ec2"
13+
"github.com/infracost/go-proto/pkg/tree/aws/ecs"
14+
"github.com/infracost/go-proto/pkg/tree/aws/eks"
15+
"github.com/infracost/go-proto/pkg/tree/aws/elasticache"
16+
"github.com/infracost/go-proto/pkg/tree/aws/msk"
17+
"github.com/infracost/go-proto/pkg/tree/aws/pipes"
18+
"github.com/infracost/go-proto/pkg/tree/aws/scheduler"
19+
"github.com/infracost/go-proto/pkg/tree/resource"
20+
"github.com/infracost/go-proto/pkg/tree/value"
21+
)
22+
23+
func TestAWSPostProcess_IsIdempotent(t *testing.T) {
24+
a := &AWS{
25+
AppAutoScaling: appautoscaling.AppAutoScaling{
26+
Targets: []appautoscaling.Target{
27+
{
28+
Resource: resource.Resource{ID: "tgt-msk"},
29+
ResourceID: value.New("msk-cluster-1", 0, "", nil),
30+
},
31+
{
32+
Resource: resource.Resource{ID: "tgt-ddb"},
33+
ResourceID: value.New("table/my-table", 0, "", nil),
34+
},
35+
{
36+
Resource: resource.Resource{ID: "tgt-rg"},
37+
ResourceID: value.New("replication-group/rg-1", 0, "", nil),
38+
},
39+
},
40+
},
41+
MSK: msk.MSK{
42+
Clusters: []msk.Cluster{
43+
{Resource: resource.Resource{ID: "msk-cluster-1"}},
44+
},
45+
},
46+
DynamoDB: dynamodb.DynamoDB{
47+
Tables: []dynamodb.Table{
48+
{
49+
Resource: resource.Resource{ID: "ddb-1"},
50+
Name: value.New("my-table", 0, "", nil),
51+
},
52+
},
53+
},
54+
ElastiCache: elasticache.ElastiCache{
55+
ReplicationGroups: []elasticache.ReplicationGroup{
56+
{
57+
Resource: resource.Resource{ID: "rg-1"},
58+
ID: value.New("rg-1", 0, "", nil),
59+
},
60+
},
61+
},
62+
EKS: eks.EKS{
63+
NodeGroups: []eks.NodeGroup{
64+
{
65+
Resource: resource.Resource{ID: "ng-1"},
66+
LaunchTemplateID: value.New("lt-1", 0, "", nil),
67+
},
68+
},
69+
},
70+
EC2: ec2.EC2{
71+
LaunchTemplates: []ec2.LaunchTemplate{
72+
{Resource: resource.Resource{ID: "lt-1"}},
73+
},
74+
Subnets: []ec2.Subnet{
75+
{Resource: resource.Resource{ID: "subnet-1"}},
76+
},
77+
TransitGateways: []ec2.TransitGateway{
78+
{Resource: resource.Resource{ID: "tgw-1"}},
79+
},
80+
},
81+
Scheduler: scheduler.Scheduler{
82+
Schedules: []scheduler.Schedule{
83+
{
84+
Resource: resource.Resource{ID: "sch-1"},
85+
TaskDefinitionARN: value.New("td-1", 0, "", nil),
86+
},
87+
},
88+
},
89+
CloudWatch: cloudwatch.CloudWatch{
90+
EventTargets: []cloudwatch.EventTarget{
91+
{
92+
Resource: resource.Resource{ID: "et-1"},
93+
TaskDefinitionID: value.New("td-1", 0, "", nil),
94+
},
95+
},
96+
},
97+
Pipes: pipes.Pipes{
98+
Pipes: []pipes.Pipe{
99+
{
100+
Resource: resource.Resource{ID: "pipe-1"},
101+
TaskDefinitionID: value.New("td-1", 0, "", nil),
102+
},
103+
},
104+
},
105+
ECS: ecs.ECS{
106+
TaskDefinitions: []ecs.TaskDefinition{
107+
{Resource: resource.Resource{ID: "td-1"}},
108+
},
109+
Services: []ecs.Service{
110+
{
111+
Resource: resource.Resource{ID: "svc-1"},
112+
SubnetIDs: *value.NewList([]value.Value[string]{
113+
value.New("subnet-1", 0, "", nil),
114+
}, 0, "", nil),
115+
},
116+
},
117+
},
118+
DirectConnect: directconnect.DirectConnect{
119+
GatewayAssociations: []directconnect.GatewayAssociation{
120+
{
121+
Resource: resource.Resource{ID: "ga-1"},
122+
AssociatedGatewayID: value.New("tgw-1", 0, "", nil),
123+
},
124+
},
125+
},
126+
}
127+
128+
a.PostProcess()
129+
130+
mskTargets := append([]appautoscaling.Target(nil), a.MSK.Clusters[0].Relationships.AppAutoscalingTargets...)
131+
ddbTargets := append([]appautoscaling.Target(nil), a.DynamoDB.Tables[0].Relationships.AppAutoscalingTargets...)
132+
rgTargets := append([]*appautoscaling.Target(nil), a.ElastiCache.ReplicationGroups[0].Relationships.AppAutoscalingTargets...)
133+
eksLT := a.EKS.NodeGroups[0].Relationships.LaunchTemplate
134+
schTD := a.Scheduler.Schedules[0].Relationships.TaskDefinition
135+
cwTD := a.CloudWatch.EventTargets[0].Relationships.TaskDefinition
136+
pipeTD := a.Pipes.Pipes[0].Relationships.TaskDefinition
137+
gaTGW := a.DirectConnect.GatewayAssociations[0].Relationships.TransitGateway
138+
svcSubnets := append([]*ec2.Subnet(nil), a.ECS.Services[0].Relationships.Subnets...)
139+
140+
a.PostProcess()
141+
142+
assert.Equal(t, mskTargets, a.MSK.Clusters[0].Relationships.AppAutoscalingTargets)
143+
assert.Equal(t, ddbTargets, a.DynamoDB.Tables[0].Relationships.AppAutoscalingTargets)
144+
assert.Equal(t, rgTargets, a.ElastiCache.ReplicationGroups[0].Relationships.AppAutoscalingTargets)
145+
assert.Equal(t, eksLT, a.EKS.NodeGroups[0].Relationships.LaunchTemplate)
146+
assert.Equal(t, schTD, a.Scheduler.Schedules[0].Relationships.TaskDefinition)
147+
assert.Equal(t, cwTD, a.CloudWatch.EventTargets[0].Relationships.TaskDefinition)
148+
assert.Equal(t, pipeTD, a.Pipes.Pipes[0].Relationships.TaskDefinition)
149+
assert.Equal(t, gaTGW, a.DirectConnect.GatewayAssociations[0].Relationships.TransitGateway)
150+
assert.Equal(t, svcSubnets, a.ECS.Services[0].Relationships.Subnets)
151+
152+
// Sanity-check we actually exercised the code paths.
153+
assert.Len(t, mskTargets, 1)
154+
assert.Len(t, ddbTargets, 1)
155+
assert.Len(t, rgTargets, 1)
156+
assert.NotNil(t, eksLT)
157+
assert.NotNil(t, schTD)
158+
assert.NotNil(t, cwTD)
159+
assert.NotNil(t, pipeTD)
160+
assert.NotNil(t, gaTGW)
161+
assert.Len(t, svcSubnets, 1)
162+
}

pkg/tree/aws/ec2/ec2.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,42 @@ type EC2 struct {
3030
}
3131

3232
func (ec2 *EC2) PostProcess() {
33-
// reset instance relationships
33+
// Reset relationships this method writes to so PostProcess is idempotent.
3434
for i := range ec2.Instances {
3535
ec2.Instances[i].Relationships = InstanceRelationships{}
3636
}
37+
for i := range ec2.AutoscalingGroups {
38+
ec2.AutoscalingGroups[i].Relationships = AutoscalingGroupRelationships{}
39+
}
40+
for i := range ec2.VPCEndpoints {
41+
ec2.VPCEndpoints[i].Relationships = VPCEndpointRelationships{}
42+
}
43+
for i := range ec2.VPCs {
44+
ec2.VPCs[i].Relationships.VPCEndpoints = nil
45+
}
46+
for i := range ec2.Subnets {
47+
ec2.Subnets[i].Relationships.NATGateways = nil
48+
}
49+
for i := range ec2.NATGateways {
50+
ec2.NATGateways[i].Relationships = NATGatewayRelationships{}
51+
}
52+
for i := range ec2.ElasticIPs {
53+
ec2.ElasticIPs[i].Relationships = ElasticIPRelationships{}
54+
}
55+
for i := range ec2.ElasticIPAssociations {
56+
ec2.ElasticIPAssociations[i].Relationships = ElasticIPAssociationRelationships{}
57+
}
58+
for i := range ec2.TransitGatewayVPCAttachments {
59+
ec2.TransitGatewayVPCAttachments[i].Relationships = TransitGatewayVPCAttachmentRelationships{}
60+
}
61+
for i := range ec2.TransitGatewayPeeringAttachments {
62+
ec2.TransitGatewayPeeringAttachments[i].Relationships = TransitGatewayPeeringAttachmentRelationships{}
63+
}
64+
for i := range ec2.LoadBalancers {
65+
for j := range ec2.LoadBalancers[i].SubnetMappings {
66+
ec2.LoadBalancers[i].SubnetMappings[j].Relationships = SubnetMappingRelationships{}
67+
}
68+
}
3769

3870
// link instance states to instances
3971
for i, instanceState := range ec2.InstanceStates {

0 commit comments

Comments
 (0)