Registry fails to start when a rate limiter leaves out warn_action, documented as optional

Summary

ApplyDefaults in configuration/configuration.go sets the rate limiter defaults (limit.period: second, action.warn_action: none and action.hard_action: none) while ranging over config.RateLimiter.Limiters by value, so each assignment goes to a copy of the limiter and is lost. No parsed configuration carries those defaults.

The rate limiter middleware validates every limiter and rejects the empty values, and NewApp returns that error. So the registry does not start when a limiter leaves out action.warn_action, which docs/configuration.md describes as optional with a default of none. The same happens when limit.period or action.hard_action is left out (the docs list those two as required).

The value loop came with 32b3f2e3 (docs: rate-limiting configuration settings, 2025-04-25), and the warn_action and hard_action defaults were added to it in d4e92b36 (feat(handlers): add rate-limiter middleware, 2025-05-08).

Steps to reproduce

  1. Start Redis:

    docker run -d --name registry-redis -p 127.0.0.1:16379:6379 redis:7.2-alpine
  2. Save this configuration as /tmp/config.yml. Its limiter is the one in the docs/configuration.md example, without warn_action and without log_only: false (the default):

    version: 0.1
    storage:
      filesystem:
        rootdirectory: /tmp/registry
    http:
      addr: 127.0.0.1:5000
      secret: registry-http-secret
    database:
      enabled: false
    redis:
      ratelimiter:
        enabled: true
        addr: 127.0.0.1:16379
    rate_limiter:
      enabled: true
      limiters:
        - name: global_rate_limit
          description: "Global IP rate limit"
          match:
            type: IP
          precedence: 10
          limit:
            rate: 5000
            period: "minute"
            burst: 8000
          action:
            warn_threshold: 0.7
            hard_action: "block"
  3. From a checkout of master, start the registry:

    go run ./cmd/registry serve /tmp/config.yml

What is the current bug behavior?

On master at 672bd753 (Go 1.27.3, Redis 7.2.16) the registry exits with status 1:

creating new registry instance: configuring application: failed to configure rate limiting: 1 error occurred:
	* action.warn_action must be one of: [none log]

A limiter that also leaves out period and hard_action fails with period must be one of: [second minute hour] and action.hard_action must be one of: [none log block] as well.

configuration.Parse alone shows the cause: after it, the limiter's Action.WarnAction is "", and so are Limit.Period and Action.HardAction when they are left out. The default cases of TestParseRateLimiter_Limiters_Limit_Period, TestParseRateLimiter_Limiters_Action_WarnAction and TestParseRateLimiter_Limiters_Action_HardAction assert those empty values.

What is the expected correct behavior?

The registry starts, and the limiter gets warn_action: none, as documented. A limiter that leaves out period or hard_action gets second or none, the values ApplyDefaults sets for them.

Relevant code

At 672bd753:

Possible fixes

Range by index and take a pointer to each element (limiter := &config.RateLimiter.Limiters[i]), and make the three tests expect the defaults. The Match.Type lowercasing in the same loop has never had an effect, and the middleware trims and lowercases the match type itself (ratelimiter.go#L148), so it can be dropped rather than made effective, which would change the parsed configuration that TestConfigSuite expects.