forked from grafana/loki
/
config_handler.go
132 lines (114 loc) · 3.12 KB
/
config_handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package loki
import (
"fmt"
"net/http"
"reflect"
"gopkg.in/yaml.v2"
)
func yamlMarshalUnmarshal(in interface{}) (map[interface{}]interface{}, error) {
yamlBytes, err := yaml.Marshal(in)
if err != nil {
return nil, err
}
object := make(map[interface{}]interface{})
if err := yaml.Unmarshal(yamlBytes, object); err != nil {
return nil, err
}
return object, nil
}
func diffConfig(defaultConfig, actualConfig map[interface{}]interface{}) (map[interface{}]interface{}, error) {
output := make(map[interface{}]interface{})
for key, value := range actualConfig {
defaultValue, ok := defaultConfig[key]
if !ok {
output[key] = value
continue
}
switch v := value.(type) {
case int:
defaultV, ok := defaultValue.(int)
if !ok || defaultV != v {
output[key] = v
}
case string:
defaultV, ok := defaultValue.(string)
if !ok || defaultV != v {
output[key] = v
}
case bool:
defaultV, ok := defaultValue.(bool)
if !ok || defaultV != v {
output[key] = v
}
case []interface{}:
defaultV, ok := defaultValue.([]interface{})
if !ok || !reflect.DeepEqual(defaultV, v) {
output[key] = v
}
case float64:
defaultV, ok := defaultValue.(float64)
if !ok || !reflect.DeepEqual(defaultV, v) {
output[key] = v
}
case map[interface{}]interface{}:
defaultV, ok := defaultValue.(map[interface{}]interface{})
if !ok {
output[key] = value
}
diff, err := diffConfig(defaultV, v)
if err != nil {
return nil, err
}
if len(diff) > 0 {
output[key] = diff
}
default:
return nil, fmt.Errorf("unsupported type %T", v)
}
}
return output, nil
}
func configHandler(actualCfg interface{}, defaultCfg interface{}) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var output interface{}
switch r.URL.Query().Get("mode") {
case "diff":
defaultCfgObj, err := yamlMarshalUnmarshal(defaultCfg)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
actualCfgObj, err := yamlMarshalUnmarshal(actualCfg)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
diff, err := diffConfig(defaultCfgObj, actualCfgObj)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
output = diff
case "defaults":
output = defaultCfg
default:
output = actualCfg
}
writeYAMLResponse(w, output)
}
}
// writeYAMLResponse writes some YAML as a HTTP response.
func writeYAMLResponse(w http.ResponseWriter, v interface{}) {
// There is not standardised content-type for YAML, text/plain ensures the
// YAML is displayed in the browser instead of offered as a download
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
data, err := yaml.Marshal(v)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// We ignore errors here, because we cannot do anything about them.
// Write will trigger sending Status code, so we cannot send a different status code afterwards.
// Also this isn't internal error, but error communicating with client.
_, _ = w.Write(data)
}