Skip to content

Commit 60b6c3b

Browse files
committed
Change overridesPerLimit to GaugeVec; test loadOverrides; add test FIXMEs
1 parent 70313aa commit 60b6c3b

File tree

4 files changed

+93
-42
lines changed

4 files changed

+93
-42
lines changed

ratelimits/limit.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ type limitRegistry struct {
358358
stats prometheus.Registerer
359359
overridesTimestamp prometheus.Gauge
360360
overridesErrors prometheus.Gauge
361-
overridesPerLimit map[Name]prometheus.Gauge
361+
overridesPerLimit prometheus.GaugeVec
362362

363363
logger blog.Logger
364364
}
@@ -396,27 +396,22 @@ func (l *limitRegistry) loadOverrides(ctx context.Context) error {
396396
return err
397397
}
398398

399-
// If it's an empty set, don't replace any current overrides.
400399
if len(newOverrides) < 1 {
401400
l.logger.Warning("loading overrides: no valid overrides")
401+
// If it's an empty set, don't replace any current overrides.
402402
return nil
403403
}
404404

405+
l.overrides = newOverrides
406+
407+
l.overridesTimestamp.SetToCurrentTime()
405408
newOverridesPerLimit := make(map[Name]float64)
406409
for _, override := range newOverrides {
407410
newOverridesPerLimit[override.Name]++
408411
}
409-
410-
l.overrides = newOverrides
411-
for name := range l.overridesPerLimit {
412-
count, ok := newOverridesPerLimit[name]
413-
if ok {
414-
l.overridesPerLimit[name].Set(count)
415-
} else {
416-
l.overridesPerLimit[name].Set(0)
417-
}
412+
for rlName, rlString := range nameToString {
413+
l.overridesPerLimit.WithLabelValues(rlString).Set(newOverridesPerLimit[rlName])
418414
}
419-
l.overridesTimestamp.SetToCurrentTime()
420415

421416
return nil
422417
}

ratelimits/limit_test.go

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package ratelimits
22

33
import (
4+
"context"
5+
"errors"
46
"net/netip"
57
"os"
68
"path/filepath"
79
"strings"
810
"testing"
911
"time"
1012

13+
"github.com/prometheus/client_golang/prometheus"
14+
io_prometheus_client "github.com/prometheus/client_model/go"
15+
1116
"github.com/letsencrypt/boulder/config"
1217
"github.com/letsencrypt/boulder/identifier"
18+
blog "github.com/letsencrypt/boulder/log"
19+
"github.com/letsencrypt/boulder/metrics"
1320
"github.com/letsencrypt/boulder/test"
1421
)
1522

@@ -26,11 +33,11 @@ func loadAndParseDefaultLimits(path string) (Limits, error) {
2633
return parseDefaultLimits(fromFile)
2734
}
2835

