github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/cortex/runtime_config.go (about) 1 package cortex 2 3 import ( 4 "errors" 5 "io" 6 "net/http" 7 8 "github.com/grafana/dskit/kv" 9 "github.com/grafana/dskit/runtimeconfig" 10 "gopkg.in/yaml.v2" 11 12 "github.com/cortexproject/cortex/pkg/ingester" 13 "github.com/cortexproject/cortex/pkg/util" 14 "github.com/cortexproject/cortex/pkg/util/validation" 15 ) 16 17 var ( 18 errMultipleDocuments = errors.New("the provided runtime configuration contains multiple documents") 19 ) 20 21 // runtimeConfigValues are values that can be reloaded from configuration file while Cortex is running. 22 // Reloading is done by runtime_config.Manager, which also keeps the currently loaded config. 23 // These values are then pushed to the components that are interested in them. 24 type runtimeConfigValues struct { 25 TenantLimits map[string]*validation.Limits `yaml:"overrides"` 26 27 Multi kv.MultiRuntimeConfig `yaml:"multi_kv_config"` 28 29 IngesterChunkStreaming *bool `yaml:"ingester_stream_chunks_when_using_blocks"` 30 31 IngesterLimits *ingester.InstanceLimits `yaml:"ingester_limits"` 32 } 33 34 // runtimeConfigTenantLimits provides per-tenant limit overrides based on a runtimeconfig.Manager 35 // that reads limits from a configuration file on disk and periodically reloads them. 36 type runtimeConfigTenantLimits struct { 37 manager *runtimeconfig.Manager 38 } 39 40 // newTenantLimits creates a new validation.TenantLimits that loads per-tenant limit overrides from 41 // a runtimeconfig.Manager 42 func newTenantLimits(manager *runtimeconfig.Manager) validation.TenantLimits { 43 return &runtimeConfigTenantLimits{ 44 manager: manager, 45 } 46 } 47 48 func (l *runtimeConfigTenantLimits) ByUserID(userID string) *validation.Limits { 49 return l.AllByUserID()[userID] 50 } 51 52 func (l *runtimeConfigTenantLimits) AllByUserID() map[string]*validation.Limits { 53 cfg, ok := l.manager.GetConfig().(*runtimeConfigValues) 54 if cfg != nil && ok { 55 return cfg.TenantLimits 56 } 57 58 return nil 59 } 60 61 func loadRuntimeConfig(r io.Reader) (interface{}, error) { 62 var overrides = &runtimeConfigValues{} 63 64 decoder := yaml.NewDecoder(r) 65 decoder.SetStrict(true) 66 67 // Decode the first document. An empty document (EOF) is OK. 68 if err := decoder.Decode(&overrides); err != nil && !errors.Is(err, io.EOF) { 69 return nil, err 70 } 71 72 // Ensure the provided YAML config is not composed of multiple documents, 73 if err := decoder.Decode(&runtimeConfigValues{}); !errors.Is(err, io.EOF) { 74 return nil, errMultipleDocuments 75 } 76 77 return overrides, nil 78 } 79 80 func multiClientRuntimeConfigChannel(manager *runtimeconfig.Manager) func() <-chan kv.MultiRuntimeConfig { 81 if manager == nil { 82 return nil 83 } 84 // returns function that can be used in MultiConfig.ConfigProvider 85 return func() <-chan kv.MultiRuntimeConfig { 86 outCh := make(chan kv.MultiRuntimeConfig, 1) 87 88 // push initial config to the channel 89 val := manager.GetConfig() 90 if cfg, ok := val.(*runtimeConfigValues); ok && cfg != nil { 91 outCh <- cfg.Multi 92 } 93 94 ch := manager.CreateListenerChannel(1) 95 go func() { 96 for val := range ch { 97 if cfg, ok := val.(*runtimeConfigValues); ok && cfg != nil { 98 outCh <- cfg.Multi 99 } 100 } 101 }() 102 103 return outCh 104 } 105 } 106 107 func ingesterChunkStreaming(manager *runtimeconfig.Manager) func() ingester.QueryStreamType { 108 if manager == nil { 109 return nil 110 } 111 112 return func() ingester.QueryStreamType { 113 val := manager.GetConfig() 114 if cfg, ok := val.(*runtimeConfigValues); ok && cfg != nil { 115 if cfg.IngesterChunkStreaming == nil { 116 return ingester.QueryStreamDefault 117 } 118 119 if *cfg.IngesterChunkStreaming { 120 return ingester.QueryStreamChunks 121 } 122 return ingester.QueryStreamSamples 123 } 124 125 return ingester.QueryStreamDefault 126 } 127 } 128 129 func ingesterInstanceLimits(manager *runtimeconfig.Manager) func() *ingester.InstanceLimits { 130 if manager == nil { 131 return nil 132 } 133 134 return func() *ingester.InstanceLimits { 135 val := manager.GetConfig() 136 if cfg, ok := val.(*runtimeConfigValues); ok && cfg != nil { 137 return cfg.IngesterLimits 138 } 139 return nil 140 } 141 } 142 143 func runtimeConfigHandler(runtimeCfgManager *runtimeconfig.Manager, defaultLimits validation.Limits) http.HandlerFunc { 144 return func(w http.ResponseWriter, r *http.Request) { 145 cfg, ok := runtimeCfgManager.GetConfig().(*runtimeConfigValues) 146 if !ok || cfg == nil { 147 util.WriteTextResponse(w, "runtime config file doesn't exist") 148 return 149 } 150 151 var output interface{} 152 switch r.URL.Query().Get("mode") { 153 case "diff": 154 // Default runtime config is just empty struct, but to make diff work, 155 // we set defaultLimits for every tenant that exists in runtime config. 156 defaultCfg := runtimeConfigValues{} 157 defaultCfg.TenantLimits = map[string]*validation.Limits{} 158 for k, v := range cfg.TenantLimits { 159 if v != nil { 160 defaultCfg.TenantLimits[k] = &defaultLimits 161 } 162 } 163 164 cfgYaml, err := util.YAMLMarshalUnmarshal(cfg) 165 if err != nil { 166 http.Error(w, err.Error(), http.StatusInternalServerError) 167 return 168 } 169 170 defaultCfgYaml, err := util.YAMLMarshalUnmarshal(defaultCfg) 171 if err != nil { 172 http.Error(w, err.Error(), http.StatusInternalServerError) 173 return 174 } 175 176 output, err = util.DiffConfig(defaultCfgYaml, cfgYaml) 177 if err != nil { 178 http.Error(w, err.Error(), http.StatusInternalServerError) 179 return 180 } 181 182 default: 183 output = cfg 184 } 185 util.WriteYAMLResponse(w, output) 186 } 187 }