Skip to content

Commit ee9a2ac

Browse files
committed
Expose initial and effective config for debugging purposes
(Still incomplete, just the initial ideas)
1 parent c663ba4 commit ee9a2ac

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright Splunk, Inc.
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package configprovider
17+
18+
import (
19+
"net"
20+
"net/http"
21+
"os"
22+
"strings"
23+
24+
"go.uber.org/zap"
25+
"gopkg.in/yaml.v2"
26+
)
27+
28+
const (
29+
defaultEndpoint = "localhost:5555"
30+
)
31+
32+
type configServer struct {
33+
logger *zap.Logger
34+
initial map[string]interface{}
35+
effective map[string]interface{}
36+
server *http.Server
37+
doneCh chan struct{}
38+
}
39+
40+
func newConfigServer(logger *zap.Logger, initial, effective map[string]interface{}) *configServer {
41+
return &configServer{
42+
logger: logger,
43+
initial: initial,
44+
effective: effective,
45+
}
46+
}
47+
48+
func (cs *configServer) start() error {
49+
endpoint := defaultEndpoint
50+
if portOverride, ok := os.LookupEnv("SPLUNK_CONFIG_SERVER_PORT"); ok {
51+
if portOverride == "" {
52+
// If explicitly set to empty do not start the server.
53+
return nil
54+
}
55+
56+
endpoint = "localhost:" + portOverride
57+
}
58+
59+
listener, err := net.Listen("tcp", endpoint)
60+
if err != nil {
61+
return err
62+
}
63+
64+
mux := http.NewServeMux()
65+
66+
initialHandleFunc, err := cs.muxHandleFunc(cs.initial)
67+
if err != nil {
68+
return err
69+
}
70+
mux.HandleFunc("/debug/configz/initial", initialHandleFunc)
71+
72+
effectiveHandleFunc, err := cs.muxHandleFunc(simpleRedact(cs.effective))
73+
if err != nil {
74+
return err
75+
}
76+
mux.HandleFunc("/debug/configz/effective", effectiveHandleFunc)
77+
78+
cs.server = &http.Server{
79+
Handler: mux,
80+
}
81+
cs.doneCh = make(chan struct{})
82+
go func() {
83+
defer close(cs.doneCh)
84+
85+
if httpErr := cs.server.Serve(listener); httpErr != http.ErrServerClosed {
86+
cs.logger.Error("config server error", zap.Error(err))
87+
}
88+
}()
89+
90+
return nil
91+
}
92+
93+
func (cs *configServer) shutdown() error {
94+
var err error
95+
if cs.server != nil {
96+
err = cs.server.Close()
97+
// If launched wait for Serve goroutine exit.
98+
<-cs.doneCh
99+
}
100+
101+
return err
102+
}
103+
104+
func (cs *configServer) muxHandleFunc(config map[string]interface{}) (func(http.ResponseWriter, *http.Request), error) {
105+
configYAML, err := yaml.Marshal(config)
106+
if err != nil {
107+
return nil, err
108+
}
109+
110+
return func(writer http.ResponseWriter, request *http.Request) {
111+
_, _ = writer.Write(configYAML)
112+
}, nil
113+
}
114+
115+
func simpleRedact(config map[string]interface{}) map[string]interface{} {
116+
redactedConfig := make(map[string]interface{})
117+
for k, v := range config {
118+
switch value := v.(type) {
119+
case string:
120+
if shouldRedactKey(k) {
121+
v = "<redacted>"
122+
}
123+
case map[string]interface{}:
124+
v = simpleRedact(value)
125+
}
126+
127+
redactedConfig[k] = v
128+
}
129+
130+
return redactedConfig
131+
}
132+
133+
// shouldRedactKey applies a simple check to see if the contents of the given key
134+
// should be redacted or not.
135+
func shouldRedactKey(k string) bool {
136+
fragments := []string{"access", "auth", "credential", "creds", "login", "password", "pwd", "user"}
137+
138+
for _, fragment := range fragments {
139+
if strings.Contains(k, fragment) {
140+
return true
141+
}
142+
}
143+
144+
return false
145+
}

internal/configprovider/config_source_provider.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type errDuplicatedConfigSourceFactory struct{ error }
3030
type configSourceParserProvider struct {
3131
logger *zap.Logger
3232
csm *Manager
33+
configServer *configServer
3334
pp parserprovider.ParserProvider
3435
appStartInfo component.ApplicationStartInfo
3536
factories []Factory
@@ -69,6 +70,11 @@ func (c *configSourceParserProvider) Get() (*config.Parser, error) {
6970
return nil, err
7071
}
7172

73+
c.configServer = newConfigServer(c.logger, defaultParser.ToStringMap(), parser.ToStringMap())
74+
if err = c.configServer.start(); err != nil {
75+
return nil, err
76+
}
77+
7278
c.csm = csm
7379
return parser, nil
7480
}
@@ -82,6 +88,9 @@ func (c *configSourceParserProvider) WatchForUpdate() error {
8288
// Close ends the watch for updates and closes the parser provider and respective
8389
// config sources.
8490
func (c *configSourceParserProvider) Close(ctx context.Context) error {
91+
if c.configServer != nil {
92+
_ = c.configServer.shutdown()
93+
}
8594
return c.csm.Close(ctx)
8695
}
8796

0 commit comments

Comments
 (0)