Documentation ¶
Overview ¶
Package rpcquota is a wrapper around LUCI quota for ResultDB.
It provides a LUCI module that configures and initializes the LUCI quota library with a quotaconfig configservice. That module also installs a UnaryServerInterceptor to automatically check quota for incoming ResultDB and Recorder RPCs.
It also automatically falls back to deducting quota from a wildcard policy if there's no policy specific to that user. See UpdateUserQuota for details.
Index ¶
Constants ¶
const ( // Disabled: Quota is fully off. No quotaconfig loaded, no quota // amounts deducted in redis, no UnaryServerInterceptor registered. RPCQuotaModeDisabled = "disabled" // Track only: deduct quota but don't fail requests. Intended for dark // launch. RPCQuotaModeTrackOnly = "track-only" // RPC quota fully enabled. Requests that exceed quota will be failed. RPCQuotaModeEnforce = "enforce" )
Variables ¶
var ErrInvalidOptions = errors.New("Invalid options")
var ModuleName = module.RegisterName("go.chromium.org/luci/resultdb/internal/rpcquota")
Functions ¶
func NewModule ¶
func NewModule(opts *ModuleOptions) module.Module
NewModule returns a module.Module for the rpcquota library initialized from the given *ModuleOptions.
func NewModuleFromFlags ¶
NewModuleFromFlags returns a module.Module for the rpcquota library which can be initialized from command line flags.
func UpdateUserQuota ¶
func UpdateUserQuota(ctx context.Context, resourcePrefix string, cost int64, service string, method string) (err error)
UpdateUserQuota is a ResultDB-specific wrapper around quota.UpdateQuota.
Its main purpose is to add a 2-stage policy lookup: first look for a user-specific policy, and iff none found fall back to a default user policy. This is used to control quota granted to different service accounts.
For example, given a config with 2 policies like:
- name: RPC/hourly/${user}, resources: 100, replenishment: 10 - name: RPC/hourly/user/alice/example.com, resources: 200, replenishment: 10
The outcome of UpdateUserQuota(ctx, "RPC/hourly/", 1) will depend on the currently authenticated identity. If it is 'user:alice@example.com', the quota will be debited from a quota bucket called "RPC/hourly/user/alice/example.com" (initialized with 200 resources). However for 'user:bob@example.com' it would be debited from a bucket called "RPC/hourly/user/bob/example.com" (initialized with 100).
In this way, we can set a default policy to apply to any user, and configure overrides for specific users that need more (or less). This includes allowing overrides for "anonymous:anonymous". Note that policy names must only use characters in the set [A-Za-z0-9-_/] (aside from the special substring ${user}), so user strings in policies should have other characters replaced with /.
Note that unlike quota.UpdateQuota this only supports updating a single resource at a time; this is sufficent for ResultDB's needs, and keeps the fallback logic simple.
This also handles transforming ErrInsufficientQuota into a ResourceExhausted appstatus.
- TODO: automatic opts.RequestID..?
Types ¶
type ModuleOptions ¶
type ModuleOptions struct { // RPCQuotaMode determines whether quota is off, active (but not // enforced), or active and enforced. RPCQuotaMode string }
func (*ModuleOptions) Register ¶
func (o *ModuleOptions) Register(fs *flag.FlagSet)