@@ -4,12 +4,14 @@ import (
4
4
"context"
5
5
"encoding/json"
6
6
"fmt"
7
+ "time"
7
8
8
9
"github.com/google/uuid"
10
+ "github.com/gotidy/ptr"
9
11
"github.com/pkg/errors"
10
12
"github.com/sirupsen/logrus"
13
+ "go.uber.org/ratelimit"
11
14
12
- "github.com/aws/aws-sdk-go/aws"
13
15
"github.com/aws/aws-sdk-go/aws/awserr"
14
16
"github.com/aws/aws-sdk-go/service/cloudcontrolapi"
15
17
@@ -53,6 +55,15 @@ func init() {
53
55
RegisterCloudControl ("AWS::NetworkFirewall::RuleGroup" )
54
56
}
55
57
58
+ // describeRateLimit is a rate limiter to avoid throttling when describing resources via the cloud control api.
59
+ // AWS does not publish the rate limits for the cloud control api, the rate seems to be 60 reqs/minute, setting to
60
+ // 55 and setting no slack to avoid throttling.
61
+ var describeRateLimit = ratelimit .New (55 , ratelimit .Per (time .Minute ), ratelimit .WithoutSlack )
62
+
63
+ // RegisterCloudControl registers a resource type for the Cloud Control API. This is a unique function that is used
64
+ // in two different places. The first place is in the init() function of this file, where it is used to register
65
+ // a select subset of Cloud Control API resource types. The second place is in nuke command file, where it is used
66
+ // to dynamically register any resource type provided via the `--cloud-control` flag.
56
67
func RegisterCloudControl (typeName string ) {
57
68
registry .Register (& registry.Registration {
58
69
Name : typeName ,
@@ -66,26 +77,32 @@ func RegisterCloudControl(typeName string) {
66
77
67
78
type CloudControlResourceLister struct {
68
79
TypeName string
80
+
81
+ logger * logrus.Entry
69
82
}
70
83
71
84
func (l * CloudControlResourceLister ) List (_ context.Context , o interface {}) ([]resource.Resource , error ) {
72
85
opts := o .(* nuke.ListerOpts )
86
+ l .logger = opts .Logger .WithField ("type-name" , l .TypeName )
73
87
74
88
svc := cloudcontrolapi .New (opts .Session )
89
+ resources := make ([]resource.Resource , 0 )
75
90
76
91
params := & cloudcontrolapi.ListResourcesInput {
77
- TypeName : aws .String (l .TypeName ),
92
+ TypeName : ptr .String (l .TypeName ),
93
+ MaxResults : ptr .Int64 (100 ),
78
94
}
79
- resources := make ([]resource. Resource , 0 )
95
+
80
96
if err := svc .ListResourcesPages (params , func (page * cloudcontrolapi.ListResourcesOutput , lastPage bool ) bool {
81
- for _ , desc := range page . ResourceDescriptions {
82
- identifier := aws . StringValue ( desc . Identifier )
97
+ dt := describeRateLimit . Take ()
98
+ l . logger . Debugf ( "rate limit time: %s" , dt )
83
99
84
- properties , err := cloudControlParseProperties (aws .StringValue (desc .Properties ))
100
+ for _ , desc := range page .ResourceDescriptions {
101
+ identifier := ptr .ToString (desc .Identifier )
102
+ properties , err := l .cloudControlParseProperties (ptr .ToString (desc .Properties ))
85
103
if err != nil {
86
- logrus .
104
+ l . logger .
87
105
WithError (errors .WithStack (err )).
88
- WithField ("type-name" , l .TypeName ).
89
106
WithField ("identifier" , identifier ).
90
107
Error ("failed to parse cloud control properties" )
91
108
continue
@@ -117,17 +134,19 @@ func (l *CloudControlResourceLister) List(_ context.Context, o interface{}) ([]r
117
134
return resources , nil
118
135
}
119
136
120
- func cloudControlParseProperties (payload string ) (types.Properties , error ) {
137
+ func ( l * CloudControlResourceLister ) cloudControlParseProperties (payload string ) (types.Properties , error ) {
121
138
// Warning: The implementation of this function is not very straightforward,
122
139
// because the aws-nuke filter functions expect a very rigid structure and
123
140
// the properties from the Cloud Control API are very dynamic.
124
141
142
+ properties := types .NewProperties ()
125
143
propMap := map [string ]interface {}{}
144
+
126
145
err := json .Unmarshal ([]byte (payload ), & propMap )
127
146
if err != nil {
128
- return nil , err
147
+ return properties , err
129
148
}
130
- properties := types . NewProperties ()
149
+
131
150
for name , value := range propMap {
132
151
switch v := value .(type ) {
133
152
case string :
@@ -147,12 +166,12 @@ func cloudControlParseProperties(payload string) (types.Properties, error) {
147
166
v2 ["Value" ],
148
167
)
149
168
} else {
150
- logrus .
169
+ l . logger .
151
170
WithField ("value" , fmt .Sprintf ("%q" , v )).
152
171
Debugf ("nested cloud control property type []%T is not supported" , value )
153
172
}
154
173
default :
155
- logrus .
174
+ l . logger .
156
175
WithField ("value" , fmt .Sprintf ("%q" , v )).
157
176
Debugf ("nested cloud control property type []%T is not supported" , value )
158
177
}
@@ -163,9 +182,9 @@ func cloudControlParseProperties(payload string) (types.Properties, error) {
163
182
// properties.Set, because it would fall back to
164
183
// fmt.Sprintf. Since the cloud control properties are
165
184
// nested it would create properties that are not
166
- // suitable for filtering. Therefore we have to
185
+ // suitable for filtering. Therefore, we have to
167
186
// implemented more sophisticated parsing.
168
- logrus .
187
+ l . logger .
169
188
WithField ("value" , fmt .Sprintf ("%q" , v )).
170
189
Debugf ("cloud control property type %T is not supported" , v )
171
190
}
0 commit comments