diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0ce897f70..2f9eacb94b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ * [CHANGE] Renamed CLI flag from `--storage.trace.maintenance-cycle` to `--storage.trace.blocklist_poll`. This is a **breaking change** [#897](https://github.com/grafana/tempo/pull/897) (@mritunjaysharma394) * [CHANGE] update jsonnet alerts and recording rules to use `job_selectors` and `cluster_selectors` for configurable unique identifier labels [#935](https://github.com/grafana/tempo/pull/935) (@kevinschoonover) * [CHANGE] Modify generated tag keys in Vulture for easier filtering [#934](https://github.com/grafana/tempo/pull/934) (@zalegrala) +* [FEATURE] Add runtime config handler [#936](https://github.com/grafana/tempo/pull/936) (@mapno) ## v1.1.0 / 2021-08-26 * [CHANGE] Upgrade Cortex from v1.9.0 to v1.9.0-131-ga4bf10354 [#841](https://github.com/grafana/tempo/pull/841) (@aknuds1) diff --git a/cmd/tempo/app/modules.go b/cmd/tempo/app/modules.go index 659dc58506e..c9b990d9014 100644 --- a/cmd/tempo/app/modules.go +++ b/cmd/tempo/app/modules.go @@ -102,6 +102,8 @@ func (t *App) initOverrides() (services.Service, error) { } t.overrides = overrides + t.Server.HTTP.Handle("/runtime_config", overrides.Handler()) + return t.overrides, nil } @@ -282,8 +284,8 @@ func (t *App) setupModuleManager() error { deps := map[string][]string{ // Server: nil, - // Overrides: nil, // Store: nil, + Overrides: {Server}, MemberlistKV: {Server}, QueryFrontend: {Server}, Ring: {Server, MemberlistKV}, diff --git a/modules/overrides/limits.go b/modules/overrides/limits.go index dc294f6e779..7c4d49999e3 100644 --- a/modules/overrides/limits.go +++ b/modules/overrides/limits.go @@ -37,7 +37,7 @@ type Limits struct { // Compactor enforced limits. BlockRetention model.Duration `yaml:"block_retention" json:"block_retention"` - // Config for overrides, convenient if it goes here. + // Configuration for overrides, convenient if it goes here. PerTenantOverrideConfig string `yaml:"per_tenant_override_config" json:"per_tenant_override_config"` PerTenantOverridePeriod model.Duration `yaml:"per_tenant_override_period" json:"per_tenant_override_period"` } diff --git a/modules/overrides/overrides.go b/modules/overrides/overrides.go index a9a93724b57..2063863dfb2 100644 --- a/modules/overrides/overrides.go +++ b/modules/overrides/overrides.go @@ -4,8 +4,10 @@ import ( "context" "fmt" "io" + "net/http" "time" + "github.com/cortexproject/cortex/pkg/util" "github.com/cortexproject/cortex/pkg/util/log" "github.com/grafana/dskit/runtimeconfig" "github.com/grafana/dskit/services" @@ -13,10 +15,6 @@ import ( "gopkg.in/yaml.v2" ) -// TenantLimits is a function that returns limits for given tenant, or -// nil, if there are no tenant-specific limits. -type TenantLimits func(userID string) *Limits - const wildcardTenant = "*" // perTenantOverrides represents the overrides config file @@ -24,6 +22,15 @@ type perTenantOverrides struct { TenantLimits map[string]*Limits `yaml:"overrides"` } +// forUser returns limits for a given tenant, or nil if there are no tenant-specific limits. +func (o *perTenantOverrides) forUser(userID string) *Limits { + l, ok := o.TenantLimits[userID] + if !ok || l == nil { + return nil + } + return l +} + // loadPerTenantOverrides is of type runtimeconfig.Loader func loadPerTenantOverrides(r io.Reader) (interface{}, error) { var overrides = &perTenantOverrides{} @@ -37,13 +44,19 @@ func loadPerTenantOverrides(r io.Reader) (interface{}, error) { return overrides, nil } +// Config is a struct used to print the complete runtime config (defaults + overrides) +type Config struct { + Defaults *Limits `yaml:"defaults"` + PerTenantOverrides perTenantOverrides `yaml:",inline"` +} + // Overrides periodically fetch a set of per-user overrides, and provides convenience // functions for fetching the correct value. type Overrides struct { services.Service - defaultLimits *Limits - tenantLimits TenantLimits + defaultLimits *Limits + runtimeConfigMgr *runtimeconfig.Manager // Manager for subservices subservices *services.Manager @@ -55,7 +68,7 @@ type Overrides struct { // are defaulted to those values. As such, the last call to NewOverrides will // become the new global defaults. func NewOverrides(defaults Limits) (*Overrides, error) { - var tenantLimits TenantLimits + var manager *runtimeconfig.Manager subservices := []services.Service(nil) if defaults.PerTenantOverrideConfig != "" { @@ -68,13 +81,13 @@ func NewOverrides(defaults Limits) (*Overrides, error) { if err != nil { return nil, fmt.Errorf("failed to create runtime config manager %w", err) } - tenantLimits = tenantLimitsFromRuntimeConfig(runtimeCfgMgr) + manager = runtimeCfgMgr subservices = append(subservices, runtimeCfgMgr) } o := &Overrides{ - tenantLimits: tenantLimits, - defaultLimits: &defaults, + runtimeConfigMgr: manager, + defaultLimits: &defaults, } if len(subservices) > 0 { @@ -123,6 +136,65 @@ func (o *Overrides) stopping(_ error) error { return nil } +func (o *Overrides) tenantOverrides() *perTenantOverrides { + if o.runtimeConfigMgr == nil { + return nil + } + cfg, ok := o.runtimeConfigMgr.GetConfig().(*perTenantOverrides) + if !ok || cfg == nil { + return nil + } + + return cfg +} + +func (o *Overrides) Handler() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var tenantOverrides perTenantOverrides + if o.tenantOverrides() != nil { + tenantOverrides = *o.tenantOverrides() + } + var output interface{} + cfg := Config{ + Defaults: o.defaultLimits, + PerTenantOverrides: tenantOverrides, + } + switch r.URL.Query().Get("mode") { + case "diff": + // Default runtime config is just empty struct, but to make diff work, + // we set defaultLimits for every tenant that exists in runtime config. + defaultCfg := perTenantOverrides{TenantLimits: map[string]*Limits{}} + defaultCfg.TenantLimits = map[string]*Limits{} + for k, v := range tenantOverrides.TenantLimits { + if v != nil { + defaultCfg.TenantLimits[k] = o.defaultLimits + } + } + + cfgYaml, err := util.YAMLMarshalUnmarshal(cfg.PerTenantOverrides) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + defaultCfgYaml, err := util.YAMLMarshalUnmarshal(defaultCfg) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + output, err = util.DiffConfig(defaultCfgYaml, cfgYaml) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + default: + output = cfg + } + util.WriteYAMLResponse(w, output) + } +} + // IngestionRateStrategy returns whether the ingestion rate limit should be individually applied // to each distributor instance (local) or evenly shared across the cluster (global). func (o *Overrides) IngestionRateStrategy() string { @@ -153,45 +225,34 @@ func (o *Overrides) MaxSearchBytesPerTrace(userID string) int { return o.getOverridesForUser(userID).MaxSearchBytesPerTrace } -// IngestionRateSpans is the number of spans per second allowed for this tenant +// IngestionRateLimitBytes is the number of spans per second allowed for this tenant func (o *Overrides) IngestionRateLimitBytes(userID string) float64 { return float64(o.getOverridesForUser(userID).IngestionRateLimitBytes) } -// IngestionBurstSize is the burst size in spans allowed for this tenant +// IngestionBurstSizeBytes is the burst size in spans allowed for this tenant func (o *Overrides) IngestionBurstSizeBytes(userID string) int { return o.getOverridesForUser(userID).IngestionBurstSizeBytes } +// BlockRetention is the duration of the block retention for this tenant func (o *Overrides) BlockRetention(userID string) time.Duration { return time.Duration(o.getOverridesForUser(userID).BlockRetention) } func (o *Overrides) getOverridesForUser(userID string) *Limits { - if o.tenantLimits != nil { - l := o.tenantLimits(userID) + if tenantOverrides := o.tenantOverrides(); tenantOverrides != nil { + l := tenantOverrides.forUser(userID) if l != nil { return l } - l = o.tenantLimits(wildcardTenant) + l = tenantOverrides.forUser(wildcardTenant) if l != nil { return l } - } - return o.defaultLimits -} -func tenantLimitsFromRuntimeConfig(c *runtimeconfig.Manager) TenantLimits { - if c == nil { - return nil } - return func(userID string) *Limits { - cfg, ok := c.GetConfig().(*perTenantOverrides) - if !ok || cfg == nil { - return nil - } - return cfg.TenantLimits[userID] - } + return o.defaultLimits }