...
1
16
17 package controlplane
18
19 import (
20 "fmt"
21 "net/url"
22 "os"
23
24 kerrors "k8s.io/apimachinery/pkg/util/errors"
25 "k8s.io/client-go/rest"
26 "sigs.k8s.io/controller-runtime/pkg/internal/testing/certs"
27 )
28
29
30
31 var NewTinyCA = certs.NewTinyCA
32
33
34
35
36
37 type ControlPlane struct {
38 APIServer *APIServer
39 Etcd *Etcd
40
41
42 KubectlPath string
43
44
45 defaultUserCfg *rest.Config
46 defaultUserKubectl *KubeCtl
47 }
48
49
50 func (f *ControlPlane) Start() (retErr error) {
51 if f.Etcd == nil {
52 f.Etcd = &Etcd{}
53 }
54 if err := f.Etcd.Start(); err != nil {
55 return err
56 }
57 defer func() {
58 if retErr != nil {
59 _ = f.Etcd.Stop()
60 }
61 }()
62
63 if f.APIServer == nil {
64 f.APIServer = &APIServer{}
65 }
66 f.APIServer.EtcdURL = f.Etcd.URL
67 if err := f.APIServer.Start(); err != nil {
68 return err
69 }
70 defer func() {
71 if retErr != nil {
72 _ = f.APIServer.Stop()
73 }
74 }()
75
76
77
78
79 user, err := f.AddUser(User{Name: "default", Groups: []string{"system:masters"}}, &rest.Config{})
80 if err != nil {
81 return fmt.Errorf("unable to provision the default (legacy) user: %w", err)
82 }
83 kubectl, err := user.Kubectl()
84 if err != nil {
85 return fmt.Errorf("unable to provision the default (legacy) kubeconfig: %w", err)
86 }
87 f.defaultUserCfg = user.Config()
88 f.defaultUserKubectl = kubectl
89 return nil
90 }
91
92
93 func (f *ControlPlane) Stop() error {
94 var errList []error
95
96 if f.APIServer != nil {
97 if err := f.APIServer.Stop(); err != nil {
98 errList = append(errList, err)
99 }
100 }
101
102 if f.Etcd != nil {
103 if err := f.Etcd.Stop(); err != nil {
104 errList = append(errList, err)
105 }
106 }
107
108 return kerrors.NewAggregate(errList)
109 }
110
111
112
113
114
115
116
117
118 func (f *ControlPlane) APIURL() *url.URL {
119 return f.APIServer.URL
120 }
121
122
123
124
125
126 func (f *ControlPlane) KubeCtl() *KubeCtl {
127 return f.defaultUserKubectl
128 }
129
130
131
132
133
134 func (f *ControlPlane) RESTClientConfig() (*rest.Config, error) {
135 return f.defaultUserCfg, nil
136 }
137
138
139
140
141
142
143
144 type AuthenticatedUser struct {
145
146 cfg *rest.Config
147
148
149 cfgIsComplete bool
150
151
152
153 plane *ControlPlane
154
155
156
157 kubectl *KubeCtl
158 }
159
160
161
162
163
164 func (u *AuthenticatedUser) Config() *rest.Config {
165
166
167
168 if u.cfgIsComplete {
169 return u.cfg
170 }
171 if len(u.plane.APIServer.SecureServing.CA) == 0 {
172 panic("the API server has not yet been started, please do that before accessing connection details")
173 }
174
175 u.cfg.CAData = u.plane.APIServer.SecureServing.CA
176 u.cfg.Host = u.plane.APIServer.SecureServing.URL("https", "/").String()
177 u.cfgIsComplete = true
178 return u.cfg
179 }
180
181
182
183
184 func (u AuthenticatedUser) KubeConfig() ([]byte, error) {
185
186
187
188
189 return KubeConfigFromREST(u.Config())
190 }
191
192
193
194
195
196 func (u *AuthenticatedUser) Kubectl() (*KubeCtl, error) {
197 if u.kubectl != nil {
198 return u.kubectl, nil
199 }
200 if len(u.plane.APIServer.CertDir) == 0 {
201 panic("the API server has not yet been started, please do that before accessing connection details")
202 }
203
204
205 out, err := os.CreateTemp(u.plane.APIServer.CertDir, "*.kubecfg")
206 if err != nil {
207 return nil, fmt.Errorf("unable to create file for kubeconfig: %w", err)
208 }
209 defer out.Close()
210 contents, err := KubeConfigFromREST(u.Config())
211 if err != nil {
212 return nil, err
213 }
214 if _, err := out.Write(contents); err != nil {
215 return nil, fmt.Errorf("unable to write kubeconfig to disk at %s: %w", out.Name(), err)
216 }
217 k := &KubeCtl{
218 Path: u.plane.KubectlPath,
219 }
220 k.Opts = append(k.Opts, fmt.Sprintf("--kubeconfig=%s", out.Name()))
221 u.kubectl = k
222 return k, nil
223 }
224
225
226
227
228
229
230
231
232
233
234 func (f *ControlPlane) AddUser(user User, baseConfig *rest.Config) (*AuthenticatedUser, error) {
235 if f.GetAPIServer().SecureServing.Authn == nil {
236 return nil, fmt.Errorf("no API server authentication is configured yet. The API server defaults one when Start is called, did you mean to use that?")
237 }
238
239 if baseConfig == nil {
240 baseConfig = &rest.Config{}
241 }
242 cfg, err := f.GetAPIServer().SecureServing.AddUser(user, baseConfig)
243 if err != nil {
244 return nil, err
245 }
246
247 return &AuthenticatedUser{
248 cfg: cfg,
249 plane: f,
250 }, nil
251 }
252
253
254 func (f *ControlPlane) GetAPIServer() *APIServer {
255 if f.APIServer == nil {
256 f.APIServer = &APIServer{}
257 }
258 return f.APIServer
259 }
260
View as plain text