configset.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package componentconfigs
  14. import (
  15. "sort"
  16. "github.com/pkg/errors"
  17. apierrors "k8s.io/apimachinery/pkg/api/errors"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. "k8s.io/apimachinery/pkg/runtime"
  20. "k8s.io/apimachinery/pkg/runtime/schema"
  21. "k8s.io/apimachinery/pkg/util/validation/field"
  22. clientset "k8s.io/client-go/kubernetes"
  23. "k8s.io/klog"
  24. kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
  25. kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
  26. "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
  27. )
  28. // handler is a package internal type that handles component config factory and common functionality.
  29. // Every component config group should have exactly one static instance of handler.
  30. type handler struct {
  31. // GroupVersion holds this handler's group name and preferred version
  32. GroupVersion schema.GroupVersion
  33. // AddToScheme points to a func that should add the GV types to a schema
  34. AddToScheme func(*runtime.Scheme) error
  35. // CreateEmpty returns an empty kubeadmapi.ComponentConfig (not even defaulted)
  36. CreateEmpty func() kubeadmapi.ComponentConfig
  37. // fromCluster should load the component config from a config map on the cluster.
  38. // Don't use this directly! Use FromCluster instead!
  39. fromCluster func(*handler, clientset.Interface, *kubeadmapi.ClusterConfiguration) (kubeadmapi.ComponentConfig, error)
  40. }
  41. // FromDocumentMap looks in the document map for documents with this handler's group.
  42. // If such are found a new component config is instantiated and the documents are loaded into it.
  43. // No error is returned if no documents are found.
  44. func (h *handler) FromDocumentMap(docmap kubeadmapi.DocumentMap) (kubeadmapi.ComponentConfig, error) {
  45. for gvk := range docmap {
  46. if gvk.Group == h.GroupVersion.Group {
  47. cfg := h.CreateEmpty()
  48. if err := cfg.Unmarshal(docmap); err != nil {
  49. return nil, err
  50. }
  51. return cfg, nil
  52. }
  53. }
  54. return nil, nil
  55. }
  56. // 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
  57. // This is an utility func. Used by the component config support implementations. Don't use it outside of that context.
  58. func (h *handler) fromConfigMap(client clientset.Interface, cmName, cmKey string, mustExist bool) (kubeadmapi.ComponentConfig, error) {
  59. configMap, err := apiclient.GetConfigMapWithRetry(client, metav1.NamespaceSystem, cmName)
  60. if err != nil {
  61. if !mustExist && (apierrors.IsNotFound(err) || apierrors.IsForbidden(err)) {
  62. klog.Warningf("Warning: No %s config is loaded. Continuing without it: %v", h.GroupVersion, err)
  63. return nil, nil
  64. }
  65. return nil, err
  66. }
  67. configData, ok := configMap.Data[cmKey]
  68. if !ok {
  69. return nil, errors.Errorf("unexpected error when reading %s ConfigMap: %s key value pair missing", cmName, cmKey)
  70. }
  71. gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(configData))
  72. if err != nil {
  73. return nil, err
  74. }
  75. return h.FromDocumentMap(gvkmap)
  76. }
  77. // FromCluster loads a component from a config map in the cluster
  78. func (h *handler) FromCluster(clientset clientset.Interface, clusterCfg *kubeadmapi.ClusterConfiguration) (kubeadmapi.ComponentConfig, error) {
  79. return h.fromCluster(h, clientset, clusterCfg)
  80. }
  81. // Marshal is an utility function, used by the component config support implementations to marshal a runtime.Object to YAML with the
  82. // correct group and version
  83. func (h *handler) Marshal(object runtime.Object) ([]byte, error) {
  84. return kubeadmutil.MarshalToYamlForCodecs(object, h.GroupVersion, Codecs)
  85. }
  86. // Unmarshal attempts to unmarshal a runtime.Object from a document map. If no object is found, no error is returned.
  87. // If a matching group is found, but no matching version an error is returned indicating that users should do manual conversion.
  88. func (h *handler) Unmarshal(from kubeadmapi.DocumentMap, into runtime.Object) error {
  89. for gvk, yaml := range from {
  90. // If this is a different group, we ignore it
  91. if gvk.Group != h.GroupVersion.Group {
  92. continue
  93. }
  94. // If this is the correct group, but different version, we return an error
  95. if gvk.Version != h.GroupVersion.Version {
  96. // TODO: Replace this with a special error type and make UX better around it
  97. return errors.Errorf("unexpected apiVersion %q, you may have to do manual conversion to %q and execute kubeadm again", gvk.GroupVersion(), h.GroupVersion)
  98. }
  99. // As long as we support only component configs with a single kind, this is allowed
  100. return runtime.DecodeInto(Codecs.UniversalDecoder(), yaml, into)
  101. }
  102. return nil
  103. }
  104. // known holds the known component config handlers. Add new component configs here.
  105. var known = []*handler{
  106. &kubeProxyHandler,
  107. &kubeletHandler,
  108. }
  109. // ensureInitializedComponentConfigs is an utility func to initialize the ComponentConfigMap in ClusterConfiguration prior to possible writes to it
  110. func ensureInitializedComponentConfigs(clusterCfg *kubeadmapi.ClusterConfiguration) {
  111. if clusterCfg.ComponentConfigs == nil {
  112. clusterCfg.ComponentConfigs = kubeadmapi.ComponentConfigMap{}
  113. }
  114. }
  115. // Default sets up defaulted component configs in the supplied ClusterConfiguration
  116. func Default(clusterCfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint *kubeadmapi.APIEndpoint) {
  117. ensureInitializedComponentConfigs(clusterCfg)
  118. for _, handler := range known {
  119. // If the component config exists, simply default it. Otherwise, create it before defaulting.
  120. group := handler.GroupVersion.Group
  121. if componentCfg, ok := clusterCfg.ComponentConfigs[group]; ok {
  122. componentCfg.Default(clusterCfg, localAPIEndpoint)
  123. } else {
  124. componentCfg := handler.CreateEmpty()
  125. componentCfg.Default(clusterCfg, localAPIEndpoint)
  126. clusterCfg.ComponentConfigs[group] = componentCfg
  127. }
  128. }
  129. }
  130. // FetchFromCluster attempts to fetch all known component configs from their config maps and store them in the supplied ClusterConfiguration
  131. func FetchFromCluster(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) error {
  132. ensureInitializedComponentConfigs(clusterCfg)
  133. for _, handler := range known {
  134. componentCfg, err := handler.FromCluster(client, clusterCfg)
  135. if err != nil {
  136. return err
  137. }
  138. if componentCfg != nil {
  139. clusterCfg.ComponentConfigs[handler.GroupVersion.Group] = componentCfg
  140. }
  141. }
  142. return nil
  143. }
  144. // FetchFromDocumentMap attempts to load all known component configs from a document map into the supplied ClusterConfiguration
  145. func FetchFromDocumentMap(clusterCfg *kubeadmapi.ClusterConfiguration, docmap kubeadmapi.DocumentMap) error {
  146. ensureInitializedComponentConfigs(clusterCfg)
  147. for _, handler := range known {
  148. componentCfg, err := handler.FromDocumentMap(docmap)
  149. if err != nil {
  150. return err
  151. }
  152. if componentCfg != nil {
  153. clusterCfg.ComponentConfigs[handler.GroupVersion.Group] = componentCfg
  154. }
  155. }
  156. return nil
  157. }
  158. // Validate is a placeholder for performing a validation on an already loaded component configs in a ClusterConfiguration
  159. // Currently it prints a warning that no validation was performed
  160. func Validate(clusterCfg *kubeadmapi.ClusterConfiguration) field.ErrorList {
  161. groups := []string{}
  162. for group := range clusterCfg.ComponentConfigs {
  163. groups = append(groups, group)
  164. }
  165. sort.Strings(groups) // The sort is needed to make the output predictable
  166. klog.Warningf("WARNING: kubeadm cannot validate component configs for API groups %v", groups)
  167. return field.ErrorList{}
  168. }