29-
// loadAndParseOverrideLimits is a helper that calls both loadOverrides and
30-
// parseOverrideLimits to handle a YAML file.
36+
// loadAndParseOverrideLimitsFromFile is a helper that calls both
37+
// loadOverridesFromFile and parseOverrideLimits to handle a YAML file.
3138
//
3239
// TODO(#7901): Update the tests to test these functions individually.
33-
func loadAndParseOverrideLimits(path string) (Limits, error) {
40+
func loadAndParseOverrideLimitsFromFile(path string) (Limits, error) {
3441
fromFile, err := loadOverridesFromFile(path)
3542
if err != nil {
3643
return nil, err
@@ -148,25 +155,25 @@ func TestValidateLimit(t *testing.T) {
148155
}
149156
}
150157

151-
func TestLoadAndParseOverrideLimits(t *testing.T) {
158+
func TestLoadAndParseOverrideLimitsFromFile(t *testing.T) {
152159
// Load a single valid override limit with Id formatted as 'enum:RegId'.
153-
l, err := loadAndParseOverrideLimits("testdata/working_override.yml")
160+
l, err := loadAndParseOverrideLimitsFromFile("testdata/working_override.yml")
154161
test.AssertNotError(t, err, "valid single override limit")
155162
expectKey := joinWithColon(NewRegistrationsPerIPAddress.EnumString(), "64.112.117.1")
156163
test.AssertEquals(t, l[expectKey].Burst, int64(40))
157164
test.AssertEquals(t, l[expectKey].Count, int64(40))
158165
test.AssertEquals(t, l[expectKey].Period.Duration, time.Second)
159166

160167
// Load single valid override limit with a 'domainOrCIDR' Id.
161-
l, err = loadAndParseOverrideLimits("testdata/working_override_regid_domainorcidr.yml")
168+
l, err = loadAndParseOverrideLimitsFromFile("testdata/working_override_regid_domainorcidr.yml")
162169
test.AssertNotError(t, err, "valid single override limit with Id of regId:domainOrCIDR")
163170
expectKey = joinWithColon(CertificatesPerDomain.EnumString(), "example.com")
164171
test.AssertEquals(t, l[expectKey].Burst, int64(40))
165172
test.AssertEquals(t, l[expectKey].Count, int64(40))
166173
test.AssertEquals(t, l[expectKey].Period.Duration, time.Second)
167174

168175
// Load multiple valid override limits with 'regId' Ids.
169-
l, err = loadAndParseOverrideLimits("testdata/working_overrides.yml")
176+
l, err = loadAndParseOverrideLimitsFromFile("testdata/working_overrides.yml")
170177
test.AssertNotError(t, err, "multiple valid override limits")
171178
expectKey1 := joinWithColon(NewRegistrationsPerIPAddress.EnumString(), "64.112.117.1")
172179
test.AssertEquals(t, l[expectKey1].Burst, int64(40))
@@ -190,7 +197,7 @@ func TestLoadAndParseOverrideLimits(t *testing.T) {
190197
identifier.NewDNS("example.com"),
191198
})
192199

193-
l, err = loadAndParseOverrideLimits("testdata/working_overrides_regid_fqdnset.yml")
200+
l, err = loadAndParseOverrideLimitsFromFile("testdata/working_overrides_regid_fqdnset.yml")
194201
test.AssertNotError(t, err, "multiple valid override limits with 'fqdnSet' Ids")
195202
test.AssertEquals(t, l[entryKey1].Burst, int64(40))
196203
test.AssertEquals(t, l[entryKey1].Count, int64(40))
@@ -206,46 +213,92 @@ func TestLoadAndParseOverrideLimits(t *testing.T) {
206213
test.AssertEquals(t, l[entryKey4].Period.Duration, time.Second*4)
207214

208215
// Path is empty string.
209-
_, err = loadAndParseOverrideLimits("")
216+
_, err = loadAndParseOverrideLimitsFromFile("")
210217
test.AssertError(t, err, "path is empty string")
211218
test.Assert(t, os.IsNotExist(err), "path is empty string")
212219

213220
// Path to file which does not exist.
214-
_, err = loadAndParseOverrideLimits("testdata/file_does_not_exist.yml")
221+
_, err = loadAndParseOverrideLimitsFromFile("testdata/file_does_not_exist.yml")
215222
test.AssertError(t, err, "a file that does not exist ")
216223
test.Assert(t, os.IsNotExist(err), "test file should not exist")
217224

218225
// Burst cannot be 0.
219-
_, err = loadAndParseOverrideLimits("testdata/busted_override_burst_0.yml")
226+
_, err = loadAndParseOverrideLimitsFromFile("testdata/busted_override_burst_0.yml")
220227
test.AssertError(t, err, "single override limit with burst=0")
221228
test.AssertContains(t, err.Error(), "invalid burst")
222229

223230
// Id cannot be empty.
224-
_, err = loadAndParseOverrideLimits("testdata/busted_override_empty_id.yml")
231+
_, err = loadAndParseOverrideLimitsFromFile("testdata/busted_override_empty_id.yml")
225232
test.AssertError(t, err, "single override limit with empty id")
226233
test.Assert(t, !os.IsNotExist(err), "test file should exist")
227234

228235
// Name cannot be empty.
229-
_, err = loadAndParseOverrideLimits("testdata/busted_override_empty_name.yml")
236+
_, err = loadAndParseOverrideLimitsFromFile("testdata/busted_override_empty_name.yml")
230237
test.AssertError(t, err, "single override limit with empty name")
231238
test.Assert(t, !os.IsNotExist(err), "test file should exist")
232239

233240
// Name must be a string representation of a valid Name enumeration.
234-
_, err = loadAndParseOverrideLimits("testdata/busted_override_invalid_name.yml")
241+
_, err = loadAndParseOverrideLimitsFromFile("testdata/busted_override_invalid_name.yml")
235242
test.AssertError(t, err, "single override limit with invalid name")
236243
test.Assert(t, !os.IsNotExist(err), "test file should exist")
237244

238245
// Multiple entries, second entry has a bad name.
239-
_, err = loadAndParseOverrideLimits("testdata/busted_overrides_second_entry_bad_name.yml")
246+
_, err = loadAndParseOverrideLimitsFromFile("testdata/busted_overrides_second_entry_bad_name.yml")
240247
test.AssertError(t, err, "multiple override limits, second entry is bad")
241248
test.Assert(t, !os.IsNotExist(err), "test file should exist")
242249

243250
// Multiple entries, third entry has id of "lol", instead of an IPv4 address.
244-
_, err = loadAndParseOverrideLimits("testdata/busted_overrides_third_entry_bad_id.yml")
251+
_, err = loadAndParseOverrideLimitsFromFile("testdata/busted_overrides_third_entry_bad_id.yml")
245252
test.AssertError(t, err, "multiple override limits, third entry has bad Id value")
246253
test.Assert(t, !os.IsNotExist(err), "test file should exist")
247254
}
248255

256+
func TestLoadOverrides(t *testing.T) {
257+
mockLog := blog.NewMock()
258+
259+
tb, err := NewTransactionBuilderFromFiles("../test/config-next/wfe2-ratelimit-defaults.yml", "../test/config-next/wfe2-ratelimit-overrides.yml", metrics.NoopRegisterer, mockLog)
260+
test.AssertNotError(t, err, "creating TransactionBuilder")
261+
overridesData, err := loadOverridesFromFile("../test/config-next/wfe2-ratelimit-overrides.yml")
262+
test.AssertNotError(t, err, "loading overrides from file")
263+
testOverrides, err := parseOverrideLimits(overridesData)
264+
test.AssertNotError(t, err, "parsing overrides")
265+
266+
test.AssertDeepEquals(t, tb.limitRegistry.overrides, testOverrides)
267+
268+
// FIXME: Test that testLimitRegistry.overridesPerLimit[name] values were set correctly, including that limits without overrides are set to 0.
269+
270+
var iom io_prometheus_client.Metric
271+
err = tb.limitRegistry.overridesTimestamp.Write(&iom)
272+
test.AssertNotError(t, err, "encoding overridesTimestamp metric")
273+
test.Assert(t, int64(iom.Gauge.GetValue()) >= time.Now().Unix()-5, "overridesTimestamp too old")
274+
275+
// A failure loading overrides should log and return an error, and not
276+
// overwrite existing overrides.
277+
mockLog.Clear()
278+
tb.limitRegistry.refreshOverrides = func(context.Context, prometheus.Gauge, blog.Logger) (Limits, error) {
279+
return nil, errors.New("mock failure")
280+
}
281+
err = tb.limitRegistry.loadOverrides(context.Background())
282+
test.AssertEquals(t, mockLog.GetAll()[0], "ERR: [AUDIT] loading overrides: mock failure")
283+
test.AssertError(t, err, "fail to load overrides")
284+
test.AssertDeepEquals(t, tb.limitRegistry.overrides, testOverrides)
285+
286+
// An empty set of overrides should log a warning, return nil, and not
287+
// overwrite existing overrides.
288+
mockLog.Clear()
289+
tb.limitRegistry.refreshOverrides = func(context.Context, prometheus.Gauge, blog.Logger) (Limits, error) {
290+
return Limits{}, nil
291+
}
292+
err = tb.limitRegistry.loadOverrides(context.Background())
293+
test.AssertEquals(t, mockLog.GetAll()[0], "WARNING: loading overrides: no valid overrides")
294+
test.AssertNotError(t, err, "load empty overrides")
295+
test.AssertDeepEquals(t, tb.limitRegistry.overrides, testOverrides)
296+
}
297+
298+
// FIXME: TestNewRefresher
299+
300+
// FIXME: TestHydrateOverrideLimit
301+
249302
func TestLoadAndParseDefaultLimits(t *testing.T) {
250303
// Load a single valid default limit.
251304
l, err := loadAndParseDefaultLimits("testdata/working_default.yml")

ratelimits/transaction.go

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -278,19 +278,16 @@ func NewTransactionBuilder(defaults LimitConfigs, overrides OverridesRefresher,
278278
})
279279
stats.MustRegister(overridesErrors)
280280

281-
overridesPerLimit := make(map[Name]prometheus.Gauge)
282-
i := Unknown + 1
283-
for i < Name(len(nameToString)) {
284-
overridesPerLimit[i] = prometheus.NewGauge(prometheus.GaugeOpts{
285-
Namespace: "ratelimits",
286-
Subsystem: "overrides",
287-
Name: "active",
288-
ConstLabels: prometheus.Labels{"limit": nameToString[i]},
289-
Help: "A gauge with the number of overrides per rate limit",
290-
})
291-
stats.MustRegister(overridesPerLimit[i])
292-
i++
293-
}
281+
overridesPerLimit := prometheus.NewGaugeVec(
282+
prometheus.GaugeOpts{
283+
Namespace: "ratelimits",
284+
Subsystem: "overrides",
285+
Name: "active",
286+
Help: "A gauge with the number of overrides, partitioned by rate limit",
287+
},
288+
[]string{"limit"},
289+
)
290+
stats.MustRegister(overridesPerLimit)
294291

295292
registry := &limitRegistry{
296293
defaults: regDefaults,
@@ -300,7 +297,7 @@ func NewTransactionBuilder(defaults LimitConfigs, overrides OverridesRefresher,
300297

301298
overridesTimestamp: overridesTimestamp,
302299
overridesErrors: overridesErrors,
303-
overridesPerLimit: overridesPerLimit,
300+
overridesPerLimit: *overridesPerLimit,
304301
}
305302

306303
// Load overrides, wrapped in a retry as a workaround to avoid depending on

ratelimits/transaction_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,10 @@ func TestNewTransactionBuilder(t *testing.T) {
228228
test.AssertEquals(t, newRegDefault.Burst, expectedBurst)
229229
test.AssertEquals(t, newRegDefault.Count, expectedCount)
230230
test.AssertEquals(t, newRegDefault.Period, expectedPeriod)
231+
232+
// FIXME: Test that metrics were set properly.
231233
}
234+
235+
// FIXME: TestNewTransactionBuilderFromDatabase
236+
237+
// FIXME: TestNewTransactionBuilderFromFiles

0 commit comments

Comments
 (0)