1
16
17 package upgrade
18
19 import (
20 "fmt"
21 "strings"
22
23 "github.com/pkg/errors"
24
25 "k8s.io/apimachinery/pkg/util/version"
26
27 "k8s.io/kubernetes/cmd/kubeadm/app/constants"
28 )
29
30 const (
31
32 MaximumAllowedMinorVersionUpgradeSkew = 1
33
34
35 MaximumAllowedMinorVersionDowngradeSkew = 1
36
37
38 MaximumAllowedMinorVersionKubeletSkew = 3
39 )
40
41
42 type VersionSkewPolicyErrors struct {
43 Mandatory []error
44 Skippable []error
45 }
46
47
48 func EnforceVersionPolicies(versionGetter VersionGetter, newK8sVersionStr string, newK8sVersion *version.Version, allowExperimentalUpgrades, allowRCUpgrades bool) *VersionSkewPolicyErrors {
49
50 skewErrors := &VersionSkewPolicyErrors{
51 Mandatory: []error{},
52 Skippable: []error{},
53 }
54
55 clusterVersionStr, clusterVersion, err := versionGetter.ClusterVersion()
56 if err != nil {
57
58 skewErrors.Mandatory = append(skewErrors.Mandatory, errors.Wrap(err, "Unable to fetch cluster version"))
59 return skewErrors
60 }
61 fmt.Printf("[upgrade/versions] Cluster version: %s\n", clusterVersionStr)
62
63 kubeadmVersionStr, kubeadmVersion, err := versionGetter.KubeadmVersion()
64 if err != nil {
65
66 skewErrors.Mandatory = append(skewErrors.Mandatory, errors.Wrap(err, "Unable to fetch kubeadm version"))
67 return skewErrors
68 }
69 fmt.Printf("[upgrade/versions] kubeadm version: %s\n", kubeadmVersionStr)
70
71 kubeletVersions, err := versionGetter.KubeletVersions()
72 if err != nil {
73
74 skewErrors.Skippable = append(skewErrors.Skippable, errors.Wrap(err, "Unable to fetch kubelet version"))
75 }
76
77
78 if !newK8sVersion.AtLeast(constants.MinimumControlPlaneVersion) {
79
80 skewErrors.Mandatory = append(skewErrors.Mandatory, errors.Errorf("Specified version to upgrade to %q is equal to or lower than the minimum supported version %q. Please specify a higher version to upgrade to", newK8sVersionStr, clusterVersionStr))
81 }
82
83
84 if newK8sVersion.Minor() > clusterVersion.Minor()+MaximumAllowedMinorVersionUpgradeSkew {
85 tooLargeUpgradeSkewErr := errors.Errorf("Specified version to upgrade to %q is too high; kubeadm can upgrade only %d minor version at a time", newK8sVersionStr, MaximumAllowedMinorVersionUpgradeSkew)
86
87
88 if len(newK8sVersion.PreRelease()) == 0 {
89 skewErrors.Mandatory = append(skewErrors.Mandatory, tooLargeUpgradeSkewErr)
90 } else {
91 skewErrors.Skippable = append(skewErrors.Skippable, tooLargeUpgradeSkewErr)
92 }
93 }
94
95
96 if newK8sVersion.Minor() < clusterVersion.Minor()-MaximumAllowedMinorVersionDowngradeSkew {
97 tooLargeDowngradeSkewErr := errors.Errorf("Specified version to downgrade to %q is too low; kubeadm can downgrade only %d minor version at a time", newK8sVersionStr, MaximumAllowedMinorVersionDowngradeSkew)
98
99
100 if len(newK8sVersion.PreRelease()) == 0 {
101 skewErrors.Mandatory = append(skewErrors.Mandatory, tooLargeDowngradeSkewErr)
102 } else {
103 skewErrors.Skippable = append(skewErrors.Skippable, tooLargeDowngradeSkewErr)
104 }
105 }
106
107
108 if kubeadmVersion.LessThan(newK8sVersion) {
109 if newK8sVersion.Minor() > kubeadmVersion.Minor() {
110 tooLargeKubeadmSkew := errors.Errorf("Specified version to upgrade to %q is at least one minor release higher than the kubeadm minor release (%d > %d). Such an upgrade is not supported", newK8sVersionStr, newK8sVersion.Minor(), kubeadmVersion.Minor())
111
112
113 if len(newK8sVersion.PreRelease()) == 0 {
114 skewErrors.Mandatory = append(skewErrors.Mandatory, tooLargeKubeadmSkew)
115 } else {
116 skewErrors.Skippable = append(skewErrors.Skippable, tooLargeKubeadmSkew)
117 }
118 } else {
119
120 skewErrors.Skippable = append(skewErrors.Skippable, errors.Errorf("Specified version to upgrade to %q is higher than the kubeadm version %q. Upgrade kubeadm first using the tool you used to install kubeadm", newK8sVersionStr, kubeadmVersionStr))
121 }
122 }
123
124 if kubeadmVersion.Major() > newK8sVersion.Major() ||
125 kubeadmVersion.Minor() > newK8sVersion.Minor() {
126 skewErrors.Skippable = append(skewErrors.Skippable, errors.Errorf("Kubeadm version %s can only be used to upgrade to Kubernetes version %d.%d", kubeadmVersionStr, kubeadmVersion.Major(), kubeadmVersion.Minor()))
127 }
128
129
130 if err = detectUnstableVersionError(newK8sVersion, newK8sVersionStr, allowExperimentalUpgrades, allowRCUpgrades); err != nil {
131 skewErrors.Skippable = append(skewErrors.Skippable, err)
132 }
133
134
135
136
137 if kubeletVersions != nil {
138 if err = detectTooOldKubelets(newK8sVersion, kubeletVersions); err != nil {
139 skewErrors.Skippable = append(skewErrors.Skippable, err)
140 }
141 }
142
143
144 if len(skewErrors.Skippable) == 0 && len(skewErrors.Mandatory) == 0 {
145 return nil
146 }
147
148
149 return skewErrors
150 }
151
152
153 func detectUnstableVersionError(newK8sVersion *version.Version, newK8sVersionStr string, allowExperimentalUpgrades, allowRCUpgrades bool) error {
154
155 if len(newK8sVersion.PreRelease()) == 0 {
156 return nil
157 }
158
159 if allowExperimentalUpgrades {
160 return nil
161 }
162
163 if strings.HasPrefix(newK8sVersion.PreRelease(), "rc") && allowRCUpgrades {
164 return nil
165 }
166
167 return errors.Errorf("Specified version to upgrade to %q is an unstable version and such upgrades weren't allowed via setting the --allow-*-upgrades flags", newK8sVersionStr)
168 }
169
170
171 func detectTooOldKubelets(newK8sVersion *version.Version, kubeletVersions map[string][]string) error {
172 var tooOldKubeletVersions []string
173 for versionStr := range kubeletVersions {
174
175 kubeletVersion, err := version.ParseSemantic(versionStr)
176 if err != nil {
177 return errors.Errorf("couldn't parse kubelet version %s", versionStr)
178 }
179
180 if newK8sVersion.Minor() > kubeletVersion.Minor()+MaximumAllowedMinorVersionKubeletSkew {
181 tooOldKubeletVersions = append(tooOldKubeletVersions, versionStr)
182 }
183 }
184 if len(tooOldKubeletVersions) == 0 {
185 return nil
186 }
187
188 return errors.Errorf("There are kubelets in this cluster that are too old that have these versions %v", tooOldKubeletVersions)
189 }
190
View as plain text