Skip to content

Commit

Permalink
Loki: Implement custom /config handler (#4785)
Browse files Browse the repository at this point in the history
* Implement custom /config handler.

* Make `customConfigEndpointHandlerFn` public.
  • Loading branch information
DylanGuedes authored Nov 26, 2021
1 parent 11bf736 commit 347e3e3
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 4 deletions.
2 changes: 1 addition & 1 deletion cmd/loki/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,6 @@ func main() {

level.Info(util_log.Logger).Log("msg", "Starting Loki", "version", version.Info())

err = t.Run()
err = t.Run(loki.RunOpts{})
util_log.CheckFatal("running loki", err)
}
21 changes: 18 additions & 3 deletions pkg/loki/loki.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,23 @@ func newDefaultConfig() *Config {
return defaultConfig
}

// RunOpts configures custom behavior for running Loki.
type RunOpts struct {
// CustomConfigEndpointHandlerFn is the handlerFunc to be used by the /config endpoint.
// If empty, default handlerFunc will be used.
CustomConfigEndpointHandlerFn func(http.ResponseWriter, *http.Request)
}

func (t *Loki) bindConfigEndpoint(opts RunOpts) {
configEndpointHandlerFn := configHandler(t.Cfg, newDefaultConfig())
if opts.CustomConfigEndpointHandlerFn != nil {
configEndpointHandlerFn = opts.CustomConfigEndpointHandlerFn
}
t.Server.HTTP.Path("/config").Methods("GET").HandlerFunc(configEndpointHandlerFn)
}

// Run starts Loki running, and blocks until a Loki stops.
func (t *Loki) Run() error {
func (t *Loki) Run(opts RunOpts) error {
serviceMap, err := t.ModuleManager.InitModuleServices(t.Cfg.Target...)
if err != nil {
return err
Expand All @@ -306,8 +321,8 @@ func (t *Loki) Run() error {

grpc_health_v1.RegisterHealthServer(t.Server.GRPC, grpcutil.NewHealthCheck(sm))

// This adds a way to see the config and the changes compared to the defaults
t.Server.HTTP.Path("/config").Methods("GET").HandlerFunc(configHandler(t.Cfg, newDefaultConfig()))
// Config endpoint adds a way to see the config and the changes compared to the defaults.
t.bindConfigEndpoint(opts)

// Each component serves its version.
t.Server.HTTP.Path("/loki/api/v1/status/buildinfo").Methods("GET").HandlerFunc(versionHandler())
Expand Down
80 changes: 80 additions & 0 deletions pkg/loki/loki_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package loki
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -86,3 +89,80 @@ func TestLoki_isModuleEnabled(t1 *testing.T) {
})
}
}

func TestLoki_CustomRunOptsBehavior(t *testing.T) {
yamlConfig := `target: querier
server:
http_listen_port: 3100
common:
path_prefix: /tmp/loki
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
row_shards: 10
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h`

cfgWrapper, _, err := configWrapperFromYAML(t, yamlConfig, nil)
require.NoError(t, err)

loki, err := New(cfgWrapper.Config)
require.NoError(t, err)

lokiHealthCheck := func() error {
// wait for Loki HTTP server to be ready.
// retries at most 10 times (1 second in total) to avoid infinite loops when no timeout is set.
for i := 0; i < 10; i++ {
// waits until request to /ready doesn't error.
resp, err := http.DefaultClient.Get("http://localhost:3100/ready")
if err != nil {
time.Sleep(time.Millisecond * 200)
continue
}

// waits until /ready returns OK.
if resp.StatusCode != http.StatusOK {
time.Sleep(time.Millisecond * 200)
continue
}

// Loki is healthy.
return nil
}

return fmt.Errorf("loki HTTP not healthy")
}

customHandlerInvoked := false
customHandler := func(w http.ResponseWriter, _ *http.Request) {
customHandlerInvoked = true
_, err := w.Write([]byte("abc"))
require.NoError(t, err)
}

// Run Loki querier in a different go routine and with custom /config handler.
go func() {
err := loki.Run(RunOpts{CustomConfigEndpointHandlerFn: customHandler})
require.NoError(t, err)
}()

err = lokiHealthCheck()
require.NoError(t, err)

resp, err := http.DefaultClient.Get("http://localhost:3100/config")
require.NoError(t, err)

defer resp.Body.Close()

bBytes, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, string(bBytes), "abc")
assert.True(t, customHandlerInvoked)
}

0 comments on commit 347e3e3

Please sign in to comment.