k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/componentconfigs/configset.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package componentconfigs 18 19 import ( 20 "github.com/pkg/errors" 21 22 apierrors "k8s.io/apimachinery/pkg/api/errors" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/apimachinery/pkg/runtime/schema" 26 "k8s.io/apimachinery/pkg/util/validation/field" 27 clientset "k8s.io/client-go/kubernetes" 28 "k8s.io/klog/v2" 29 30 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" 31 outputapiv1alpha3 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha3" 32 kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" 33 "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" 34 "k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict" 35 ) 36 37 // handler is a package internal type that handles component config factory and common functionality. 38 // Every component config group should have exactly one static instance of handler. 39 type handler struct { 40 // GroupVersion holds this handler's group name and preferred version 41 GroupVersion schema.GroupVersion 42 43 // AddToScheme points to a func that should add the GV types to a schema 44 AddToScheme func(*runtime.Scheme) error 45 46 // CreateEmpty returns an empty kubeadmapi.ComponentConfig (not even defaulted) 47 CreateEmpty func() kubeadmapi.ComponentConfig 48 49 // fromCluster should load the component config from a config map on the cluster. 50 // Don't use this directly! Use FromCluster instead! 51 fromCluster func(*handler, clientset.Interface, *kubeadmapi.ClusterConfiguration) (kubeadmapi.ComponentConfig, error) 52 } 53 54 // FromDocumentMap looks in the document map for documents with this handler's group. 55 // If such are found a new component config is instantiated and the documents are loaded into it. 56 // No error is returned if no documents are found. 57 func (h *handler) FromDocumentMap(docmap kubeadmapi.DocumentMap) (kubeadmapi.ComponentConfig, error) { 58 for gvk := range docmap { 59 if gvk.Group == h.GroupVersion.Group { 60 cfg := h.CreateEmpty() 61 if err := cfg.Unmarshal(docmap); err != nil { 62 return nil, err 63 } 64 // consider all successfully loaded configs from a document map as user supplied 65 cfg.SetUserSupplied(true) 66 return cfg, nil 67 } 68 } 69 return nil, nil 70 } 71 72 // fromConfigMap is an utility function, which will load the value of a key of a config map and use h.FromDocumentMap() to perform the parsing 73 // This is an utility func. Used by the component config support implementations. Don't use it outside of that context. 74 func (h *handler) fromConfigMap(client clientset.Interface, cmName, cmKey string, mustExist bool) (kubeadmapi.ComponentConfig, error) { 75 configMap, err := apiclient.GetConfigMapWithShortRetry(client, metav1.NamespaceSystem, cmName) 76 if err != nil { 77 if !mustExist && (apierrors.IsNotFound(err) || apierrors.IsForbidden(err)) { 78 klog.Warningf("Warning: No %s config is loaded. Continuing without it: %v", h.GroupVersion, err) 79 return nil, nil 80 } 81 return nil, err 82 } 83 84 configData, ok := configMap.Data[cmKey] 85 if !ok { 86 return nil, errors.Errorf("unexpected error when reading %s ConfigMap: %s key value pair missing", cmName, cmKey) 87 } 88 89 gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(configData)) 90 if err != nil { 91 return nil, err 92 } 93 94 // If the checksum comes up neatly we assume the config was generated 95 generatedConfig := VerifyConfigMapSignature(configMap) 96 97 componentCfg, err := h.FromDocumentMap(gvkmap) 98 if err != nil { 99 // If the config was generated and we get UnsupportedConfigVersionError, we skip loading it. 100 // This will force us to use the generated default current version (effectively regenerating the config with the current version). 101 if _, ok := err.(*UnsupportedConfigVersionError); ok && generatedConfig { 102 return nil, nil 103 } 104 return nil, err 105 } 106 107 if componentCfg != nil { 108 componentCfg.SetUserSupplied(!generatedConfig) 109 } 110 111 return componentCfg, nil 112 } 113 114 // FromCluster loads a component from a config map in the cluster 115 func (h *handler) FromCluster(clientset clientset.Interface, clusterCfg *kubeadmapi.ClusterConfiguration) (kubeadmapi.ComponentConfig, error) { 116 return h.fromCluster(h, clientset, clusterCfg) 117 } 118 119 // known holds the known component config handlers. Add new component configs here. 120 var known = []*handler{ 121 &kubeProxyHandler, 122 &kubeletHandler, 123 } 124 125 // configBase is the base type for all component config implementations 126 type configBase struct { 127 // GroupVersion holds the supported GroupVersion for the inheriting config 128 GroupVersion schema.GroupVersion 129 130 // userSupplied tells us if the config is user supplied (invalid checksum) or not 131 userSupplied bool 132 } 133 134 func (cb *configBase) IsUserSupplied() bool { 135 return cb.userSupplied 136 } 137 138 func (cb *configBase) SetUserSupplied(userSupplied bool) { 139 cb.userSupplied = userSupplied 140 } 141 142 func (cb *configBase) DeepCopyInto(other *configBase) { 143 *other = *cb 144 } 145 146 func cloneBytes(in []byte) []byte { 147 out := make([]byte, len(in)) 148 copy(out, in) 149 return out 150 } 151 152 // Marshal is an utility function, used by the component config support implementations to marshal a runtime.Object to YAML with the 153 // correct group and version 154 func (cb *configBase) Marshal(object runtime.Object) ([]byte, error) { 155 return kubeadmutil.MarshalToYamlForCodecs(object, cb.GroupVersion, Codecs) 156 } 157 158 // Unmarshal attempts to unmarshal a runtime.Object from a document map. If no object is found, no error is returned. 159 // If a matching group is found, but no matching version an error is returned indicating that users should do manual conversion. 160 func (cb *configBase) Unmarshal(from kubeadmapi.DocumentMap, into runtime.Object) error { 161 for gvk, yaml := range from { 162 // If this is a different group, we ignore it 163 if gvk.Group != cb.GroupVersion.Group { 164 continue 165 } 166 167 if gvk.Version != cb.GroupVersion.Version { 168 return &UnsupportedConfigVersionError{ 169 OldVersion: gvk.GroupVersion(), 170 CurrentVersion: cb.GroupVersion, 171 Document: cloneBytes(yaml), 172 } 173 } 174 175 // Print warnings for strict errors 176 if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{Scheme}, gvk, yaml); err != nil { 177 klog.Warning(err.Error()) 178 } 179 180 // As long as we support only component configs with a single kind, this is allowed 181 return runtime.DecodeInto(Codecs.UniversalDecoder(), yaml, into) 182 } 183 184 return nil 185 } 186 187 // ensureInitializedComponentConfigs is an utility func to initialize the ComponentConfigMap in ClusterConfiguration prior to possible writes to it 188 func ensureInitializedComponentConfigs(clusterCfg *kubeadmapi.ClusterConfiguration) { 189 if clusterCfg.ComponentConfigs == nil { 190 clusterCfg.ComponentConfigs = kubeadmapi.ComponentConfigMap{} 191 } 192 } 193 194 // Default sets up defaulted component configs in the supplied ClusterConfiguration 195 func Default(clusterCfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint *kubeadmapi.APIEndpoint, nodeRegOpts *kubeadmapi.NodeRegistrationOptions) { 196 ensureInitializedComponentConfigs(clusterCfg) 197 198 for _, handler := range known { 199 // If the component config exists, simply default it. Otherwise, create it before defaulting. 200 group := handler.GroupVersion.Group 201 if componentCfg, ok := clusterCfg.ComponentConfigs[group]; ok { 202 componentCfg.Default(clusterCfg, localAPIEndpoint, nodeRegOpts) 203 } else { 204 componentCfg := handler.CreateEmpty() 205 componentCfg.Default(clusterCfg, localAPIEndpoint, nodeRegOpts) 206 clusterCfg.ComponentConfigs[group] = componentCfg 207 } 208 } 209 } 210 211 // FetchFromCluster attempts to fetch all known component configs from their config maps and store them in the supplied ClusterConfiguration 212 func FetchFromCluster(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) error { 213 ensureInitializedComponentConfigs(clusterCfg) 214 215 for _, handler := range known { 216 componentCfg, err := handler.FromCluster(client, clusterCfg) 217 if err != nil { 218 return err 219 } 220 221 if componentCfg != nil { 222 clusterCfg.ComponentConfigs[handler.GroupVersion.Group] = componentCfg 223 } 224 } 225 226 return nil 227 } 228 229 // FetchFromDocumentMap attempts to load all known component configs from a document map into the supplied ClusterConfiguration 230 func FetchFromDocumentMap(clusterCfg *kubeadmapi.ClusterConfiguration, docmap kubeadmapi.DocumentMap) error { 231 ensureInitializedComponentConfigs(clusterCfg) 232 233 for _, handler := range known { 234 componentCfg, err := handler.FromDocumentMap(docmap) 235 if err != nil { 236 return err 237 } 238 239 if componentCfg != nil { 240 clusterCfg.ComponentConfigs[handler.GroupVersion.Group] = componentCfg 241 } 242 } 243 244 return nil 245 } 246 247 // GetVersionStates returns a slice of ComponentConfigVersionState structs 248 // describing all supported component config groups that were identified on the cluster 249 func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) ([]outputapiv1alpha3.ComponentConfigVersionState, error) { 250 // We don't want to modify clusterCfg so we make a working deep copy of it. 251 // Also, we don't want the defaulted component configs so we get rid of them. 252 scratchClusterCfg := clusterCfg.DeepCopy() 253 scratchClusterCfg.ComponentConfigs = kubeadmapi.ComponentConfigMap{} 254 255 err := FetchFromCluster(scratchClusterCfg, client) 256 if err != nil { 257 // This seems to be a genuine error so we end here 258 return nil, err 259 } 260 261 results := []outputapiv1alpha3.ComponentConfigVersionState{} 262 for _, handler := range known { 263 group := handler.GroupVersion.Group 264 if _, ok := scratchClusterCfg.ComponentConfigs[group]; ok { 265 // Normally loaded component config. No manual upgrade required on behalf of users. 266 results = append(results, outputapiv1alpha3.ComponentConfigVersionState{ 267 Group: group, 268 CurrentVersion: handler.GroupVersion.Version, // Currently kubeadm supports only one version per API 269 PreferredVersion: handler.GroupVersion.Version, // group so we can get away with these being the same 270 }) 271 } else { 272 // This config was either not present (user did not install an addon) or the config was unsupported kubeadm 273 // generated one and is therefore skipped so we can automatically re-generate it (no action required on 274 // behalf of the user). 275 results = append(results, outputapiv1alpha3.ComponentConfigVersionState{ 276 Group: group, 277 PreferredVersion: handler.GroupVersion.Version, 278 }) 279 } 280 } 281 282 return results, nil 283 } 284 285 // Validate is a placeholder for performing a validation on an already loaded component configs in a ClusterConfiguration 286 // TODO: investigate if the function can be repurposed for validating component config via CLI 287 func Validate(clusterCfg *kubeadmapi.ClusterConfiguration) field.ErrorList { 288 return field.ErrorList{} 289 }