carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1// Package awsinv discovers what an AWS account is carrying and reports it
2// as an internal/infraview.Inventory. It is the ONLY package in this repo
3// that imports an AWS SDK for inventory purposes, which is the whole point
4// of the seam: the console page that renders an estate never sees an AWS
5// type, so a second provider is a new package rather than a rewrite.
6//
7// Every call here is read-only. The IAM policy this package needs
8// (infra/modules/region-host/main.tf) contains no write action, so that
9// property is enforced by the credential and not by this comment.
10package awsinv
11
12import (
13 "context"
14
15 "github.com/aws/aws-sdk-go-v2/aws"
16 "github.com/aws/aws-sdk-go-v2/service/acm"
17 "github.com/aws/aws-sdk-go-v2/service/cloudwatch"
18 "github.com/aws/aws-sdk-go-v2/service/ec2"
19 "github.com/aws/aws-sdk-go-v2/service/iam"
20 "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
21 "github.com/aws/aws-sdk-go-v2/service/route53"
22 "github.com/aws/aws-sdk-go-v2/service/s3"
23)
24
25// The narrow interfaces exist so the mappings can be tested against fakes
26// with no network and no credential. Each lists exactly the calls this
27// package makes and nothing else — an interface that named the whole
28// client would be an invitation to reach for a write.
29type EC2API interface {
30 DescribeInstances(context.Context, *ec2.DescribeInstancesInput, ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
31 DescribeVolumes(context.Context, *ec2.DescribeVolumesInput, ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error)
32 DescribeAddresses(context.Context, *ec2.DescribeAddressesInput, ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error)
33 DescribeSecurityGroups(context.Context, *ec2.DescribeSecurityGroupsInput, ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error)
34 DescribeVpcs(context.Context, *ec2.DescribeVpcsInput, ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error)
35 DescribeSubnets(context.Context, *ec2.DescribeSubnetsInput, ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error)
36 DescribeRegions(context.Context, *ec2.DescribeRegionsInput, ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error)
37}
38
39type S3API interface {
40 ListBuckets(context.Context, *s3.ListBucketsInput, ...func(*s3.Options)) (*s3.ListBucketsOutput, error)
41 GetBucketLocation(context.Context, *s3.GetBucketLocationInput, ...func(*s3.Options)) (*s3.GetBucketLocationOutput, error)
42 GetBucketVersioning(context.Context, *s3.GetBucketVersioningInput, ...func(*s3.Options)) (*s3.GetBucketVersioningOutput, error)
43 GetBucketEncryption(context.Context, *s3.GetBucketEncryptionInput, ...func(*s3.Options)) (*s3.GetBucketEncryptionOutput, error)
44 GetPublicAccessBlock(context.Context, *s3.GetPublicAccessBlockInput, ...func(*s3.Options)) (*s3.GetPublicAccessBlockOutput, error)
45}
46
47type Route53API interface {
48 ListHostedZones(context.Context, *route53.ListHostedZonesInput, ...func(*route53.Options)) (*route53.ListHostedZonesOutput, error)
49 ListResourceRecordSets(context.Context, *route53.ListResourceRecordSetsInput, ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error)
50}
51
52type IAMAPI interface {
53 ListRoles(context.Context, *iam.ListRolesInput, ...func(*iam.Options)) (*iam.ListRolesOutput, error)
54 ListUsers(context.Context, *iam.ListUsersInput, ...func(*iam.Options)) (*iam.ListUsersOutput, error)
55 ListAttachedRolePolicies(context.Context, *iam.ListAttachedRolePoliciesInput, ...func(*iam.Options)) (*iam.ListAttachedRolePoliciesOutput, error)
56}
57
58type CloudWatchAPI interface {
59 DescribeAlarms(context.Context, *cloudwatch.DescribeAlarmsInput, ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsOutput, error)
60}
61
62type ACMAPI interface {
63 ListCertificates(context.Context, *acm.ListCertificatesInput, ...func(*acm.Options)) (*acm.ListCertificatesOutput, error)
64}
65
66type TaggingAPI interface {
67 GetResources(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error)
68}
69
70// ec2Methods satisfies EC2API from method values of one ec2 client.
71type ec2Methods struct {
72 describeInstances func(context.Context, *ec2.DescribeInstancesInput, ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
73 describeVolumes func(context.Context, *ec2.DescribeVolumesInput, ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error)
74 describeAddresses func(context.Context, *ec2.DescribeAddressesInput, ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error)
75 describeSecurityGroups func(context.Context, *ec2.DescribeSecurityGroupsInput, ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error)
76 describeVpcs func(context.Context, *ec2.DescribeVpcsInput, ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error)
77 describeSubnets func(context.Context, *ec2.DescribeSubnetsInput, ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error)
78 describeRegions func(context.Context, *ec2.DescribeRegionsInput, ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error)
79}
80
81func (m ec2Methods) DescribeInstances(ctx context.Context, in *ec2.DescribeInstancesInput, opts ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) {
82 return m.describeInstances(ctx, in, opts...)
83}
84
85func (m ec2Methods) DescribeVolumes(ctx context.Context, in *ec2.DescribeVolumesInput, opts ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error) {
86 return m.describeVolumes(ctx, in, opts...)
87}
88
89func (m ec2Methods) DescribeAddresses(ctx context.Context, in *ec2.DescribeAddressesInput, opts ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error) {
90 return m.describeAddresses(ctx, in, opts...)
91}
92
93func (m ec2Methods) DescribeSecurityGroups(ctx context.Context, in *ec2.DescribeSecurityGroupsInput, opts ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error) {
94 return m.describeSecurityGroups(ctx, in, opts...)
95}
96
97func (m ec2Methods) DescribeVpcs(ctx context.Context, in *ec2.DescribeVpcsInput, opts ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error) {
98 return m.describeVpcs(ctx, in, opts...)
99}
100
101func (m ec2Methods) DescribeSubnets(ctx context.Context, in *ec2.DescribeSubnetsInput, opts ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) {
102 return m.describeSubnets(ctx, in, opts...)
103}
104
105func (m ec2Methods) DescribeRegions(ctx context.Context, in *ec2.DescribeRegionsInput, opts ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) {
106 return m.describeRegions(ctx, in, opts...)
107}
108
109func newEc2Methods(c *ec2.Client) ec2Methods {
110 return ec2Methods{
111 describeInstances: c.DescribeInstances,
112 describeVolumes: c.DescribeVolumes,
113 describeAddresses: c.DescribeAddresses,
114 describeSecurityGroups: c.DescribeSecurityGroups,
115 describeVpcs: c.DescribeVpcs,
116 describeSubnets: c.DescribeSubnets,
117 describeRegions: c.DescribeRegions,
118 }
119}
120
121// s3Methods satisfies S3API from method values of one s3 client.
122type s3Methods struct {
123 listBuckets func(context.Context, *s3.ListBucketsInput, ...func(*s3.Options)) (*s3.ListBucketsOutput, error)
124 getBucketLocation func(context.Context, *s3.GetBucketLocationInput, ...func(*s3.Options)) (*s3.GetBucketLocationOutput, error)
125 getBucketVersioning func(context.Context, *s3.GetBucketVersioningInput, ...func(*s3.Options)) (*s3.GetBucketVersioningOutput, error)
126 getBucketEncryption func(context.Context, *s3.GetBucketEncryptionInput, ...func(*s3.Options)) (*s3.GetBucketEncryptionOutput, error)
127 getPublicAccessBlock func(context.Context, *s3.GetPublicAccessBlockInput, ...func(*s3.Options)) (*s3.GetPublicAccessBlockOutput, error)
128}
129
130func (m s3Methods) ListBuckets(ctx context.Context, in *s3.ListBucketsInput, opts ...func(*s3.Options)) (*s3.ListBucketsOutput, error) {
131 return m.listBuckets(ctx, in, opts...)
132}
133
134func (m s3Methods) GetBucketLocation(ctx context.Context, in *s3.GetBucketLocationInput, opts ...func(*s3.Options)) (*s3.GetBucketLocationOutput, error) {
135 return m.getBucketLocation(ctx, in, opts...)
136}
137
138func (m s3Methods) GetBucketVersioning(ctx context.Context, in *s3.GetBucketVersioningInput, opts ...func(*s3.Options)) (*s3.GetBucketVersioningOutput, error) {
139 return m.getBucketVersioning(ctx, in, opts...)
140}
141
142func (m s3Methods) GetBucketEncryption(ctx context.Context, in *s3.GetBucketEncryptionInput, opts ...func(*s3.Options)) (*s3.GetBucketEncryptionOutput, error) {
143 return m.getBucketEncryption(ctx, in, opts...)
144}
145
146func (m s3Methods) GetPublicAccessBlock(ctx context.Context, in *s3.GetPublicAccessBlockInput, opts ...func(*s3.Options)) (*s3.GetPublicAccessBlockOutput, error) {
147 return m.getPublicAccessBlock(ctx, in, opts...)
148}
149
150func newS3Methods(c *s3.Client) s3Methods {
151 return s3Methods{
152 listBuckets: c.ListBuckets,
153 getBucketLocation: c.GetBucketLocation,
154 getBucketVersioning: c.GetBucketVersioning,
155 getBucketEncryption: c.GetBucketEncryption,
156 getPublicAccessBlock: c.GetPublicAccessBlock,
157 }
158}
159
160// route53Methods satisfies Route53API from method values of one route53 client.
161type route53Methods struct {
162 listHostedZones func(context.Context, *route53.ListHostedZonesInput, ...func(*route53.Options)) (*route53.ListHostedZonesOutput, error)
163 listResourceRecordSets func(context.Context, *route53.ListResourceRecordSetsInput, ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error)
164}
165
166func (m route53Methods) ListHostedZones(ctx context.Context, in *route53.ListHostedZonesInput, opts ...func(*route53.Options)) (*route53.ListHostedZonesOutput, error) {
167 return m.listHostedZones(ctx, in, opts...)
168}
169
170func (m route53Methods) ListResourceRecordSets(ctx context.Context, in *route53.ListResourceRecordSetsInput, opts ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error) {
171 return m.listResourceRecordSets(ctx, in, opts...)
172}
173
174func newRoute53Methods(c *route53.Client) route53Methods {
175 return route53Methods{
176 listHostedZones: c.ListHostedZones,
177 listResourceRecordSets: c.ListResourceRecordSets,
178 }
179}
180
181// iamMethods satisfies IAMAPI from method values of one iam client.
182type iamMethods struct {
183 listRoles func(context.Context, *iam.ListRolesInput, ...func(*iam.Options)) (*iam.ListRolesOutput, error)
184 listUsers func(context.Context, *iam.ListUsersInput, ...func(*iam.Options)) (*iam.ListUsersOutput, error)
185 listAttachedRolePolicies func(context.Context, *iam.ListAttachedRolePoliciesInput, ...func(*iam.Options)) (*iam.ListAttachedRolePoliciesOutput, error)
186}
187
188func (m iamMethods) ListRoles(ctx context.Context, in *iam.ListRolesInput, opts ...func(*iam.Options)) (*iam.ListRolesOutput, error) {
189 return m.listRoles(ctx, in, opts...)
190}
191
192func (m iamMethods) ListUsers(ctx context.Context, in *iam.ListUsersInput, opts ...func(*iam.Options)) (*iam.ListUsersOutput, error) {
193 return m.listUsers(ctx, in, opts...)
194}
195
196func (m iamMethods) ListAttachedRolePolicies(ctx context.Context, in *iam.ListAttachedRolePoliciesInput, opts ...func(*iam.Options)) (*iam.ListAttachedRolePoliciesOutput, error) {
197 return m.listAttachedRolePolicies(ctx, in, opts...)
198}
199
200func newIamMethods(c *iam.Client) iamMethods {
201 return iamMethods{
202 listRoles: c.ListRoles,
203 listUsers: c.ListUsers,
204 listAttachedRolePolicies: c.ListAttachedRolePolicies,
205 }
206}
207
208// cloudwatchMethods satisfies CloudWatchAPI from method values of one cloudwatch client.
209type cloudwatchMethods struct {
210 describeAlarms func(context.Context, *cloudwatch.DescribeAlarmsInput, ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsOutput, error)
211}
212
213func (m cloudwatchMethods) DescribeAlarms(ctx context.Context, in *cloudwatch.DescribeAlarmsInput, opts ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsOutput, error) {
214 return m.describeAlarms(ctx, in, opts...)
215}
216
217func newCloudwatchMethods(c *cloudwatch.Client) cloudwatchMethods {
218 return cloudwatchMethods{
219 describeAlarms: c.DescribeAlarms,
220 }
221}
222
223// acmMethods satisfies ACMAPI from method values of one acm client.
224type acmMethods struct {
225 listCertificates func(context.Context, *acm.ListCertificatesInput, ...func(*acm.Options)) (*acm.ListCertificatesOutput, error)
226}
227
228func (m acmMethods) ListCertificates(ctx context.Context, in *acm.ListCertificatesInput, opts ...func(*acm.Options)) (*acm.ListCertificatesOutput, error) {
229 return m.listCertificates(ctx, in, opts...)
230}
231
232func newAcmMethods(c *acm.Client) acmMethods {
233 return acmMethods{
234 listCertificates: c.ListCertificates,
235 }
236}
237
238// resourcegroupstaggingapiMethods satisfies TaggingAPI from method values of one resourcegroupstaggingapi client.
239type resourcegroupstaggingapiMethods struct {
240 getResources func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error)
241}
242
243func (m resourcegroupstaggingapiMethods) GetResources(ctx context.Context, in *resourcegroupstaggingapi.GetResourcesInput, opts ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
244 return m.getResources(ctx, in, opts...)
245}
246
247func newResourcegroupstaggingapiMethods(c *resourcegroupstaggingapi.Client) resourcegroupstaggingapiMethods {
248 return resourcegroupstaggingapiMethods{
249 getResources: c.GetResources,
250 }
251}
252
253// Clients is one region's worth of read-only clients. S3/Route53/IAM are
254// global services; the assembly calls them once, from the home region's
255// Clients, rather than once per region.
256type Clients struct {
257 EC2 EC2API
258 S3 S3API
259 Route53 Route53API
260 IAM IAMAPI
261 CloudWatch CloudWatchAPI
262 ACM ACMAPI
263 Tagging TaggingAPI
264}
265
266// Factory builds the clients for one region. Tests inject a fake; the real
267// one below rebases a single resolved aws.Config onto each region so the
268// credential chain resolves exactly once.
269type Factory func(region string) Clients
270
271// factoryFor stores METHOD VALUES, not the SDK clients, in the interfaces.
272// html/template (the edge and console pages) switches the linker into a
273// mode that keeps every exported method of any concrete type stored in an
274// interface value. A *ec2.Client in an interface kept all ~2,400 EC2
275// methods (14 MB), and the other clients kept theirs too: the linux/arm64
276// binary doubled to 77.7 MB (discussion #275). A func value bound to one
277// method keeps that method and nothing else. Do not "simplify" these back
278// to ec2.NewFromConfig(c) in the struct; the CI size ceiling will say so.
279func factoryFor(cfg aws.Config) Factory {
280 return func(region string) Clients {
281 c := cfg.Copy()
282 if region != "" {
283 c.Region = region
284 }
285 return Clients{
286 EC2: newEc2Methods(ec2.NewFromConfig(c)),
287 S3: newS3Methods(s3.NewFromConfig(c)),
288 Route53: newRoute53Methods(route53.NewFromConfig(c)),
289 IAM: newIamMethods(iam.NewFromConfig(c)),
290 CloudWatch: newCloudwatchMethods(cloudwatch.NewFromConfig(c)),
291 ACM: newAcmMethods(acm.NewFromConfig(c)),
292 Tagging: newResourcegroupstaggingapiMethods(resourcegroupstaggingapi.NewFromConfig(c)),
293 }
294 }
295}
296