Skip to content

Commit d8d7795

Browse files
authored
Merge pull request #2 from mathieu-pousse/multi-region
2 parents baeff46 + 5982637 commit d8d7795

File tree

7 files changed

+183
-124
lines changed

7 files changed

+183
-124
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
# vendor/
66

77
bin/
8+
scaleway_exporter
89

9-
*.env
10+
*.env

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PLATFORM=local
22

33
REPOSITORY=yoannm/scaleway_exporter
4-
VERSION=0.1.0
4+
VERSION=0.2.0
55

66
export DOCKER_BUILDKIT=1
77
export COMPOSE_DOCKER_CLI_BUILD=1

README.md

+17-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,30 @@
22

33
Prometheus exporter for various metrics about your [Scaleway Elements](https://www.scaleway.com/en/elements/) loadbalancers and managed databases, written in Go.
44

5+
## How to
6+
7+
```
8+
$ export SCALEWAY_ACCESS_KEY=<access key goes here>
9+
$ export SCALEWAY_SECRET_KEY=<secret key goes here>
10+
$ ./scaleway_exporter
11+
level=info ts=2022-07-19T13:25:40.352520863Z caller=main.go:83 msg="Scaleway Region is set to ALL"
12+
level=info ts=2022-07-19T13:25:40.352550422Z caller=main.go:89 msg="starting scaleway_exporter" version= revision= buildDate= goVersion=go1.18.3
13+
level=info ts=2022-07-19T13:25:40.352691527Z caller=main.go:145 msg=listening addr=:9503
14+
```
15+
16+
By default, all the collectors are enabled (buckets, databases, loadbalancer) over all Scaleway regions.
17+
If needed, you can disable certain collections by adding the `disable-bucket-collector`, `disable-database-collector` or `disable-loadbalancer-collector` flags to the command line.
18+
You can also limit the scraped region by setting the environment variable `SCALEWAY_REGION=fr-par` for instance.
19+
520
## TODO
621

722
- [ ] Add more documentation
823
- [ ] Example prometheus rules
924
- [ ] Example grafana dashboard
1025
- [ ] Proper CI
11-
- [ ] Cross Region metrics pulling
26+
- [x] Cross Region metrics pulling
1227
- [ ] More metrics ? (Container Registry size is available)
13-
- [ ] Ability to filter the kind of product (only database for example)
28+
- [x] Ability to filter the kind of product (only database for example)
1429
- [ ] Register a new default port as it's using one from [another Scaleway Exporter](https://github.com/promhippie/scw_exporter) ? (see [prometheus documentation](https://github.com/prometheus/prometheus/wiki/Default-port-allocations))
1530

1631
## Acknowledgements

collector/bucket.go

+87-69
Original file line numberDiff line numberDiff line change
@@ -22,51 +22,61 @@ import (
2222

2323
// BucketCollector collects metrics about all buckets.
2424
type BucketCollector struct {
25-
logger log.Logger
26-
errors *prometheus.CounterVec
27-
client *scw.Client
28-
region *scw.Region
29-
s3Client *s3.S3
30-
timeout time.Duration
25+
logger log.Logger
26+
errors *prometheus.CounterVec
27+
endpoints []Endpoint
28+
timeout time.Duration
3129

3230
ObjectCount *prometheus.Desc
3331
Bandwidth *prometheus.Desc
3432
StorageUsageStandard *prometheus.Desc
3533
StorageUsageGlacier *prometheus.Desc
3634
}
3735

36+
type Endpoint struct {
37+
client *scw.Client
38+
region scw.Region
39+
s3Client *s3.S3
40+
}
41+
3842
// NewBucketCollector returns a new BucketCollector.
39-
func NewBucketCollector(logger log.Logger, errors *prometheus.CounterVec, client *scw.Client, timeout time.Duration) *BucketCollector {
43+
func NewBucketCollector(logger log.Logger, errors *prometheus.CounterVec, client *scw.Client, timeout time.Duration, regions []scw.Region) *BucketCollector {
4044
errors.WithLabelValues("bucket").Add(0)
4145

42-
region, _ := client.GetDefaultRegion()
43-
4446
accessKey, _ := client.GetAccessKey()
4547

4648
secretKey, _ := client.GetSecretKey()
4749

48-
newSession, err := session.NewSession(&aws.Config{
49-
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
50-
Region: aws.String(fmt.Sprint(region)),
51-
})
50+
endpoints := make([]Endpoint, len(regions))
5251

53-
if err != nil {
54-
_ = level.Error(logger).Log("msg", "can't create a S3 client", "err", err)
55-
os.Exit(1)
56-
}
52+
for i, region := range regions {
5753

58-
s3Client := s3.New(newSession, &aws.Config{
59-
Endpoint: aws.String("https://s3." + fmt.Sprint(region) + ".scw.cloud"),
60-
S3ForcePathStyle: aws.Bool(true),
61-
})
54+
newSession, err := session.NewSession(&aws.Config{
55+
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
56+
Region: aws.String(fmt.Sprint(region)),
57+
})
6258

59+
if err != nil {
60+
_ = level.Error(logger).Log("msg", "can't create a S3 client", "err", err)
61+
os.Exit(1)
62+
}
63+
64+
s3Client := s3.New(newSession, &aws.Config{
65+
Endpoint: aws.String("https://s3." + fmt.Sprint(region) + ".scw.cloud"),
66+
S3ForcePathStyle: aws.Bool(true),
67+
})
68+
69+
endpoints[i] = Endpoint{
70+
client: client,
71+
s3Client: s3Client,
72+
region: region,
73+
}
74+
}
6375
return &BucketCollector{
64-
region: &region,
65-
logger: logger,
66-
errors: errors,
67-
client: client,
68-
s3Client: s3Client,
69-
timeout: timeout,
76+
logger: logger,
77+
errors: errors,
78+
endpoints: endpoints,
79+
timeout: timeout,
7080

7181
ObjectCount: prometheus.NewDesc(
7282
"scaleway_s3_object_total",
@@ -142,13 +152,15 @@ type HandleSimpleMetricOptions struct {
142152
MetricName MetricName
143153
Desc *prometheus.Desc
144154
labels []string
155+
Endpoint Endpoint
145156
}
146157

147158
type HandleMultiMetricsOptions struct {
148159
Bucket string
149160
MetricName MetricName
150161
DescMatrix map[string]*prometheus.Desc
151162
labels []string
163+
Endpoint Endpoint
152164
}
153165

154166
// Collect is called by the Prometheus registry when collecting metrics.
@@ -157,69 +169,72 @@ func (c *BucketCollector) Collect(ch chan<- prometheus.Metric) {
157169
_, cancel := context.WithTimeout(context.Background(), c.timeout)
158170
defer cancel()
159171

160-
buckets, err := c.s3Client.ListBuckets(&s3.ListBucketsInput{})
172+
for _, endpoint := range c.endpoints {
161173

162-
if err != nil {
163-
c.errors.WithLabelValues("bucket").Add(1)
164-
_ = level.Warn(c.logger).Log("msg", "can't fetch the list of buckets", "err", err)
174+
buckets, err := endpoint.s3Client.ListBuckets(&s3.ListBucketsInput{})
165175

166-
return
167-
}
176+
if err != nil {
177+
c.errors.WithLabelValues("bucket").Add(1)
178+
_ = level.Warn(c.logger).Log("msg", "can't fetch the list of buckets", "err", err)
168179

169-
scwReq := &scw.ScalewayRequest{
170-
Method: "POST",
171-
Path: "/object-private/v1/regions/" + fmt.Sprint(c.region) + "/buckets-info/",
172-
}
180+
return
181+
}
173182

174-
var bucketNames []string
183+
scwReq := &scw.ScalewayRequest{
184+
Method: "POST",
185+
Path: "/object-private/v1/regions/" + fmt.Sprint(endpoint.region) + "/buckets-info/",
186+
}
175187

176-
for _, bucket := range buckets.Buckets {
188+
var bucketNames []string
177189

178-
bucketNames = append(bucketNames, *bucket.Name)
179-
}
190+
for _, bucket := range buckets.Buckets {
180191

181-
projectId := strings.Split(*buckets.Owner.ID, ":")[0]
192+
bucketNames = append(bucketNames, *bucket.Name)
193+
}
182194

183-
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("found %d buckets under projectID %s : %s", len(bucketNames), projectId, bucketNames))
195+
projectId := strings.Split(*buckets.Owner.ID, ":")[0]
184196

185-
err = scwReq.SetBody(&BucketInfoRequestBody{ProjectId: projectId, BucketsName: bucketNames})
197+
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("found %d buckets under projectID %s : %s", len(bucketNames), projectId, bucketNames))
186198

187-
if err != nil {
188-
c.errors.WithLabelValues("bucket").Add(1)
189-
_ = level.Warn(c.logger).Log("msg", "can't fetch details of buckets", "err", err)
199+
err = scwReq.SetBody(&BucketInfoRequestBody{ProjectId: projectId, BucketsName: bucketNames})
190200

191-
return
192-
}
201+
if err != nil {
202+
c.errors.WithLabelValues("bucket").Add(1)
203+
_ = level.Warn(c.logger).Log("msg", "can't fetch details of buckets", "err", err)
193204

194-
var response BucketInfoList
205+
return
206+
}
195207

196-
err = c.client.Do(scwReq, &response)
208+
var response BucketInfoList
197209

198-
if err != nil {
199-
c.errors.WithLabelValues("bucket").Add(1)
200-
_ = level.Warn(c.logger).Log("msg", "can't fetch details of buckets", "err", err)
210+
err = endpoint.client.Do(scwReq, &response)
201211

202-
return
203-
}
212+
if err != nil {
213+
c.errors.WithLabelValues("bucket").Add(1)
214+
_ = level.Warn(c.logger).Log("msg", "can't fetch details of buckets", "err", err)
204215

205-
var wg sync.WaitGroup
206-
defer wg.Wait()
216+
return
217+
}
207218

208-
for name, bucket := range response.Buckets {
219+
var wg sync.WaitGroup
220+
defer wg.Wait()
209221

210-
wg.Add(1)
222+
for name, bucket := range response.Buckets {
211223

212-
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("Fetching metrics for bucket : %s", name))
224+
wg.Add(1)
213225

214-
go c.FetchMetricsForBucket(&wg, ch, name, bucket)
226+
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("Fetching metrics for bucket : %s", name))
227+
228+
go c.FetchMetricsForBucket(&wg, ch, name, bucket, endpoint)
229+
}
215230
}
216231
}
217232

218-
func (c *BucketCollector) FetchMetricsForBucket(parentWg *sync.WaitGroup, ch chan<- prometheus.Metric, name string, bucket BucketInfo) {
233+
func (c *BucketCollector) FetchMetricsForBucket(parentWg *sync.WaitGroup, ch chan<- prometheus.Metric, name string, bucket BucketInfo, endpoint Endpoint) {
219234

220235
defer parentWg.Done()
221236

222-
labels := []string{name, fmt.Sprint(c.region), fmt.Sprint(bucket.IsPublic)}
237+
labels := []string{name, fmt.Sprint(endpoint.region), fmt.Sprint(bucket.IsPublic)}
223238

224239
// TODO check if it is possible to add bucket tag as labels
225240
//for _, tags := range instance.Tags {
@@ -236,20 +251,23 @@ func (c *BucketCollector) FetchMetricsForBucket(parentWg *sync.WaitGroup, ch cha
236251
MetricName: ObjectCount,
237252
labels: labels,
238253
Desc: c.ObjectCount,
254+
Endpoint: endpoint,
239255
})
240256

241257
go c.HandleSimpleMetric(&wg, ch, &HandleSimpleMetricOptions{
242258
Bucket: name,
243259
MetricName: BytesSent,
244260
labels: labels,
245261
Desc: c.Bandwidth,
262+
Endpoint: endpoint,
246263
})
247264

248265
go c.HandleMultiMetrics(&wg, ch, &HandleMultiMetricsOptions{
249266
Bucket: name,
250267
MetricName: StorageUsage,
251268
labels: labels,
252269
DescMatrix: map[string]*prometheus.Desc{"STANDARD": c.StorageUsageStandard, "GLACIER": c.StorageUsageGlacier},
270+
Endpoint: endpoint,
253271
})
254272
}
255273

@@ -259,7 +277,7 @@ func (c *BucketCollector) HandleSimpleMetric(parentWg *sync.WaitGroup, ch chan<-
259277

260278
var response Metric
261279

262-
err := c.FetchMetric(options.Bucket, options.MetricName, &response)
280+
err := c.FetchMetric(options.Bucket, options.MetricName, &response, options.Endpoint)
263281

264282
if err != nil {
265283

@@ -304,7 +322,7 @@ func (c *BucketCollector) HandleMultiMetrics(parentWg *sync.WaitGroup, ch chan<-
304322

305323
var response Metric
306324

307-
err := c.FetchMetric(options.Bucket, options.MetricName, &response)
325+
err := c.FetchMetric(options.Bucket, options.MetricName, &response, options.Endpoint)
308326

309327
if err != nil {
310328

@@ -355,7 +373,7 @@ func (c *BucketCollector) HandleMultiMetrics(parentWg *sync.WaitGroup, ch chan<-
355373
}
356374
}
357375

358-
func (c *BucketCollector) FetchMetric(Bucket string, MetricName MetricName, response *Metric) error {
376+
func (c *BucketCollector) FetchMetric(Bucket string, MetricName MetricName, response *Metric, endpoint Endpoint) error {
359377

360378
query := url.Values{}
361379

@@ -365,11 +383,11 @@ func (c *BucketCollector) FetchMetric(Bucket string, MetricName MetricName, resp
365383

366384
scwReq := &scw.ScalewayRequest{
367385
Method: "GET",
368-
Path: "/object-private/v1/regions/" + fmt.Sprint(c.region) + "/buckets/" + Bucket + "/metrics",
386+
Path: "/object-private/v1/regions/" + fmt.Sprint(endpoint.region) + "/buckets/" + Bucket + "/metrics",
369387
Query: query,
370388
}
371389

372-
err := c.client.Do(scwReq, &response)
390+
err := endpoint.client.Do(scwReq, &response)
373391

374392
if err != nil {
375393

0 commit comments

Comments
 (0)