1
16
17 package upgrade
18
19 import (
20 "fmt"
21 "strings"
22 "testing"
23
24 "github.com/google/go-cmp/cmp"
25 apps "k8s.io/api/apps/v1"
26 v1 "k8s.io/api/core/v1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 versionutil "k8s.io/apimachinery/pkg/util/version"
29 clientsetfake "k8s.io/client-go/kubernetes/fake"
30
31 "k8s.io/kubernetes/cmd/kubeadm/app/constants"
32 "k8s.io/kubernetes/cmd/kubeadm/app/util/output"
33 )
34
35 type fakeVersionGetter struct {
36 clusterVersion string
37 kubeadmVersion string
38 stableVersion string
39 latestVersion string
40 latestDevBranchVersion string
41 stablePatchVersion string
42 kubeletVersion string
43 componentVersion string
44 etcdVersion string
45 isExternalEtcd bool
46 }
47
48 var _ VersionGetter = &fakeVersionGetter{}
49
50
51 func (f *fakeVersionGetter) ClusterVersion() (string, *versionutil.Version, error) {
52 return f.clusterVersion, versionutil.MustParseSemantic(f.clusterVersion), nil
53 }
54
55
56 func (f *fakeVersionGetter) KubeadmVersion() (string, *versionutil.Version, error) {
57 return f.kubeadmVersion, versionutil.MustParseSemantic(f.kubeadmVersion), nil
58 }
59
60
61 func (f *fakeVersionGetter) VersionFromCILabel(ciVersionLabel, _ string) (string, *versionutil.Version, error) {
62 if ciVersionLabel == "stable" {
63 return f.stableVersion, versionutil.MustParseSemantic(f.stableVersion), nil
64 }
65 if ciVersionLabel == "latest" {
66 return f.latestVersion, versionutil.MustParseSemantic(f.latestVersion), nil
67 }
68 if f.latestDevBranchVersion != "" && strings.HasPrefix(ciVersionLabel, "latest-") {
69 return f.latestDevBranchVersion, versionutil.MustParseSemantic(f.latestDevBranchVersion), nil
70 }
71 return f.stablePatchVersion, versionutil.MustParseSemantic(f.stablePatchVersion), nil
72 }
73
74
75 func (f *fakeVersionGetter) KubeletVersions() (map[string][]string, error) {
76 return map[string][]string{
77 f.kubeletVersion: {"node1"},
78 }, nil
79 }
80
81
82 func (f *fakeVersionGetter) ComponentVersions(name string) (map[string][]string, error) {
83 if name == constants.Etcd {
84 if f.isExternalEtcd {
85 return map[string][]string{}, nil
86 }
87 return map[string][]string{
88 f.etcdVersion: {"node1"},
89 }, nil
90 }
91
92 return map[string][]string{
93 f.componentVersion: {"node1"},
94 }, nil
95 }
96
97 const fakeCurrentEtcdVersion = "3.1.12"
98
99 func getEtcdVersion(v *versionutil.Version) string {
100 etcdVer, _, _ := constants.EtcdSupportedVersion(constants.SupportedEtcdVersion, v.String())
101 return etcdVer.String()
102 }
103
104 const fakeCurrentCoreDNSVersion = "1.0.6"
105
106 func TestGetAvailableUpgrades(t *testing.T) {
107
108
109
110
111
112 v1X0 := versionutil.MustParseSemantic("v1.14.0")
113 v1X5 := v1X0.WithPatch(5)
114
115
116 v1Y0 := versionutil.MustParseSemantic("v1.15.0")
117 v1Y0alpha0 := v1Y0.WithPreRelease("alpha.0")
118 v1Y0alpha1 := v1Y0.WithPreRelease("alpha.1")
119 v1Y1 := v1Y0.WithPatch(1)
120 v1Y2 := v1Y0.WithPatch(2)
121 v1Y3 := v1Y0.WithPatch(3)
122 v1Y5 := v1Y0.WithPatch(5)
123
124
125 v1Z0 := versionutil.MustParseSemantic("v1.16.0")
126 v1Z0alpha1 := v1Z0.WithPreRelease("alpha.1")
127 v1Z0alpha2 := v1Z0.WithPreRelease("alpha.2")
128 v1Z0beta1 := v1Z0.WithPreRelease("beta.1")
129 v1Z0rc1 := v1Z0.WithPreRelease("rc.1")
130 v1Z1 := v1Z0.WithPatch(1)
131
132 tests := []struct {
133 name string
134 vg VersionGetter
135 expectedUpgrades []Upgrade
136 allowExperimental, allowRCs bool
137 errExpected bool
138 beforeDNSVersion string
139 }{
140 {
141 name: "no action needed, already up-to-date",
142 vg: &fakeVersionGetter{
143 clusterVersion: v1Y0.String(),
144 componentVersion: v1Y0.String(),
145 kubeletVersion: v1Y0.String(),
146 kubeadmVersion: v1Y0.String(),
147 etcdVersion: fakeCurrentEtcdVersion,
148
149 stablePatchVersion: v1Y0.String(),
150 stableVersion: v1Y0.String(),
151 },
152 beforeDNSVersion: fakeCurrentCoreDNSVersion,
153 expectedUpgrades: nil,
154 allowExperimental: false,
155 errExpected: false,
156 },
157 {
158 name: "simple patch version upgrade",
159 vg: &fakeVersionGetter{
160 clusterVersion: v1Y1.String(),
161 componentVersion: v1Y1.String(),
162 kubeletVersion: v1Y1.String(),
163 kubeadmVersion: v1Y2.String(),
164 etcdVersion: fakeCurrentEtcdVersion,
165
166 stablePatchVersion: v1Y3.String(),
167 stableVersion: v1Y3.String(),
168 },
169 beforeDNSVersion: fakeCurrentCoreDNSVersion,
170 expectedUpgrades: []Upgrade{
171 {
172 Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
173 Before: ClusterState{
174 KubeVersion: v1Y1.String(),
175 KubeAPIServerVersions: map[string][]string{
176 v1Y1.String(): {"node1"},
177 },
178 KubeControllerManagerVersions: map[string][]string{
179 v1Y1.String(): {"node1"},
180 },
181 KubeSchedulerVersions: map[string][]string{
182 v1Y1.String(): {"node1"},
183 },
184 KubeletVersions: map[string][]string{
185 v1Y1.String(): {"node1"},
186 },
187 KubeadmVersion: v1Y2.String(),
188 DNSVersion: fakeCurrentCoreDNSVersion,
189 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
190 },
191 After: ClusterState{
192 KubeVersion: v1Y3.String(),
193 KubeadmVersion: v1Y3.String(),
194 DNSVersion: constants.CoreDNSVersion,
195 EtcdVersion: getEtcdVersion(v1Y3),
196 },
197 },
198 },
199 allowExperimental: false,
200 errExpected: false,
201 },
202 {
203 name: "simple patch version upgrade with external etcd",
204 vg: &fakeVersionGetter{
205 clusterVersion: v1Y1.String(),
206 componentVersion: v1Y1.String(),
207 kubeletVersion: v1Y1.String(),
208 kubeadmVersion: v1Y2.String(),
209 isExternalEtcd: true,
210
211 stablePatchVersion: v1Y3.String(),
212 stableVersion: v1Y3.String(),
213 },
214 beforeDNSVersion: fakeCurrentCoreDNSVersion,
215 expectedUpgrades: []Upgrade{
216 {
217 Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
218 Before: ClusterState{
219 KubeVersion: v1Y1.String(),
220 KubeAPIServerVersions: map[string][]string{
221 v1Y1.String(): {"node1"},
222 },
223 KubeControllerManagerVersions: map[string][]string{
224 v1Y1.String(): {"node1"},
225 },
226 KubeSchedulerVersions: map[string][]string{
227 v1Y1.String(): {"node1"},
228 },
229 KubeletVersions: map[string][]string{
230 v1Y1.String(): {"node1"},
231 },
232 EtcdVersions: map[string][]string{},
233 KubeadmVersion: v1Y2.String(),
234 DNSVersion: fakeCurrentCoreDNSVersion,
235 },
236 After: ClusterState{
237 KubeVersion: v1Y3.String(),
238 KubeadmVersion: v1Y3.String(),
239 DNSVersion: constants.CoreDNSVersion,
240 EtcdVersion: "",
241 },
242 },
243 },
244 allowExperimental: false,
245 errExpected: false,
246 },
247 {
248 name: "no version provided to offline version getter does not change behavior",
249 vg: NewOfflineVersionGetter(&fakeVersionGetter{
250 clusterVersion: v1Y1.String(),
251 componentVersion: v1Y1.String(),
252 kubeletVersion: v1Y1.String(),
253 kubeadmVersion: v1Y2.String(),
254 etcdVersion: fakeCurrentEtcdVersion,
255
256 stablePatchVersion: v1Y3.String(),
257 stableVersion: v1Y3.String(),
258 }, ""),
259 beforeDNSVersion: fakeCurrentCoreDNSVersion,
260 expectedUpgrades: []Upgrade{
261 {
262 Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
263 Before: ClusterState{
264 KubeVersion: v1Y1.String(),
265 KubeAPIServerVersions: map[string][]string{
266 v1Y1.String(): {"node1"},
267 },
268 KubeControllerManagerVersions: map[string][]string{
269 v1Y1.String(): {"node1"},
270 },
271 KubeSchedulerVersions: map[string][]string{
272 v1Y1.String(): {"node1"},
273 },
274 KubeletVersions: map[string][]string{
275 v1Y1.String(): {"node1"},
276 },
277 KubeadmVersion: v1Y2.String(),
278 DNSVersion: fakeCurrentCoreDNSVersion,
279 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
280 },
281 After: ClusterState{
282 KubeVersion: v1Y3.String(),
283 KubeadmVersion: v1Y3.String(),
284 DNSVersion: constants.CoreDNSVersion,
285 EtcdVersion: getEtcdVersion(v1Y3),
286 },
287 },
288 },
289 allowExperimental: false,
290 errExpected: false,
291 },
292 {
293 name: "minor version upgrade only",
294 vg: &fakeVersionGetter{
295 clusterVersion: v1Y1.String(),
296 componentVersion: v1Y1.String(),
297 kubeletVersion: v1Y1.String(),
298 kubeadmVersion: v1Z0.String(),
299 etcdVersion: fakeCurrentEtcdVersion,
300
301 stablePatchVersion: v1Y1.String(),
302 stableVersion: v1Z0.String(),
303 },
304 beforeDNSVersion: fakeCurrentCoreDNSVersion,
305 expectedUpgrades: []Upgrade{
306 {
307 Description: "stable version",
308 Before: ClusterState{
309 KubeVersion: v1Y1.String(),
310 KubeAPIServerVersions: map[string][]string{
311 v1Y1.String(): {"node1"},
312 },
313 KubeControllerManagerVersions: map[string][]string{
314 v1Y1.String(): {"node1"},
315 },
316 KubeSchedulerVersions: map[string][]string{
317 v1Y1.String(): {"node1"},
318 },
319 KubeletVersions: map[string][]string{
320 v1Y1.String(): {"node1"},
321 },
322 KubeadmVersion: v1Z0.String(),
323 DNSVersion: fakeCurrentCoreDNSVersion,
324 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
325 },
326 After: ClusterState{
327 KubeVersion: v1Z0.String(),
328 KubeadmVersion: v1Z0.String(),
329 DNSVersion: constants.CoreDNSVersion,
330 EtcdVersion: getEtcdVersion(v1Z0),
331 },
332 },
333 },
334 allowExperimental: false,
335 errExpected: false,
336 },
337 {
338 name: "both minor version upgrade and patch version upgrade available",
339 vg: &fakeVersionGetter{
340 clusterVersion: v1Y3.String(),
341 componentVersion: v1Y3.String(),
342 kubeletVersion: v1Y3.String(),
343 kubeadmVersion: v1Y5.String(),
344 etcdVersion: fakeCurrentEtcdVersion,
345
346 stablePatchVersion: v1Y5.String(),
347 stableVersion: v1Z1.String(),
348 },
349 beforeDNSVersion: fakeCurrentCoreDNSVersion,
350 expectedUpgrades: []Upgrade{
351 {
352 Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
353 Before: ClusterState{
354 KubeVersion: v1Y3.String(),
355 KubeAPIServerVersions: map[string][]string{
356 v1Y3.String(): {"node1"},
357 },
358 KubeControllerManagerVersions: map[string][]string{
359 v1Y3.String(): {"node1"},
360 },
361 KubeSchedulerVersions: map[string][]string{
362 v1Y3.String(): {"node1"},
363 },
364 KubeletVersions: map[string][]string{
365 v1Y3.String(): {"node1"},
366 },
367 KubeadmVersion: v1Y5.String(),
368 DNSVersion: fakeCurrentCoreDNSVersion,
369 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
370 },
371 After: ClusterState{
372 KubeVersion: v1Y5.String(),
373 KubeadmVersion: v1Y5.String(),
374 DNSVersion: constants.CoreDNSVersion,
375 EtcdVersion: getEtcdVersion(v1Y5),
376 },
377 },
378 {
379 Description: "stable version",
380 Before: ClusterState{
381 KubeVersion: v1Y3.String(),
382 KubeAPIServerVersions: map[string][]string{
383 v1Y3.String(): {"node1"},
384 },
385 KubeControllerManagerVersions: map[string][]string{
386 v1Y3.String(): {"node1"},
387 },
388 KubeSchedulerVersions: map[string][]string{
389 v1Y3.String(): {"node1"},
390 },
391 KubeletVersions: map[string][]string{
392 v1Y3.String(): {"node1"},
393 },
394 KubeadmVersion: v1Y5.String(),
395 DNSVersion: fakeCurrentCoreDNSVersion,
396 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
397 },
398 After: ClusterState{
399 KubeVersion: v1Z1.String(),
400 KubeadmVersion: v1Z1.String(),
401 DNSVersion: constants.CoreDNSVersion,
402 EtcdVersion: getEtcdVersion(v1Z1),
403 },
404 },
405 },
406 allowExperimental: false,
407 errExpected: false,
408 },
409 {
410 name: "allow experimental upgrades, but no upgrade available",
411 vg: &fakeVersionGetter{
412 clusterVersion: v1Z0alpha2.String(),
413 componentVersion: v1Z0alpha2.String(),
414 kubeletVersion: v1Y5.String(),
415 kubeadmVersion: v1Y5.String(),
416 etcdVersion: fakeCurrentEtcdVersion,
417
418 stablePatchVersion: v1Y5.String(),
419 stableVersion: v1Y5.String(),
420 latestVersion: v1Z0alpha2.String(),
421 },
422 beforeDNSVersion: fakeCurrentCoreDNSVersion,
423 expectedUpgrades: nil,
424 allowExperimental: true,
425 errExpected: false,
426 },
427 {
428 name: "upgrade to an unstable version should be supported",
429 vg: &fakeVersionGetter{
430 clusterVersion: v1Y5.String(),
431 componentVersion: v1Y5.String(),
432 kubeletVersion: v1Y5.String(),
433 kubeadmVersion: v1Y5.String(),
434 etcdVersion: fakeCurrentEtcdVersion,
435
436 stablePatchVersion: v1Y5.String(),
437 stableVersion: v1Y5.String(),
438 latestVersion: v1Z0alpha2.String(),
439 },
440 beforeDNSVersion: fakeCurrentCoreDNSVersion,
441 expectedUpgrades: []Upgrade{
442 {
443 Description: "experimental version",
444 Before: ClusterState{
445 KubeVersion: v1Y5.String(),
446 KubeAPIServerVersions: map[string][]string{
447 v1Y5.String(): {"node1"},
448 },
449 KubeControllerManagerVersions: map[string][]string{
450 v1Y5.String(): {"node1"},
451 },
452 KubeSchedulerVersions: map[string][]string{
453 v1Y5.String(): {"node1"},
454 },
455 KubeletVersions: map[string][]string{
456 v1Y5.String(): {"node1"},
457 },
458 KubeadmVersion: v1Y5.String(),
459 DNSVersion: fakeCurrentCoreDNSVersion,
460 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
461 },
462 After: ClusterState{
463 KubeVersion: v1Z0alpha2.String(),
464 KubeadmVersion: v1Z0alpha2.String(),
465 DNSVersion: constants.CoreDNSVersion,
466 EtcdVersion: getEtcdVersion(v1Z0alpha2),
467 },
468 },
469 },
470 allowExperimental: true,
471 errExpected: false,
472 },
473 {
474 name: "upgrade from an unstable version to an unstable version should be supported",
475 vg: &fakeVersionGetter{
476 clusterVersion: v1Z0alpha1.String(),
477 componentVersion: v1Z0alpha1.String(),
478 kubeletVersion: v1Y5.String(),
479 kubeadmVersion: v1Y5.String(),
480 etcdVersion: fakeCurrentEtcdVersion,
481
482 stablePatchVersion: v1Y5.String(),
483 stableVersion: v1Y5.String(),
484 latestVersion: v1Z0alpha2.String(),
485 },
486 beforeDNSVersion: fakeCurrentCoreDNSVersion,
487 expectedUpgrades: []Upgrade{
488 {
489 Description: "experimental version",
490 Before: ClusterState{
491 KubeVersion: v1Z0alpha1.String(),
492 KubeAPIServerVersions: map[string][]string{
493 v1Z0alpha1.String(): {"node1"},
494 },
495 KubeControllerManagerVersions: map[string][]string{
496 v1Z0alpha1.String(): {"node1"},
497 },
498 KubeSchedulerVersions: map[string][]string{
499 v1Z0alpha1.String(): {"node1"},
500 },
501 KubeletVersions: map[string][]string{
502 v1Y5.String(): {"node1"},
503 },
504 KubeadmVersion: v1Y5.String(),
505 DNSVersion: fakeCurrentCoreDNSVersion,
506 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
507 },
508 After: ClusterState{
509 KubeVersion: v1Z0alpha2.String(),
510 KubeadmVersion: v1Z0alpha2.String(),
511 DNSVersion: constants.CoreDNSVersion,
512 EtcdVersion: getEtcdVersion(v1Z0alpha2),
513 },
514 },
515 },
516 allowExperimental: true,
517 errExpected: false,
518 },
519 {
520 name: "v1.X.0-alpha.0 should be ignored",
521 vg: &fakeVersionGetter{
522 clusterVersion: v1X5.String(),
523 componentVersion: v1X5.String(),
524 kubeletVersion: v1X5.String(),
525 kubeadmVersion: v1X5.String(),
526 etcdVersion: fakeCurrentEtcdVersion,
527
528 stablePatchVersion: v1X5.String(),
529 stableVersion: v1X5.String(),
530 latestDevBranchVersion: v1Z0beta1.String(),
531 latestVersion: v1Y0alpha0.String(),
532 },
533 beforeDNSVersion: fakeCurrentCoreDNSVersion,
534 expectedUpgrades: []Upgrade{
535 {
536 Description: "experimental version",
537 Before: ClusterState{
538 KubeVersion: v1X5.String(),
539 KubeAPIServerVersions: map[string][]string{
540 v1X5.String(): {"node1"},
541 },
542 KubeControllerManagerVersions: map[string][]string{
543 v1X5.String(): {"node1"},
544 },
545 KubeSchedulerVersions: map[string][]string{
546 v1X5.String(): {"node1"},
547 },
548 KubeletVersions: map[string][]string{
549 v1X5.String(): {"node1"},
550 },
551 KubeadmVersion: v1X5.String(),
552 DNSVersion: fakeCurrentCoreDNSVersion,
553 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
554 },
555 After: ClusterState{
556 KubeVersion: v1Z0beta1.String(),
557 KubeadmVersion: v1Z0beta1.String(),
558 DNSVersion: constants.CoreDNSVersion,
559 EtcdVersion: getEtcdVersion(v1Z0beta1),
560 },
561 },
562 },
563 allowExperimental: true,
564 errExpected: false,
565 },
566 {
567 name: "upgrade to an RC version should be supported",
568 vg: &fakeVersionGetter{
569 clusterVersion: v1X5.String(),
570 componentVersion: v1X5.String(),
571 kubeletVersion: v1X5.String(),
572 kubeadmVersion: v1X5.String(),
573 etcdVersion: fakeCurrentEtcdVersion,
574
575 stablePatchVersion: v1X5.String(),
576 stableVersion: v1X5.String(),
577 latestDevBranchVersion: v1Z0rc1.String(),
578 latestVersion: v1Y0alpha1.String(),
579 },
580 beforeDNSVersion: fakeCurrentCoreDNSVersion,
581 expectedUpgrades: []Upgrade{
582 {
583 Description: "release candidate version",
584 Before: ClusterState{
585 KubeVersion: v1X5.String(),
586 KubeAPIServerVersions: map[string][]string{
587 v1X5.String(): {"node1"},
588 },
589 KubeControllerManagerVersions: map[string][]string{
590 v1X5.String(): {"node1"},
591 },
592 KubeSchedulerVersions: map[string][]string{
593 v1X5.String(): {"node1"},
594 },
595 KubeletVersions: map[string][]string{
596 v1X5.String(): {"node1"},
597 },
598 KubeadmVersion: v1X5.String(),
599 DNSVersion: fakeCurrentCoreDNSVersion,
600 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
601 },
602 After: ClusterState{
603 KubeVersion: v1Z0rc1.String(),
604 KubeadmVersion: v1Z0rc1.String(),
605 DNSVersion: constants.CoreDNSVersion,
606 EtcdVersion: getEtcdVersion(v1Z0rc1),
607 },
608 },
609 },
610 allowRCs: true,
611 errExpected: false,
612 },
613 {
614 name: "it is possible (but very uncommon) that the latest version from the previous branch is an rc and the current latest version is alpha.0. In that case, show the RC",
615 vg: &fakeVersionGetter{
616 clusterVersion: v1X5.String(),
617 componentVersion: v1X5.String(),
618 kubeletVersion: v1X5.String(),
619 kubeadmVersion: v1X5.String(),
620 etcdVersion: fakeCurrentEtcdVersion,
621
622 stablePatchVersion: v1X5.String(),
623 stableVersion: v1X5.String(),
624 latestDevBranchVersion: v1Z0rc1.String(),
625 latestVersion: v1Y0alpha0.String(),
626 },
627 beforeDNSVersion: fakeCurrentCoreDNSVersion,
628 expectedUpgrades: []Upgrade{
629 {
630 Description: "experimental version",
631 Before: ClusterState{
632 KubeVersion: v1X5.String(),
633 KubeAPIServerVersions: map[string][]string{
634 v1X5.String(): {"node1"},
635 },
636 KubeControllerManagerVersions: map[string][]string{
637 v1X5.String(): {"node1"},
638 },
639 KubeSchedulerVersions: map[string][]string{
640 v1X5.String(): {"node1"},
641 },
642 KubeletVersions: map[string][]string{
643 v1X5.String(): {"node1"},
644 },
645 KubeadmVersion: v1X5.String(),
646 DNSVersion: fakeCurrentCoreDNSVersion,
647 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
648 },
649 After: ClusterState{
650 KubeVersion: v1Z0rc1.String(),
651 KubeadmVersion: v1Z0rc1.String(),
652 DNSVersion: constants.CoreDNSVersion,
653 EtcdVersion: getEtcdVersion(v1Z0rc1),
654 },
655 },
656 },
657 allowExperimental: true,
658 errExpected: false,
659 },
660 {
661 name: "upgrade to an RC version should be supported. There may also be an even newer unstable version.",
662 vg: &fakeVersionGetter{
663 clusterVersion: v1X5.String(),
664 componentVersion: v1X5.String(),
665 kubeletVersion: v1X5.String(),
666 kubeadmVersion: v1X5.String(),
667 etcdVersion: fakeCurrentEtcdVersion,
668
669 stablePatchVersion: v1X5.String(),
670 stableVersion: v1X5.String(),
671 latestDevBranchVersion: v1Z0rc1.String(),
672 latestVersion: v1Y0alpha1.String(),
673 },
674 beforeDNSVersion: fakeCurrentCoreDNSVersion,
675 expectedUpgrades: []Upgrade{
676 {
677 Description: "release candidate version",
678 Before: ClusterState{
679 KubeVersion: v1X5.String(),
680 KubeAPIServerVersions: map[string][]string{
681 v1X5.String(): {"node1"},
682 },
683 KubeControllerManagerVersions: map[string][]string{
684 v1X5.String(): {"node1"},
685 },
686 KubeSchedulerVersions: map[string][]string{
687 v1X5.String(): {"node1"},
688 },
689 KubeletVersions: map[string][]string{
690 v1X5.String(): {"node1"},
691 },
692 KubeadmVersion: v1X5.String(),
693 DNSVersion: fakeCurrentCoreDNSVersion,
694 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
695 },
696 After: ClusterState{
697 KubeVersion: v1Z0rc1.String(),
698 KubeadmVersion: v1Z0rc1.String(),
699 DNSVersion: constants.CoreDNSVersion,
700 EtcdVersion: getEtcdVersion(v1Z0rc1),
701 },
702 },
703 {
704 Description: "experimental version",
705 Before: ClusterState{
706 KubeVersion: v1X5.String(),
707 KubeAPIServerVersions: map[string][]string{
708 v1X5.String(): {"node1"},
709 },
710 KubeControllerManagerVersions: map[string][]string{
711 v1X5.String(): {"node1"},
712 },
713 KubeSchedulerVersions: map[string][]string{
714 v1X5.String(): {"node1"},
715 },
716 KubeletVersions: map[string][]string{
717 v1X5.String(): {"node1"},
718 },
719 KubeadmVersion: v1X5.String(),
720 DNSVersion: fakeCurrentCoreDNSVersion,
721 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
722 },
723 After: ClusterState{
724 KubeVersion: v1Y0alpha1.String(),
725 KubeadmVersion: v1Y0alpha1.String(),
726 DNSVersion: constants.CoreDNSVersion,
727 EtcdVersion: getEtcdVersion(v1Y0alpha1),
728 },
729 },
730 },
731 allowRCs: true,
732 allowExperimental: true,
733 errExpected: false,
734 },
735 {
736 name: "offline version getter",
737 vg: NewOfflineVersionGetter(&fakeVersionGetter{
738 clusterVersion: v1Y1.String(),
739 componentVersion: v1Y1.String(),
740 kubeletVersion: v1Y0.String(),
741 kubeadmVersion: v1Y1.String(),
742 etcdVersion: fakeCurrentEtcdVersion,
743 }, v1Z1.String()),
744 beforeDNSVersion: fakeCurrentCoreDNSVersion,
745 expectedUpgrades: []Upgrade{
746 {
747 Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
748 Before: ClusterState{
749 KubeVersion: v1Y1.String(),
750 KubeAPIServerVersions: map[string][]string{
751 v1Y1.String(): {"node1"},
752 },
753 KubeControllerManagerVersions: map[string][]string{
754 v1Y1.String(): {"node1"},
755 },
756 KubeSchedulerVersions: map[string][]string{
757 v1Y1.String(): {"node1"},
758 },
759 KubeletVersions: map[string][]string{
760 v1Y0.String(): {"node1"},
761 },
762 KubeadmVersion: v1Y1.String(),
763 DNSVersion: fakeCurrentCoreDNSVersion,
764 EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
765 },
766 After: ClusterState{
767 KubeVersion: v1Z1.String(),
768 KubeadmVersion: v1Z1.String(),
769 DNSVersion: constants.CoreDNSVersion,
770 EtcdVersion: getEtcdVersion(v1Z1),
771 },
772 },
773 },
774 },
775 }
776
777
778
779 for _, rt := range tests {
780 t.Run(rt.name, func(t *testing.T) {
781
782 dnsName := constants.CoreDNSDeploymentName
783
784 client := clientsetfake.NewSimpleClientset(&apps.Deployment{
785 TypeMeta: metav1.TypeMeta{
786 Kind: "Deployment",
787 APIVersion: "apps/v1",
788 },
789 ObjectMeta: metav1.ObjectMeta{
790 Name: dnsName,
791 Namespace: "kube-system",
792 Labels: map[string]string{
793 "k8s-app": "kube-dns",
794 },
795 },
796 Spec: apps.DeploymentSpec{
797 Template: v1.PodTemplateSpec{
798 Spec: v1.PodSpec{
799 Containers: []v1.Container{
800 {
801 Image: "test:" + rt.beforeDNSVersion,
802 },
803 },
804 },
805 },
806 },
807 })
808
809 actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, client, &output.TextPrinter{})
810 if diff := cmp.Diff(rt.expectedUpgrades, actualUpgrades); len(diff) > 0 {
811 t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades:\n%v\n\tgot:\n%v\n\tdiff:\n%v", rt.expectedUpgrades, actualUpgrades, diff)
812 }
813 if rt.errExpected && actualErr == nil {
814 t.Error("unexpected success")
815 } else if !rt.errExpected && actualErr != nil {
816 t.Errorf("unexpected failure: %v", actualErr)
817 }
818 if diff := cmp.Diff(rt.expectedUpgrades, actualUpgrades); len(diff) > 0 {
819 t.Logf("diff: %s", cmp.Diff(rt.expectedUpgrades, actualUpgrades))
820 t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades:\n%v\n\tgot:\n%v\n\tdiff:\n%v", rt.expectedUpgrades, actualUpgrades, diff)
821 }
822 })
823 }
824 }
825
826 func TestKubeletUpgrade(t *testing.T) {
827 tests := []struct {
828 name string
829 before map[string][]string
830 after string
831 expected bool
832 }{
833 {
834 name: "upgrade from v1.10.1 to v1.10.3 is available",
835 before: map[string][]string{
836 "v1.10.1": {"node1"},
837 },
838 after: "v1.10.3",
839 expected: true,
840 },
841 {
842 name: "upgrade from v1.10.1 and v1.10.3/2 to v1.10.3 is available",
843 before: map[string][]string{
844 "v1.10.1": {"node1"},
845 "v1.10.3": {"node2", "node3"},
846 },
847 after: "v1.10.3",
848 expected: true,
849 },
850 {
851 name: "upgrade from v1.10.3 to v1.10.3 is not available",
852 before: map[string][]string{
853 "v1.10.3": {"node1"},
854 },
855 after: "v1.10.3",
856 expected: false,
857 },
858 {
859 name: "upgrade from v1.10.3/2 to v1.10.3 is not available",
860 before: map[string][]string{
861 "v1.10.3": {"node1", "node2"},
862 },
863 after: "v1.10.3",
864 expected: false,
865 },
866 {
867 name: "upgrade is not available if we don't know anything about the earlier state",
868 before: map[string][]string{},
869 after: "v1.10.3",
870 expected: false,
871 },
872 }
873
874 for _, rt := range tests {
875 t.Run(rt.name, func(t *testing.T) {
876 upgrade := Upgrade{
877 Before: ClusterState{
878 KubeletVersions: rt.before,
879 },
880 After: ClusterState{
881 KubeVersion: rt.after,
882 },
883 }
884 actual := upgrade.CanUpgradeKubelets()
885 if actual != rt.expected {
886 t.Errorf("failed TestKubeletUpgrade\n\texpected: %t\n\tgot: %t\n\ttest object: %v", rt.expected, actual, upgrade)
887 }
888 })
889 }
890 }
891
892 func TestGetBranchFromVersion(t *testing.T) {
893 testCases := []struct {
894 version string
895 expectedVersion string
896 }{
897 {
898 version: "v1.9.5",
899 expectedVersion: "1.9",
900 },
901 {
902 version: "v1.9.0-alpha.2",
903 expectedVersion: "1.9",
904 },
905 {
906 version: "v1.9.0-beta.0",
907 expectedVersion: "1.9",
908 },
909 {
910 version: "v1.9.0-rc.1",
911 expectedVersion: "1.9",
912 },
913 {
914 version: "v1.11.0-alpha.0",
915 expectedVersion: "1.11",
916 },
917
918 {
919 version: "v1.11.0-beta.1",
920 expectedVersion: "1.11",
921 },
922 {
923 version: "v1.11.0-rc.0",
924 expectedVersion: "1.11",
925 },
926 {
927 version: "1.12.5",
928 expectedVersion: "1.12",
929 },
930 }
931
932 for _, tc := range testCases {
933 t.Run(tc.version, func(t *testing.T) {
934 v := getBranchFromVersion(tc.version)
935 if v != tc.expectedVersion {
936 t.Errorf("expected version %s, got %s", tc.expectedVersion, v)
937 }
938 })
939 }
940 }
941
942 func TestGetSuggestedEtcdVersion(t *testing.T) {
943 constants.SupportedEtcdVersion = map[uint8]string{
944 16: "3.3.17-0",
945 17: "3.4.3-0",
946 18: "3.4.3-0",
947 19: "3.4.13-0",
948 20: "3.4.13-0",
949 21: "3.4.13-0",
950 22: "3.5.5-0",
951 }
952
953 tests := []struct {
954 name string
955 externalEtcd bool
956 kubernetesVersion string
957 expectedVersion string
958 }{
959 {
960 name: "external etcd: no version",
961 externalEtcd: true,
962 kubernetesVersion: "1.1.0",
963 expectedVersion: "",
964 },
965 {
966 name: "local etcd: illegal kubernetes version",
967 externalEtcd: false,
968 kubernetesVersion: "1.x.5",
969 expectedVersion: "N/A",
970 },
971 {
972 name: "local etcd: no supported version for 1.10.5, return the nearest version",
973 externalEtcd: false,
974 kubernetesVersion: "1.10.5",
975 expectedVersion: "3.3.17-0",
976 },
977 {
978 name: "local etcd: has supported version for 1.17.0",
979 externalEtcd: false,
980 kubernetesVersion: "1.17.0",
981 expectedVersion: "3.4.3-0",
982 },
983 {
984 name: "local etcd: no supported version for v1.99.0, return the nearest version",
985 externalEtcd: false,
986 kubernetesVersion: "v1.99.0",
987 expectedVersion: "3.5.5-0",
988 },
989 }
990 for _, tt := range tests {
991 t.Run(tt.name, func(t *testing.T) {
992 if got := getSuggestedEtcdVersion(tt.externalEtcd, tt.kubernetesVersion); got != tt.expectedVersion {
993 t.Errorf("getSuggestedEtcdVersion() want %v, got %v", tt.expectedVersion, got)
994 }
995 })
996 }
997 }
998
View as plain text