/
controller.go
171 lines (139 loc) · 4.48 KB
/
controller.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package kubernetes
import (
"path/filepath"
"regexp"
"strings"
"sync"
"time"
"github.com/homedepot/go-clouddriver/internal/kubernetes/cached/disk"
"github.com/homedepot/go-clouddriver/internal/kubernetes/cached/memory"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
)
var (
useDiskCache bool
)
//go:generate counterfeiter . Controller
// Controller holds the ability to generate a new
// dynamic kubernetes client.
type Controller interface {
NewClient(*rest.Config) (Client, error)
NewClientset(*rest.Config) (Clientset, error)
}
// NewController returns an instance of Controller.
func NewController() Controller {
return &controller{}
}
type controller struct{}
// UseDiskCache sets the controller to generate clients that use
// disk cache instead of memory cache for discovery and HTTP responses.
func UseDiskCache() {
useDiskCache = true
}
// NewClient returns a new dynamic Kubernetes client. By default it returns
// a client that uses in-memory cache store, unless `useDiskCache` is set
// to true. This is where the client stores and references its discovery of
// the Kubernetes API server.
func (c *controller) NewClient(config *rest.Config) (Client, error) {
var (
client Client
err error
)
if useDiskCache {
client, err = newClientWithDefaultDiskCache(config)
} else {
client, err = newClientWithMemoryCache(config)
}
return client, err
}
// NewClientset returns a new kubernetes Clientset wrapper.
func (c *controller) NewClientset(config *rest.Config) (Clientset, error) {
cs, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
return &clientset{
clientset: cs,
}, nil
}
const (
// Default cache directory.
cacheDir = "/var/kube/cache"
defaultTimeout = 180 * time.Second
ttl = 10 * time.Minute
)
var (
mux sync.Mutex
memCaches = map[string]*memory.Cache{}
)
func newClientWithMemoryCache(config *rest.Config) (Client, error) {
// If the timeout is not set, set it to the default timeout.
if config.Timeout == 0 {
config.Timeout = defaultTimeout
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, err
}
mc, err := memCacheClientForConfig(config)
if err != nil {
return nil, err
}
mapper := restmapper.NewDeferredDiscoveryRESTMapper(mc)
kubeClient := &client{
c: dynamicClient,
config: config,
mapper: mapper,
}
return kubeClient, nil
}
func memCacheClientForConfig(inConfig *rest.Config) (memory.CachedDiscoveryClient, error) {
mux.Lock()
defer mux.Unlock()
config := inConfig
if _, ok := memCaches[config.Host]; !ok {
memCaches[config.Host] = memory.NewCache(ttl)
}
memCache := memCaches[config.Host]
return memCache.NewClientForConfig(config)
}
func newClientWithDefaultDiskCache(config *rest.Config) (Client, error) {
// If the timeout is not set, set it to the default timeout.
if config.Timeout == 0 {
config.Timeout = defaultTimeout
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, err
}
// Some code to define this take from
// https://github.com/kubernetes/cli-runtime/blob/master/pkg/genericclioptions/config_flags.go#L215
httpCacheDir := filepath.Join(cacheDir, "http")
discoveryCacheDir := computeDiscoverCacheDir(filepath.Join(cacheDir, "discovery"), config.Host)
// DiscoveryClient queries API server about the resources
cdc, err := disk.NewCachedDiscoveryClientForConfig(config, discoveryCacheDir, httpCacheDir, ttl)
if err != nil {
return nil, err
}
mapper := restmapper.NewDeferredDiscoveryRESTMapper(cdc)
kubeClient := &client{
c: dynamicClient,
config: config,
mapper: mapper,
}
return kubeClient, nil
}
// overlyCautiousIllegalFileCharacters matches characters that *might* not be supported.
// Windows is really restrictive, so this is really restrictive.
var overlyCautiousIllegalFileCharacters = regexp.MustCompile(`[^(\w/\.)]`)
// computeDiscoverCacheDir takes the parentDir and the host and comes up with a "usually non-colliding" name.
func computeDiscoverCacheDir(parentDir, host string) string {
// strip the optional scheme from host if its there:
schemelessHost := strings.Replace(strings.Replace(host, "https://", "", 1), "http://", "", 1)
// now do a simple collapse of non-AZ09 characters. Collisions are possible but unlikely.
// Even if we do collide the problem is short lived
safeHost := overlyCautiousIllegalFileCharacters.ReplaceAllString(schemelessHost, "_")
return filepath.Join(parentDir, safeHost)
}