forked from kubermatic/kubermatic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
version_skew.go
116 lines (92 loc) · 4.05 KB
/
version_skew.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
Copyright 2020 The Kubermatic Kubernetes Platform contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package common
import (
"context"
"fmt"
"strings"
"github.com/Masterminds/semver"
kubermaticapiv1 "github.com/kubermatic/kubermatic/api/pkg/crd/kubermatic/v1"
"github.com/kubermatic/kubermatic/api/pkg/provider"
"github.com/kubermatic/kubermatic/api/pkg/validation/nodeupdate"
clusterv1alpha1 "github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
)
// CheckClusterVersionSkew returns a list of machines and/or machine deployments
// that are running kubelet at a version incompatible with the cluster's control plane.
func CheckClusterVersionSkew(ctx context.Context, userInfoGetter provider.UserInfoGetter, clusterProvider provider.ClusterProvider, cluster *kubermaticapiv1.Cluster, projectID string) ([]string, error) {
client, err := GetClusterClient(ctx, userInfoGetter, clusterProvider, cluster, projectID)
if err != nil {
return nil, fmt.Errorf("failed to create a machine client: %v", err)
}
// get deduplicated list of all used kubelet versions
kubeletVersions, err := getKubeletVersions(ctx, client)
if err != nil {
return nil, fmt.Errorf("failed to get the list of kubelet versions used in the cluster: %v", err)
}
// this is where the incompatible versions shall be saved
incompatibleVersionsSet := map[string]bool{}
clusterVersion := cluster.Spec.Version.Semver()
for _, ver := range kubeletVersions {
kubeletVersion, parseErr := semver.NewVersion(ver)
if parseErr != nil {
return nil, fmt.Errorf("failed to parse kubelet version: %v", parseErr)
}
if err = nodeupdate.EnsureVersionCompatible(clusterVersion, kubeletVersion); err != nil {
// errVersionSkew says it's incompatible
if _, ok := err.(nodeupdate.ErrVersionSkew); ok {
incompatibleVersionsSet[kubeletVersion.String()] = true
continue
}
// other error types
return nil, fmt.Errorf("failed to check compatibility between kubelet %q and control plane %q: %v", kubeletVersion, clusterVersion, err)
}
}
// collect the deduplicated map entries into a slice
var incompatibleVersionsList []string
for ver := range incompatibleVersionsSet {
incompatibleVersionsList = append(incompatibleVersionsList, ver)
}
return incompatibleVersionsList, nil
}
// getKubeletVersions returns the list of all kubelet versions used by a given cluster's Machines and MachineDeployments
func getKubeletVersions(ctx context.Context, client ctrlruntimeclient.Client) ([]string, error) {
machineList := &clusterv1alpha1.MachineList{}
if err := client.List(ctx, machineList); err != nil {
return nil, fmt.Errorf("failed to load machines from cluster: %v", err)
}
machineDeployments := &clusterv1alpha1.MachineDeploymentList{}
if err := client.List(ctx, machineDeployments); err != nil {
return nil, KubernetesErrorToHTTPError(err)
}
kubeletVersionsSet := map[string]bool{}
// first let's go through the legacy non-MD nodes
for _, m := range machineList.Items {
// Only list Machines that are not controlled, i.e. by Machine Set.
if len(m.ObjectMeta.OwnerReferences) == 0 {
ver := strings.TrimSpace(m.Spec.Versions.Kubelet)
kubeletVersionsSet[ver] = true
}
}
// now the deployments
for _, md := range machineDeployments.Items {
ver := strings.TrimSpace(md.Spec.Template.Spec.Versions.Kubelet)
kubeletVersionsSet[ver] = true
}
// deduplicated list
kubeletVersionList := []string{}
for ver := range kubeletVersionsSet {
kubeletVersionList = append(kubeletVersionList, ver)
}
return kubeletVersionList, nil
}