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  }