-
Notifications
You must be signed in to change notification settings - Fork 0
/
discoverer_aws_rds.go
215 lines (191 loc) · 6.75 KB
/
discoverer_aws_rds.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
package discoverers
import (
"context"
"fmt"
"strings"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/rds/types"
"github.com/borderzero/border0-go/lib/types/set"
"github.com/borderzero/border0-go/lib/types/slice"
"github.com/borderzero/discovery"
"github.com/borderzero/discovery/utils"
)
const (
defaultAwsRdsDiscovererDiscovererId = "aws_rds_discoverer"
defaultAwsRdsDiscovererGetAccountIdTimeout = time.Second * 10
)
var (
defaultAwsRdsDiscovererIncludedInstanceStatuses = set.New("creating", "backing-up", "starting", "available", "maintenance", "modifying")
)
// AwsRdsDiscoverer represents a discoverer for AWS RDS resources.
type AwsRdsDiscoverer struct {
cfg aws.Config
discovererId string
getAccountIdTimeout time.Duration
includedInstanceStatuses set.Set[string]
inclusionInstanceTags map[string][]string
exclusionInstanceTags map[string][]string
}
// ensure AwsRdsDiscoverer implements discovery.Discoverer at compile-time.
var _ discovery.Discoverer = (*AwsRdsDiscoverer)(nil)
// AwsRdsDiscovererOption represents a configuration option for an AwsRdsDiscoverer.
type AwsRdsDiscovererOption func(*AwsRdsDiscoverer)
// WithAwsEcsDiscovererDiscovererId is the AwsRdsDiscovererOption to set a non default discoverer id.
func WithAwsRdsDiscovererDiscovererId(discovererId string) AwsRdsDiscovererOption {
return func(rdsd *AwsRdsDiscoverer) {
rdsd.discovererId = discovererId
}
}
// WithAwsRdsDiscovererGetAccountIdTimeout is the AwsRdsDiscovererOption
// to set a non default timeout for getting the aws account id.
func WithAwsRdsDiscovererGetAccountIdTimeout(timeout time.Duration) AwsRdsDiscovererOption {
return func(rdsd *AwsRdsDiscoverer) {
rdsd.getAccountIdTimeout = timeout
}
}
// WithAwsRdsDiscovererIncludedInstanceStatuses is the AwsRdsDiscovererOption
// to set a non default list of statuses for instances to include in results.
func WithAwsRdsDiscovererIncludedInstanceStatuses(statuses ...string) AwsRdsDiscovererOption {
return func(rdsd *AwsRdsDiscoverer) {
lowercased := slice.Transform(statuses, func(s string) string { return strings.ToLower(s) })
rdsd.includedInstanceStatuses = set.New(lowercased...)
}
}
// WithAwsRdsDiscovererInclusionInstanceTags is the AwsRdsDiscovererOption
// to set the inclusion tags filter for instances to include in results.
func WithAwsRdsDiscovererInclusionInstanceTags(tags map[string][]string) AwsRdsDiscovererOption {
return func(rdsd *AwsRdsDiscoverer) {
rdsd.inclusionInstanceTags = tags
}
}
// WithAwsRdsDiscovererExclusionInstanceTags is the AwsRdsDiscovererOption
// to set the exclusion tags filter for instances to exclude in results.
func WithAwsRdsDiscovererExclusionInstanceTags(tags map[string][]string) AwsRdsDiscovererOption {
return func(rdsd *AwsRdsDiscoverer) {
rdsd.exclusionInstanceTags = tags
}
}
// NewAwsRdsDiscoverer returns a new AwsRdsDiscoverer, initialized with the given options.
func NewAwsRdsDiscoverer(cfg aws.Config, opts ...AwsRdsDiscovererOption) *AwsRdsDiscoverer {
rdsd := &AwsRdsDiscoverer{
cfg: cfg,
discovererId: defaultAwsRdsDiscovererDiscovererId,
getAccountIdTimeout: defaultAwsRdsDiscovererGetAccountIdTimeout,
includedInstanceStatuses: defaultAwsRdsDiscovererIncludedInstanceStatuses,
inclusionInstanceTags: nil,
exclusionInstanceTags: nil,
}
for _, opt := range opts {
opt(rdsd)
}
return rdsd
}
// Discover runs the AwsRdsDiscoverer.
func (rdsd *AwsRdsDiscoverer) Discover(ctx context.Context) *discovery.Result {
result := discovery.NewResult(rdsd.discovererId)
defer result.Done()
awsAccountId, err := utils.AwsAccountIdFromConfig(ctx, rdsd.cfg, rdsd.getAccountIdTimeout)
if err != nil {
result.AddError(fmt.Errorf("failed to get AWS account ID from AWS configuration: %w", err))
return result
}
// describe rds instances
rdsClient := rds.NewFromConfig(rdsd.cfg)
// TODO: new context with timeout for describe instances
describeDBInstancesOutput, err := rdsClient.DescribeDBInstances(ctx, &rds.DescribeDBInstancesInput{})
if err != nil {
result.AddError(fmt.Errorf("failed to describe rds instances: %w", err))
return result
}
// filter and build resources
for _, instance := range describeDBInstancesOutput.DBInstances {
// ignore instances with no status
if instance.DBInstanceStatus == nil {
continue // NOTE: this should emit a warning.
}
// ignore instances with un-included instance status
if !rdsd.includedInstanceStatuses.Has(aws.ToString(instance.DBInstanceStatus)) {
continue
}
// ignore rds db instances that don't satisfy tag conditions
if !evaluateRdsInstanceTags(
instance.TagList,
rdsd.inclusionInstanceTags,
rdsd.exclusionInstanceTags,
) {
continue
}
// build resource
awsBaseDetails := discovery.AwsBaseDetails{
AwsRegion: rdsd.cfg.Region,
AwsAccountId: awsAccountId,
AwsArn: aws.ToString(instance.DBInstanceArn),
}
tags := map[string]string{}
for _, t := range instance.TagList {
tags[aws.ToString(t.Key)] = aws.ToString(t.Value)
}
rdsInstanceDetails := &discovery.AwsRdsInstanceDetails{
AwsBaseDetails: awsBaseDetails,
Tags: tags,
DbInstanceIdentifier: aws.ToString(instance.DBInstanceIdentifier),
DbInstanceStatus: aws.ToString(instance.DBInstanceStatus),
Engine: aws.ToString(instance.Engine),
EngineVersion: aws.ToString(instance.EngineVersion),
}
if instance.DBSubnetGroup != nil {
rdsInstanceDetails.DBSubnetGroupName = aws.ToString(instance.DBSubnetGroup.DBSubnetGroupName)
rdsInstanceDetails.VpcId = aws.ToString(instance.DBSubnetGroup.VpcId)
} else {
rdsInstanceDetails.DBSubnetGroupName = ""
rdsInstanceDetails.VpcId = ""
}
if instance.Endpoint != nil {
rdsInstanceDetails.EndpointAddress = aws.ToString(instance.Endpoint.Address)
rdsInstanceDetails.EndpointPort = instance.Endpoint.Port
} else {
rdsInstanceDetails.EndpointAddress = ""
rdsInstanceDetails.EndpointPort = -1
}
result.AddResources(discovery.Resource{
ResourceType: discovery.ResourceTypeAwsRdsInstance,
AwsRdsInstanceDetails: rdsInstanceDetails,
})
}
return result
}
func evaluateRdsInstanceTags(
tags []types.Tag,
inclusion map[string][]string,
exclusion map[string][]string,
) bool {
included := (inclusion == nil)
excluded := false
if inclusion != nil {
for _, tag := range tags {
if utils.TagMatchesFilter(
aws.ToString(tag.Key),
aws.ToString(tag.Value),
inclusion,
) {
included = true
break
}
}
}
if exclusion != nil {
for _, tag := range tags {
if utils.TagMatchesFilter(
aws.ToString(tag.Key),
aws.ToString(tag.Value),
exclusion,
) {
excluded = true
break
}
}
}
return included && !excluded
}