1
16
17 package config
18
19 import (
20 "bytes"
21 "net"
22 "os"
23 "strconv"
24 "strings"
25
26 "github.com/pkg/errors"
27
28 v1 "k8s.io/api/core/v1"
29 "k8s.io/apimachinery/pkg/runtime"
30 "k8s.io/apimachinery/pkg/runtime/schema"
31 netutil "k8s.io/apimachinery/pkg/util/net"
32 bootstraputil "k8s.io/cluster-bootstrap/token/util"
33 nodeutil "k8s.io/component-helpers/node/util"
34 "k8s.io/klog/v2"
35 netutils "k8s.io/utils/net"
36
37 bootstraptokenv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1"
38 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
39 kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
40 kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
41 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
42 "k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
43 kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
44 kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
45 "k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
46 kubeadmruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime"
47 )
48
49 var (
50
51 PlaceholderToken = bootstraptokenv1.BootstrapToken{
52 Token: &bootstraptokenv1.BootstrapTokenString{
53 ID: "abcdef",
54 Secret: "0123456789abcdef",
55 },
56 }
57 )
58
59
60 func SetInitDynamicDefaults(cfg *kubeadmapi.InitConfiguration, skipCRIDetect bool) error {
61 if err := SetBootstrapTokensDynamicDefaults(&cfg.BootstrapTokens); err != nil {
62 return err
63 }
64 if err := SetNodeRegistrationDynamicDefaults(&cfg.NodeRegistration, true, skipCRIDetect); err != nil {
65 return err
66 }
67 if err := SetAPIEndpointDynamicDefaults(&cfg.LocalAPIEndpoint); err != nil {
68 return err
69 }
70 return SetClusterDynamicDefaults(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, &cfg.NodeRegistration)
71 }
72
73
74 func SetBootstrapTokensDynamicDefaults(cfg *[]bootstraptokenv1.BootstrapToken) error {
75
76
77
78
79
80 for i, bt := range *cfg {
81 if bt.Token != nil && len(bt.Token.String()) > 0 {
82 continue
83 }
84
85 tokenStr, err := bootstraputil.GenerateBootstrapToken()
86 if err != nil {
87 return errors.Wrap(err, "couldn't generate random token")
88 }
89 token, err := bootstraptokenv1.NewBootstrapTokenString(tokenStr)
90 if err != nil {
91 return err
92 }
93 (*cfg)[i].Token = token
94 }
95
96 return nil
97 }
98
99
100 func SetNodeRegistrationDynamicDefaults(cfg *kubeadmapi.NodeRegistrationOptions, controlPlaneTaint, skipCRIDetect bool) error {
101 var err error
102 cfg.Name, err = nodeutil.GetHostname(cfg.Name)
103 if err != nil {
104 return err
105 }
106
107
108 if controlPlaneTaint && cfg.Taints == nil {
109 cfg.Taints = []v1.Taint{kubeadmconstants.ControlPlaneTaint}
110 }
111
112 if cfg.CRISocket == "" {
113 if skipCRIDetect {
114 klog.V(4).Infof("skip CRI socket detection, fill with the default CRI socket %s", kubeadmconstants.DefaultCRISocket)
115 cfg.CRISocket = kubeadmconstants.DefaultCRISocket
116 return nil
117 }
118 cfg.CRISocket, err = kubeadmruntime.DetectCRISocket()
119 if err != nil {
120 return err
121 }
122 klog.V(1).Infof("detected and using CRI socket: %s", cfg.CRISocket)
123 } else {
124 if !strings.HasPrefix(cfg.CRISocket, kubeadmapiv1.DefaultContainerRuntimeURLScheme) {
125 klog.Warningf("Usage of CRI endpoints without URL scheme is deprecated and can cause kubelet errors "+
126 "in the future. Automatically prepending scheme %q to the \"criSocket\" with value %q. "+
127 "Please update your configuration!", kubeadmapiv1.DefaultContainerRuntimeURLScheme, cfg.CRISocket)
128 cfg.CRISocket = kubeadmapiv1.DefaultContainerRuntimeURLScheme + "://" + cfg.CRISocket
129 }
130 }
131
132 return nil
133 }
134
135
136 func SetAPIEndpointDynamicDefaults(cfg *kubeadmapi.APIEndpoint) error {
137
138 addressIP := netutils.ParseIPSloppy(cfg.AdvertiseAddress)
139 if addressIP == nil && cfg.AdvertiseAddress != "" {
140 return errors.Errorf("couldn't use \"%s\" as \"apiserver-advertise-address\", must be ipv4 or ipv6 address", cfg.AdvertiseAddress)
141 }
142
143
144
145 if addressIP.IsLoopback() {
146 loopbackIP, err := netutil.ChooseBindAddressForInterface(netutil.LoopbackInterfaceName)
147 if err != nil {
148 return err
149 }
150 if loopbackIP != nil {
151 klog.V(4).Infof("Found active IP %v on loopback interface", loopbackIP.String())
152 cfg.AdvertiseAddress = loopbackIP.String()
153 return nil
154 }
155 return errors.New("unable to resolve link-local addresses")
156 }
157
158
159
160 ip, err := ChooseAPIServerBindAddress(addressIP)
161 if err != nil {
162 return err
163 }
164 cfg.AdvertiseAddress = ip.String()
165
166 return nil
167 }
168
169
170 func SetClusterDynamicDefaults(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint *kubeadmapi.APIEndpoint, nodeRegOpts *kubeadmapi.NodeRegistrationOptions) error {
171
172 componentconfigs.Default(cfg, localAPIEndpoint, nodeRegOpts)
173
174
175 if err := NormalizeKubernetesVersion(cfg); err != nil {
176 return err
177 }
178
179
180
181
182 if cfg.ControlPlaneEndpoint != "" {
183 host, port, err := kubeadmutil.ParseHostPort(cfg.ControlPlaneEndpoint)
184 if err != nil {
185 return err
186 }
187 if port == "" {
188 cfg.ControlPlaneEndpoint = net.JoinHostPort(host, strconv.FormatInt(int64(localAPIEndpoint.BindPort), 10))
189 }
190 }
191
192
193 LowercaseSANs(cfg.APIServer.CertSANs)
194 return nil
195 }
196
197
198 func DefaultedStaticInitConfiguration() (*kubeadmapi.InitConfiguration, error) {
199 versionedInitCfg := &kubeadmapiv1.InitConfiguration{
200 LocalAPIEndpoint: kubeadmapiv1.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
201 BootstrapTokens: []bootstraptokenv1.BootstrapToken{PlaceholderToken},
202 NodeRegistration: kubeadmapiv1.NodeRegistrationOptions{
203 CRISocket: kubeadmconstants.DefaultCRISocket,
204 Name: "node",
205 },
206 }
207 versionedClusterCfg := &kubeadmapiv1.ClusterConfiguration{
208 KubernetesVersion: kubeadmconstants.CurrentKubernetesVersion.String(),
209 }
210
211 internalcfg := &kubeadmapi.InitConfiguration{}
212
213
214
215 kubeadmscheme.Scheme.Default(versionedInitCfg)
216 if err := kubeadmscheme.Scheme.Convert(versionedInitCfg, internalcfg, nil); err != nil {
217 return nil, err
218 }
219
220 kubeadmscheme.Scheme.Default(versionedClusterCfg)
221 if err := kubeadmscheme.Scheme.Convert(versionedClusterCfg, &internalcfg.ClusterConfiguration, nil); err != nil {
222 return nil, err
223 }
224
225
226 componentconfigs.Default(&internalcfg.ClusterConfiguration, &internalcfg.LocalAPIEndpoint, &internalcfg.NodeRegistration)
227
228 return internalcfg, nil
229 }
230
231
232 func DefaultedInitConfiguration(versionedInitCfg *kubeadmapiv1.InitConfiguration, versionedClusterCfg *kubeadmapiv1.ClusterConfiguration, opts LoadOrDefaultConfigurationOptions) (*kubeadmapi.InitConfiguration, error) {
233 internalcfg := &kubeadmapi.InitConfiguration{}
234
235
236
237 kubeadmscheme.Scheme.Default(versionedInitCfg)
238 if err := kubeadmscheme.Scheme.Convert(versionedInitCfg, internalcfg, nil); err != nil {
239 return nil, err
240 }
241
242 kubeadmscheme.Scheme.Default(versionedClusterCfg)
243 if err := kubeadmscheme.Scheme.Convert(versionedClusterCfg, &internalcfg.ClusterConfiguration, nil); err != nil {
244 return nil, err
245 }
246
247
248 if err := SetInitDynamicDefaults(internalcfg, opts.SkipCRIDetect); err != nil {
249 return nil, err
250 }
251
252 if err := validation.ValidateInitConfiguration(internalcfg).ToAggregate(); err != nil {
253 return nil, err
254 }
255 return internalcfg, nil
256 }
257
258
259 func LoadInitConfigurationFromFile(cfgPath string, opts LoadOrDefaultConfigurationOptions) (*kubeadmapi.InitConfiguration, error) {
260 klog.V(1).Infof("loading configuration from %q", cfgPath)
261
262 b, err := os.ReadFile(cfgPath)
263 if err != nil {
264 return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
265 }
266
267 return BytesToInitConfiguration(b, opts.SkipCRIDetect)
268 }
269
270
271
272
273
274
275 func LoadOrDefaultInitConfiguration(cfgPath string, versionedInitCfg *kubeadmapiv1.InitConfiguration, versionedClusterCfg *kubeadmapiv1.ClusterConfiguration, opts LoadOrDefaultConfigurationOptions) (*kubeadmapi.InitConfiguration, error) {
276 var (
277 config *kubeadmapi.InitConfiguration
278 err error
279 )
280 if cfgPath != "" {
281
282 config, err = LoadInitConfigurationFromFile(cfgPath, opts)
283 } else {
284 config, err = DefaultedInitConfiguration(versionedInitCfg, versionedClusterCfg, opts)
285 }
286 if err == nil {
287 prepareStaticVariables(config)
288 }
289 return config, err
290 }
291
292
293
294
295
296 func BytesToInitConfiguration(b []byte, skipCRIDetect bool) (*kubeadmapi.InitConfiguration, error) {
297 gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
298 if err != nil {
299 return nil, err
300 }
301
302 return documentMapToInitConfiguration(gvkmap, false, false, false, skipCRIDetect)
303 }
304
305
306 func documentMapToInitConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated, allowExperimental, strictErrors, skipCRIDetect bool) (*kubeadmapi.InitConfiguration, error) {
307 var initcfg *kubeadmapi.InitConfiguration
308 var clustercfg *kubeadmapi.ClusterConfiguration
309
310 for gvk, fileContent := range gvkmap {
311
312 if err := validateSupportedVersion(gvk.GroupVersion(), allowDeprecated, allowExperimental); err != nil {
313 return nil, err
314 }
315
316
317 if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme, componentconfigs.Scheme}, gvk, fileContent); err != nil {
318 if !strictErrors {
319 klog.Warning(err.Error())
320 } else {
321 return nil, err
322 }
323 }
324
325 if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvk) {
326
327 initcfg = &kubeadmapi.InitConfiguration{}
328
329
330 if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), fileContent, initcfg); err != nil {
331 return nil, err
332 }
333 continue
334 }
335 if kubeadmutil.GroupVersionKindsHasClusterConfiguration(gvk) {
336
337 clustercfg = &kubeadmapi.ClusterConfiguration{}
338
339
340 if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), fileContent, clustercfg); err != nil {
341 return nil, err
342 }
343 continue
344 }
345
346
347 if !componentconfigs.Scheme.IsGroupRegistered(gvk.Group) {
348 klog.Warningf("[config] WARNING: Ignored YAML document with GroupVersionKind %v\n", gvk)
349 }
350 }
351
352
353 if initcfg == nil && clustercfg == nil {
354 return nil, errors.New("no InitConfiguration or ClusterConfiguration kind was found in the YAML file")
355 }
356
357
358 if initcfg == nil {
359 extinitcfg := &kubeadmapiv1.InitConfiguration{}
360 kubeadmscheme.Scheme.Default(extinitcfg)
361
362 initcfg = &kubeadmapi.InitConfiguration{}
363 if err := kubeadmscheme.Scheme.Convert(extinitcfg, initcfg, nil); err != nil {
364 return nil, err
365 }
366 }
367
368 if clustercfg != nil {
369 initcfg.ClusterConfiguration = *clustercfg
370 } else {
371
372 extclustercfg := &kubeadmapiv1.ClusterConfiguration{}
373 kubeadmscheme.Scheme.Default(extclustercfg)
374 if err := kubeadmscheme.Scheme.Convert(extclustercfg, &initcfg.ClusterConfiguration, nil); err != nil {
375 return nil, err
376 }
377 }
378
379
380 if err := componentconfigs.FetchFromDocumentMap(&initcfg.ClusterConfiguration, gvkmap); err != nil {
381 return nil, err
382 }
383
384
385 if err := SetInitDynamicDefaults(initcfg, skipCRIDetect); err != nil {
386 return nil, err
387 }
388
389
390 if err := validation.ValidateInitConfiguration(initcfg).ToAggregate(); err != nil {
391 return nil, err
392 }
393
394 return initcfg, nil
395 }
396
397
398
399 func MarshalInitConfigurationToBytes(cfg *kubeadmapi.InitConfiguration, gv schema.GroupVersion) ([]byte, error) {
400 initbytes, err := kubeadmutil.MarshalToYamlForCodecs(cfg, gv, kubeadmscheme.Codecs)
401 if err != nil {
402 return []byte{}, err
403 }
404 allFiles := [][]byte{initbytes}
405
406
407
408 if gv.Version != runtime.APIVersionInternal {
409 clusterbytes, err := kubeadmutil.MarshalToYamlForCodecs(&cfg.ClusterConfiguration, gv, kubeadmscheme.Codecs)
410 if err != nil {
411 return []byte{}, err
412 }
413 allFiles = append(allFiles, clusterbytes)
414 }
415 return bytes.Join(allFiles, []byte(kubeadmconstants.YAMLDocumentSeparator)), nil
416 }
417
View as plain text