1
16
17 package file
18
19 import (
20 "context"
21 "time"
22
23 "github.com/pkg/errors"
24
25 v1 "k8s.io/api/core/v1"
26 apierrors "k8s.io/apimachinery/pkg/api/errors"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/util/wait"
29 "k8s.io/client-go/tools/clientcmd"
30 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
31 bootstrapapi "k8s.io/cluster-bootstrap/token/api"
32 "k8s.io/klog/v2"
33
34 "k8s.io/kubernetes/cmd/kubeadm/app/constants"
35 kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
36 )
37
38
39
40
41 func RetrieveValidatedConfigInfo(filepath string, discoveryTimeout time.Duration) (*clientcmdapi.Config, error) {
42 config, err := clientcmd.LoadFromFile(filepath)
43 if err != nil {
44 return nil, err
45 }
46 return ValidateConfigInfo(config, discoveryTimeout)
47 }
48
49
50
51
52 func ValidateConfigInfo(config *clientcmdapi.Config, discoveryTimeout time.Duration) (*clientcmdapi.Config, error) {
53 if len(config.Clusters) < 1 {
54 return nil, errors.New("the provided kubeconfig file must have at least one Cluster defined")
55 }
56 currentClusterName, currentCluster := kubeconfigutil.GetClusterFromKubeConfig(config)
57 if currentCluster == nil {
58 return nil, errors.New("the provided kubeconfig file must have a unnamed Cluster or a CurrentContext that specifies a non-nil Cluster")
59 }
60 if err := clientcmd.Validate(*config); err != nil {
61 return nil, err
62 }
63
64
65 if err := kubeconfigutil.EnsureCertificateAuthorityIsEmbedded(currentCluster); err != nil {
66 return nil, err
67 }
68
69
70 if kubeconfigutil.HasAuthenticationCredentials(config) {
71 klog.V(1).Info("[discovery] Using authentication credentials from the discovery file for validating TLS connection")
72
73
74
75 if err := kubeconfigutil.EnsureAuthenticationInfoAreEmbedded(config); err != nil {
76 return nil, err
77 }
78 } else {
79
80 klog.V(1).Info("[discovery] Discovery file does not contains authentication credentials, using unauthenticated request for validating TLS connection")
81 }
82
83
84
85 client, err := kubeconfigutil.ToClientSet(config)
86 if err != nil {
87 return nil, err
88 }
89
90 klog.V(1).Infof("[discovery] Created cluster-info discovery client, requesting info from %q\n", currentCluster.Server)
91
92 var clusterinfoCM *v1.ConfigMap
93
94 var lastError error
95 err = wait.PollUntilContextTimeout(context.Background(),
96 constants.DiscoveryRetryInterval, discoveryTimeout,
97 true, func(_ context.Context) (bool, error) {
98 clusterinfoCM, lastError = client.CoreV1().ConfigMaps(metav1.NamespacePublic).Get(context.TODO(), bootstrapapi.ConfigMapClusterInfo, metav1.GetOptions{})
99 if lastError != nil {
100 if apierrors.IsForbidden(lastError) {
101
102
103 klog.Warningf("[discovery] Could not access the %s ConfigMap for refreshing the cluster-info information, but the TLS cert is valid so proceeding...\n", bootstrapapi.ConfigMapClusterInfo)
104 return true, nil
105 }
106 klog.V(1).Infof("[discovery] Error reading the %s ConfigMap, will try again: %v\n", bootstrapapi.ConfigMapClusterInfo, err)
107 return false, nil
108 }
109 return true, nil
110 })
111 if err != nil {
112 return nil, errors.Wrapf(lastError, "Abort reading the %s ConfigMap after timeout of %v",
113 bootstrapapi.ConfigMapClusterInfo, discoveryTimeout)
114 }
115
116
117 if clusterinfoCM == nil {
118 return config, nil
119 }
120
121
122 refreshedBaseKubeConfig, err := tryParseClusterInfoFromConfigMap(clusterinfoCM)
123 if err != nil {
124 klog.V(1).Infof("[discovery] The %s ConfigMap isn't set up properly (%v), but the TLS cert is valid so proceeding...\n", bootstrapapi.ConfigMapClusterInfo, err)
125 return config, nil
126 }
127
128 _, refreshedCluster := kubeconfigutil.GetClusterFromKubeConfig(refreshedBaseKubeConfig)
129 if currentCluster.Server != refreshedCluster.Server {
130 klog.Warningf("[discovery] the API Server endpoint %q in use is different from the endpoint %q which defined in the %s ConfigMap", currentCluster.Server, refreshedCluster.Server, bootstrapapi.ConfigMapClusterInfo)
131 }
132
133 if len(currentCluster.CertificateAuthorityData) == 0 && len(refreshedCluster.CertificateAuthorityData) > 0 {
134 config.Clusters[currentClusterName].CertificateAuthorityData = refreshedCluster.CertificateAuthorityData
135 klog.V(1).Infof("[discovery] Synced CertificateAuthorityData from the %s ConfigMap", bootstrapapi.ConfigMapClusterInfo)
136 }
137
138 return config, nil
139 }
140
141
142 func tryParseClusterInfoFromConfigMap(cm *v1.ConfigMap) (*clientcmdapi.Config, error) {
143 kubeConfigString, ok := cm.Data[bootstrapapi.KubeConfigKey]
144 if !ok || len(kubeConfigString) == 0 {
145 return nil, errors.Errorf("no %s key in ConfigMap", bootstrapapi.KubeConfigKey)
146 }
147 parsedKubeConfig, err := clientcmd.Load([]byte(kubeConfigString))
148 if err != nil {
149 return nil, errors.Wrapf(err, "couldn't parse the kubeconfig file in the %s ConfigMap", bootstrapapi.ConfigMapClusterInfo)
150 }
151 return parsedKubeConfig, nil
152 }
153
View as plain text