-
Notifications
You must be signed in to change notification settings - Fork 5
/
client.go
120 lines (102 loc) · 2.83 KB
/
client.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
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
)
var (
pluginName = "my-client-plugin"
// ClientRegisterer is the symbol the plugin loader will try to load. It must implement the RegisterClient interface
ClientRegisterer = registerer(pluginName)
errRegistererNotFound = fmt.Errorf("%s plugin disabled: config not found", pluginName)
)
type registerer string
func (r registerer) RegisterClients(f func(
name string,
handler func(context.Context, map[string]interface{}) (http.Handler, error),
)) {
f(string(r), r.registerClients)
}
func (r registerer) registerClients(_ context.Context, extra map[string]interface{}) (http.Handler, error) {
// check the cfg. If the modifier requires some configuration,
// it should be under the name of the plugin. E.g.:
/*
"extra_config":{
"plugin/http-client":{
"name":"my-client-plugin",
"my-client-plugin":{
"option": "/some-path"
}
}
}
*/
config, err := parseConfig(extra)
if err != nil {
return nil, err
}
logger.Debug(fmt.Sprintf("The plugin is now running %s", config.Opt))
// return the actual handler wrapping or your custom logic so it can be used as a replacement for the default http handler
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
resp, err := http.DefaultClient.Do(req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Copy headers, status codes, and body from the backend to the response writer
for k, hs := range resp.Header {
for _, h := range hs {
w.Header().Add(k, h)
}
}
w.WriteHeader(resp.StatusCode)
if resp.Body == nil {
return
}
io.Copy(w, resp.Body)
resp.Body.Close()
}), nil
}
type config struct {
Opt string `json:"option"`
}
// parseConfig parses the configuration marshaling and unmarshaling into a struct.
// you can also manually check for fields in the extra conffig map
func parseConfig(extra map[string]interface{}) (*config, error) {
if name, ok := extra["name"].(string); !ok || name != pluginName {
return nil, errRegistererNotFound
}
cfgRaw, ok := extra[pluginName].(map[string]interface{})
if !ok {
return nil, errRegistererNotFound
}
cfg := config{}
b, err := json.Marshal(cfgRaw)
if err != nil {
return nil, err
}
if err = json.Unmarshal(b, &cfg); err != nil {
return nil, err
}
return &cfg, nil
}
func main() {}
var logger Logger = nil
func (registerer) RegisterLogger(v interface{}) {
l, ok := v.(Logger)
if !ok {
return
}
logger = l
logger.Debug(fmt.Sprintf("[PLUGIN: %s] Example client plugin loaded", pluginName))
}
type Logger interface {
Debug(v ...interface{})
Info(v ...interface{})
Warning(v ...interface{})
Error(v ...interface{})
Critical(v ...interface{})
Fatal(v ...interface{})
}