1
16
17 package upgrade
18
19 import (
20 "context"
21 "fmt"
22 "io"
23 "os"
24 "path/filepath"
25
26 "github.com/pkg/errors"
27
28 apierrors "k8s.io/apimachinery/pkg/api/errors"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/labels"
31 errorsutil "k8s.io/apimachinery/pkg/util/errors"
32 "k8s.io/apimachinery/pkg/util/sets"
33 clientset "k8s.io/client-go/kubernetes"
34 "k8s.io/klog/v2"
35
36 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
37 kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
38 "k8s.io/kubernetes/cmd/kubeadm/app/features"
39 "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
40 "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy"
41 "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo"
42 nodebootstraptoken "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
43 kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
44 patchnodephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/patchnode"
45 "k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig"
46 kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
47 dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
48 )
49
50
51
52 func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, patchesDir string, dryRun bool, out io.Writer) error {
53 var errs []error
54
55
56
57
58 if err := uploadconfig.UploadConfiguration(cfg, client); err != nil {
59 errs = append(errs, err)
60 }
61
62
63 if err := kubeletphase.CreateConfigMap(&cfg.ClusterConfiguration, client); err != nil {
64 errs = append(errs, errors.Wrap(err, "error creating kubelet configuration ConfigMap"))
65 }
66
67
68 if err := WriteKubeletConfigFiles(cfg, patchesDir, dryRun, out); err != nil {
69 errs = append(errs, err)
70 }
71
72
73
74
75 if err := patchnodephase.AnnotateCRISocket(client, cfg.NodeRegistration.Name, cfg.NodeRegistration.CRISocket); err != nil {
76 errs = append(errs, errors.Wrap(err, "error uploading crisocket"))
77 }
78
79
80 if err := nodebootstraptoken.AllowBoostrapTokensToGetNodes(client); err != nil {
81 errs = append(errs, err)
82 }
83
84
85 if err := nodebootstraptoken.AllowBootstrapTokensToPostCSRs(client); err != nil {
86 errs = append(errs, err)
87 }
88
89
90 if err := nodebootstraptoken.AutoApproveNodeBootstrapTokens(client); err != nil {
91 errs = append(errs, err)
92 }
93
94
95 if err := nodebootstraptoken.AutoApproveNodeCertificateRotation(client); err != nil {
96 errs = append(errs, err)
97 }
98
99
100
101
102
103
104
105 if err := clusterinfo.CreateClusterInfoRBACRules(client); err != nil {
106 errs = append(errs, err)
107 }
108
109 if err := PerformAddonsUpgrade(client, cfg, out); err != nil {
110 errs = append(errs, err)
111 }
112
113 return errorsutil.NewAggregate(errs)
114 }
115
116
117
118
119 func PerformAddonsUpgrade(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, out io.Writer) error {
120 unupgradedControlPlanes, err := unupgradedControlPlaneInstances(client, cfg.NodeRegistration.Name)
121 if err != nil {
122 err = errors.Wrapf(err, "failed to determine whether all the control plane instances have been upgraded")
123 if !features.Enabled(cfg.FeatureGates, features.UpgradeAddonsBeforeControlPlane) {
124 return err
125 }
126
127
128 klog.V(1).Info(err)
129 }
130 if len(unupgradedControlPlanes) > 0 {
131 if !features.Enabled(cfg.FeatureGates, features.UpgradeAddonsBeforeControlPlane) {
132 fmt.Fprintf(out, "[upgrade/addons] skip upgrade addons because control plane instances %v have not been upgraded\n", unupgradedControlPlanes)
133 return nil
134 }
135
136
137 klog.V(1).Infof("upgrading addons when control plane instances %v have not been upgraded "+
138 "may lead to incompatibility problems. You can disable the UpgradeAddonsBeforeControlPlane feature gate to "+
139 "ensure that the addons upgrade is executed only when all the control plane instances have been upgraded.", unupgradedControlPlanes)
140 }
141
142 var errs []error
143
144
145
146
147
148
149 var missingCoreDNSConfigMap bool
150 if _, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(
151 context.TODO(),
152 kubeadmconstants.CoreDNSConfigMap,
153 metav1.GetOptions{},
154 ); err != nil && apierrors.IsNotFound(err) {
155 missingCoreDNSConfigMap = true
156 }
157 if missingCoreDNSConfigMap {
158 klog.Warningf("the ConfigMaps %q in the namespace %q were not found. "+
159 "Assuming that a DNS server was not deployed for this cluster. "+
160 "Note that once 'kubeadm upgrade apply' supports phases you "+
161 "will have to skip the DNS upgrade manually",
162 kubeadmconstants.CoreDNSConfigMap,
163 metav1.NamespaceSystem)
164 } else {
165
166 if err := dns.EnsureDNSAddon(&cfg.ClusterConfiguration, client, out, false); err != nil {
167 errs = append(errs, err)
168 }
169 }
170
171
172
173
174
175
176 if _, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(
177 context.TODO(),
178 kubeadmconstants.KubeProxyConfigMap,
179 metav1.GetOptions{},
180 ); err != nil && apierrors.IsNotFound(err) {
181 klog.Warningf("the ConfigMap %q in the namespace %q was not found. "+
182 "Assuming that kube-proxy was not deployed for this cluster. "+
183 "Note that once 'kubeadm upgrade apply' supports phases you "+
184 "will have to skip the kube-proxy upgrade manually",
185 kubeadmconstants.KubeProxyConfigMap,
186 metav1.NamespaceSystem)
187 } else {
188
189 if err := proxy.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, out, false); err != nil {
190 errs = append(errs, err)
191 }
192 }
193
194 return errorsutil.NewAggregate(errs)
195 }
196
197
198
199
200
201
202 func unupgradedControlPlaneInstances(client clientset.Interface, nodeName string) ([]string, error) {
203 selector := labels.SelectorFromSet(labels.Set(map[string]string{
204 "component": kubeadmconstants.KubeAPIServer,
205 }))
206 pods, err := client.CoreV1().Pods(metav1.NamespaceSystem).List(context.TODO(), metav1.ListOptions{
207 LabelSelector: selector.String(),
208 })
209 if err != nil {
210 return nil, errors.Wrap(err, "failed to list kube-apiserver Pod from cluster")
211 }
212 if len(pods.Items) == 0 {
213 return nil, errors.Errorf("cannot find kube-apiserver Pod by label selector: %v", selector.String())
214 }
215
216 nodeImageMap := map[string]string{}
217
218 for _, pod := range pods.Items {
219 found := false
220 for _, c := range pod.Spec.Containers {
221 if c.Name == kubeadmconstants.KubeAPIServer {
222 nodeImageMap[pod.Spec.NodeName] = c.Image
223 found = true
224 break
225 }
226 }
227 if !found {
228 return nil, errors.Errorf("cannot find container by name %q for Pod %v", kubeadmconstants.KubeAPIServer, klog.KObj(&pod))
229 }
230 }
231
232 upgradedImage, ok := nodeImageMap[nodeName]
233 if !ok {
234 return nil, errors.Errorf("cannot find kube-apiserver image for current control plane instance %v", nodeName)
235 }
236
237 unupgradedNodes := sets.New[string]()
238 for node, image := range nodeImageMap {
239 if image != upgradedImage {
240 unupgradedNodes.Insert(node)
241 }
242 }
243
244 if len(unupgradedNodes) > 0 {
245 return sets.List(unupgradedNodes), nil
246 }
247
248 return nil, nil
249 }
250
251
252 func WriteKubeletConfigFiles(cfg *kubeadmapi.InitConfiguration, patchesDir string, dryRun bool, out io.Writer) error {
253
254 kubeletDir, err := GetKubeletDir(dryRun)
255 if err != nil {
256
257 return err
258 }
259
260
261 backupDir, err := kubeadmconstants.CreateTempDirForKubeadm(kubeadmconstants.KubernetesDir, "kubeadm-kubelet-config")
262 if err != nil {
263 return err
264 }
265 src := filepath.Join(kubeletDir, kubeadmconstants.KubeletConfigurationFileName)
266 dest := filepath.Join(backupDir, kubeadmconstants.KubeletConfigurationFileName)
267
268 if !dryRun {
269 fmt.Printf("[upgrade] Backing up kubelet config file to %s\n", dest)
270 err := kubeadmutil.CopyFile(src, dest)
271 if err != nil {
272 return errors.Wrap(err, "error backing up the kubelet config file")
273 }
274 } else {
275 fmt.Printf("[dryrun] Would back up kubelet config file to %s\n", dest)
276 }
277
278 errs := []error{}
279
280 if err := kubeletphase.WriteConfigToDisk(&cfg.ClusterConfiguration, kubeletDir, patchesDir, out); err != nil {
281 errs = append(errs, errors.Wrap(err, "error writing kubelet configuration to file"))
282 }
283
284 if dryRun {
285 err := dryrunutil.PrintDryRunFile(kubeadmconstants.KubeletConfigurationFileName, kubeletDir, kubeadmconstants.KubeletRunDirectory, os.Stdout)
286 if err != nil {
287 errs = append(errs, errors.Wrap(err, "error printing files on dryrun"))
288 }
289 }
290 return errorsutil.NewAggregate(errs)
291 }
292
293
294 func GetKubeletDir(dryRun bool) (string, error) {
295 if dryRun {
296 return kubeadmconstants.CreateTempDirForKubeadm("", "kubeadm-upgrade-dryrun")
297 }
298 return kubeadmconstants.KubeletRunDirectory, nil
299 }
300
View as plain text