Skip to content

Commit

Permalink
Expose initial and effective config for debugging purposes
Browse files Browse the repository at this point in the history
(Still incomplete, just the initial ideas)
  • Loading branch information
pjanotti committed Apr 23, 2021
1 parent c663ba4 commit ee9a2ac
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 0 deletions.
145 changes: 145 additions & 0 deletions internal/configprovider/config_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright Splunk, Inc.
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package configprovider

import (
"net"
"net/http"
"os"
"strings"

"go.uber.org/zap"
"gopkg.in/yaml.v2"
)

const (
defaultEndpoint = "localhost:5555"
)

type configServer struct {
logger *zap.Logger
initial map[string]interface{}
effective map[string]interface{}
server *http.Server
doneCh chan struct{}
}

func newConfigServer(logger *zap.Logger, initial, effective map[string]interface{}) *configServer {
return &configServer{
logger: logger,
initial: initial,
effective: effective,
}
}

func (cs *configServer) start() error {
endpoint := defaultEndpoint
if portOverride, ok := os.LookupEnv("SPLUNK_CONFIG_SERVER_PORT"); ok {
if portOverride == "" {
// If explicitly set to empty do not start the server.
return nil
}

endpoint = "localhost:" + portOverride
}

listener, err := net.Listen("tcp", endpoint)
if err != nil {
return err
}

mux := http.NewServeMux()

initialHandleFunc, err := cs.muxHandleFunc(cs.initial)
if err != nil {
return err
}
mux.HandleFunc("/debug/configz/initial", initialHandleFunc)

effectiveHandleFunc, err := cs.muxHandleFunc(simpleRedact(cs.effective))
if err != nil {
return err
}
mux.HandleFunc("/debug/configz/effective", effectiveHandleFunc)

cs.server = &http.Server{
Handler: mux,
}
cs.doneCh = make(chan struct{})
go func() {
defer close(cs.doneCh)

if httpErr := cs.server.Serve(listener); httpErr != http.ErrServerClosed {
cs.logger.Error("config server error", zap.Error(err))
}
}()

return nil
}

func (cs *configServer) shutdown() error {
var err error
if cs.server != nil {
err = cs.server.Close()
// If launched wait for Serve goroutine exit.
<-cs.doneCh
}

return err
}

func (cs *configServer) muxHandleFunc(config map[string]interface{}) (func(http.ResponseWriter, *http.Request), error) {
configYAML, err := yaml.Marshal(config)
if err != nil {
return nil, err
}

return func(writer http.ResponseWriter, request *http.Request) {
_, _ = writer.Write(configYAML)
}, nil
}

func simpleRedact(config map[string]interface{}) map[string]interface{} {
redactedConfig := make(map[string]interface{})
for k, v := range config {
switch value := v.(type) {
case string:
if shouldRedactKey(k) {
v = "<redacted>"
}
case map[string]interface{}:
v = simpleRedact(value)
}

redactedConfig[k] = v
}

return redactedConfig
}

// shouldRedactKey applies a simple check to see if the contents of the given key
// should be redacted or not.
func shouldRedactKey(k string) bool {
fragments := []string{"access", "auth", "credential", "creds", "login", "password", "pwd", "user"}

for _, fragment := range fragments {
if strings.Contains(k, fragment) {
return true
}
}

return false
}
9 changes: 9 additions & 0 deletions internal/configprovider/config_source_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type errDuplicatedConfigSourceFactory struct{ error }
type configSourceParserProvider struct {
logger *zap.Logger
csm *Manager
configServer *configServer
pp parserprovider.ParserProvider
appStartInfo component.ApplicationStartInfo
factories []Factory
Expand Down Expand Up @@ -69,6 +70,11 @@ func (c *configSourceParserProvider) Get() (*config.Parser, error) {
return nil, err
}

c.configServer = newConfigServer(c.logger, defaultParser.ToStringMap(), parser.ToStringMap())
if err = c.configServer.start(); err != nil {
return nil, err
}

c.csm = csm
return parser, nil
}
Expand All @@ -82,6 +88,9 @@ func (c *configSourceParserProvider) WatchForUpdate() error {
// Close ends the watch for updates and closes the parser provider and respective
// config sources.
func (c *configSourceParserProvider) Close(ctx context.Context) error {
if c.configServer != nil {
_ = c.configServer.shutdown()
}
return c.csm.Close(ctx)
}

Expand Down

0 comments on commit ee9a2ac

Please sign in to comment.