|
9 | 9 | "strings" |
10 | 10 | "time" |
11 | 11 |
|
| 12 | + "github.com/mitchellh/mapstructure" |
12 | 13 | "github.com/sirupsen/logrus" |
13 | 14 | "gopkg.in/yaml.v3" |
14 | 15 |
|
@@ -254,65 +255,79 @@ func (h *Handlers) LokiBuildInfos() func(w http.ResponseWriter, r *http.Request) |
254 | 255 | } |
255 | 256 | } |
256 | 257 |
|
257 | | -func (h *Handlers) LokiConfig(param string) func(w http.ResponseWriter, r *http.Request) { |
258 | | - return func(w http.ResponseWriter, r *http.Request) { |
259 | | - if !h.Cfg.IsLokiEnabled() { |
260 | | - writeError(w, http.StatusBadRequest, "Loki is disabled") |
261 | | - return |
262 | | - } |
263 | | - lokiClient := newLokiClient(&h.Cfg.Loki, r.Header, true) |
264 | | - baseURL := strings.TrimRight(h.Cfg.Loki.GetStatusURL(), "/") |
| 258 | +func (h *Handlers) fetchLokiConfig(cl httpclient.Caller, output any) error { |
| 259 | + baseURL := strings.TrimRight(h.Cfg.Loki.GetStatusURL(), "/") |
265 | 260 |
|
266 | | - resp, code, err := executeLokiQuery(fmt.Sprintf("%s/%s", baseURL, "config"), lokiClient) |
267 | | - if err != nil { |
268 | | - writeError(w, code, err.Error()) |
269 | | - return |
270 | | - } |
| 261 | + resp, _, err := executeLokiQuery(fmt.Sprintf("%s/%s", baseURL, "config"), cl) |
| 262 | + if err != nil { |
| 263 | + return err |
| 264 | + } |
271 | 265 |
|
272 | | - cfg := make(map[string]interface{}) |
273 | | - err = yaml.Unmarshal(resp, &cfg) |
274 | | - if err != nil { |
275 | | - hlog.WithError(err).Errorf("cannot unmarshal, response was: %v", string(resp)) |
276 | | - writeError(w, code, err.Error()) |
277 | | - return |
278 | | - } |
279 | | - writeJSON(w, code, cfg[param]) |
| 266 | + cfg := make(map[string]interface{}) |
| 267 | + err = yaml.Unmarshal(resp, &cfg) |
| 268 | + if err != nil { |
| 269 | + hlog.WithError(err).Errorf("cannot unmarshal Loki config, response was: %v", string(resp)) |
| 270 | + return err |
| 271 | + } |
| 272 | + |
| 273 | + err = mapstructure.Decode(cfg, output) |
| 274 | + if err != nil { |
| 275 | + hlog.WithError(err).Errorf("cannot decode Loki config, response was: %v", cfg) |
| 276 | + return err |
280 | 277 | } |
| 278 | + |
| 279 | + return nil |
281 | 280 | } |
282 | 281 |
|
283 | | -func (h *Handlers) IngesterMaxChunkAge() func(w http.ResponseWriter, r *http.Request) { |
| 282 | +func (h *Handlers) LokiLimits() func(w http.ResponseWriter, r *http.Request) { |
284 | 283 | return func(w http.ResponseWriter, r *http.Request) { |
285 | 284 | if !h.Cfg.IsLokiEnabled() { |
286 | | - writeError(w, http.StatusBadRequest, "Loki is disabled") |
| 285 | + writeJSON(w, http.StatusNoContent, "Loki is disabled") |
287 | 286 | return |
288 | 287 | } |
289 | 288 | lokiClient := newLokiClient(&h.Cfg.Loki, r.Header, true) |
290 | | - baseURL := strings.TrimRight(h.Cfg.Loki.GetStatusURL(), "/") |
291 | | - |
292 | | - resp, code, err := executeLokiQuery(fmt.Sprintf("%s/%s", baseURL, "config"), lokiClient) |
| 289 | + limits, err := h.fetchLokiLimits(lokiClient) |
293 | 290 | if err != nil { |
294 | | - writeError(w, code, err.Error()) |
| 291 | + hlog.WithError(err).Error("cannot fetch Loki limits") |
| 292 | + writeError(w, http.StatusInternalServerError, err.Error()) |
295 | 293 | return |
296 | 294 | } |
| 295 | + writeJSON(w, http.StatusOK, limits) |
| 296 | + } |
| 297 | +} |
297 | 298 |
|
298 | | - cfg := make(map[string]interface{}) |
299 | | - err = yaml.Unmarshal(resp, &cfg) |
300 | | - if err != nil { |
301 | | - hlog.WithError(err).Errorf("cannot unmarshal, response was: %v", string(resp)) |
302 | | - writeError(w, code, err.Error()) |
303 | | - return |
304 | | - } |
| 299 | +func (h *Handlers) fetchLokiLimits(cl httpclient.Caller) (map[string]any, error) { |
| 300 | + type LimitsConfig struct { |
| 301 | + Limits map[string]any `mapstructure:"limits_config"` |
| 302 | + } |
| 303 | + limitsCfg := LimitsConfig{} |
| 304 | + if err := h.fetchLokiConfig(cl, &limitsCfg); err != nil { |
| 305 | + return nil, fmt.Errorf("Error when fetching Loki limits: %w", err) |
| 306 | + } |
| 307 | + return limitsCfg.Limits, nil |
| 308 | +} |
| 309 | + |
| 310 | +func (h *Handlers) fetchIngesterMaxChunkAge(cl httpclient.Caller) (time.Duration, error) { |
| 311 | + type ChunkAgeConfig struct { |
| 312 | + Ingester struct { |
| 313 | + MaxChunkAge string `mapstructure:"max_chunk_age"` |
| 314 | + } `mapstructure:"ingester"` |
| 315 | + } |
| 316 | + ageCfg := ChunkAgeConfig{} |
| 317 | + if err := h.fetchLokiConfig(cl, &ageCfg); err != nil { |
| 318 | + return 0, fmt.Errorf("error when fetching Loki ingester max chunk age: %w", err) |
| 319 | + } |
305 | 320 |
|
| 321 | + if ageCfg.Ingester.MaxChunkAge == "" { |
306 | 322 | // default max chunk age is 2h |
307 | 323 | // see https://grafana.com/docs/loki/latest/configure/#ingester |
308 | | - var maxChunkAge interface{} = "2h" |
309 | | - if cfg["ingester"] != nil { |
310 | | - ingester := cfg["ingester"].(map[string]interface{}) |
311 | | - if ingester["max_chunk_age"] != nil { |
312 | | - maxChunkAge = ingester["max_chunk_age"] |
313 | | - } |
314 | | - } |
| 324 | + return 2 * time.Hour, nil |
| 325 | + } |
315 | 326 |
|
316 | | - writeJSON(w, code, maxChunkAge) |
| 327 | + parsed, err := time.ParseDuration(ageCfg.Ingester.MaxChunkAge) |
| 328 | + if err != nil { |
| 329 | + return 0, fmt.Errorf("cannot parse max chunk age: %w", err) |
317 | 330 | } |
| 331 | + |
| 332 | + return parsed, nil |
318 | 333 | } |
0 commit comments