1
16
17 package upgrade
18
19 import (
20 "context"
21 "fmt"
22
23 "github.com/pkg/errors"
24
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 versionutil "k8s.io/apimachinery/pkg/util/version"
27 pkgversion "k8s.io/apimachinery/pkg/version"
28 fakediscovery "k8s.io/client-go/discovery/fake"
29 clientset "k8s.io/client-go/kubernetes"
30 "k8s.io/component-base/version"
31
32 "k8s.io/kubernetes/cmd/kubeadm/app/constants"
33 kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
34 "k8s.io/kubernetes/cmd/kubeadm/app/util/image"
35 )
36
37
38
39 type VersionGetter interface {
40
41 ClusterVersion() (string, *versionutil.Version, error)
42
43 KubeadmVersion() (string, *versionutil.Version, error)
44
45 VersionFromCILabel(string, string) (string, *versionutil.Version, error)
46
47 KubeletVersions() (map[string][]string, error)
48
49 ComponentVersions(string) (map[string][]string, error)
50 }
51
52
53 type KubeVersionGetter struct {
54 client clientset.Interface
55 }
56
57
58 func NewKubeVersionGetter(client clientset.Interface) VersionGetter {
59 return &KubeVersionGetter{
60 client: client,
61 }
62 }
63
64
65 func (g *KubeVersionGetter) ClusterVersion() (string, *versionutil.Version, error) {
66 var (
67 clusterVersionInfo *pkgversion.Info
68 err error
69 )
70
71
72
73
74
75
76 fakeclientDiscovery, ok := g.client.Discovery().(*fakediscovery.FakeDiscovery)
77 if ok {
78 clusterVersionInfo = fakeclientDiscovery.FakedServerVersion
79 } else {
80 clusterVersionInfo, err = g.client.Discovery().ServerVersion()
81 if err != nil {
82 return "", nil, errors.Wrap(err, "Couldn't fetch cluster version from the API Server")
83 }
84 }
85
86 clusterVersion, err := versionutil.ParseSemantic(clusterVersionInfo.String())
87 if err != nil {
88 return "", nil, errors.Wrap(err, "Couldn't parse cluster version")
89 }
90 return clusterVersionInfo.String(), clusterVersion, nil
91 }
92
93
94 func (g *KubeVersionGetter) KubeadmVersion() (string, *versionutil.Version, error) {
95 kubeadmVersionInfo := version.Get()
96
97 kubeadmVersion, err := versionutil.ParseSemantic(kubeadmVersionInfo.String())
98 if err != nil {
99 return "", nil, errors.Wrap(err, "Couldn't parse kubeadm version")
100 }
101 return kubeadmVersionInfo.String(), kubeadmVersion, nil
102 }
103
104
105 func (g *KubeVersionGetter) VersionFromCILabel(ciVersionLabel, description string) (string, *versionutil.Version, error) {
106 versionStr, err := kubeadmutil.KubernetesReleaseVersion(ciVersionLabel)
107 if err != nil {
108 return "", nil, errors.Wrapf(err, "Couldn't fetch latest %s from the internet", description)
109 }
110
111 ver, err := versionutil.ParseSemantic(versionStr)
112 if err != nil {
113 return "", nil, errors.Wrapf(err, "Couldn't parse latest %s", description)
114 }
115 return versionStr, ver, nil
116 }
117
118
119 func (g *KubeVersionGetter) KubeletVersions() (map[string][]string, error) {
120 nodes, err := g.client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
121 if err != nil {
122 return nil, errors.New("couldn't list all nodes in cluster")
123 }
124
125
126 kubeletVersions := make(map[string][]string)
127 for _, node := range nodes.Items {
128 kver := node.Status.NodeInfo.KubeletVersion
129 kubeletVersions[kver] = append(kubeletVersions[kver], node.Name)
130 }
131 return kubeletVersions, nil
132 }
133
134
135
136
137 func (g *KubeVersionGetter) ComponentVersions(name string) (map[string][]string, error) {
138 podList, err := g.client.CoreV1().Pods(metav1.NamespaceSystem).List(
139 context.TODO(),
140 metav1.ListOptions{
141 LabelSelector: fmt.Sprintf("component=%s,tier=%s", name, constants.ControlPlaneTier),
142 },
143 )
144 if err != nil {
145 return nil, errors.Wrap(err, "couldn't list pods in cluster")
146 }
147
148 componentVersions := make(map[string][]string)
149 for _, pod := range podList.Items {
150 tag := convertImageTagMetadataToSemver(image.TagFromImage(pod.Spec.Containers[0].Image))
151 componentVersions[tag] = append(componentVersions[tag], pod.Spec.NodeName)
152 }
153 return componentVersions, nil
154 }
155
156
157 type OfflineVersionGetter struct {
158 VersionGetter
159 version string
160 }
161
162
163
164 func NewOfflineVersionGetter(versionGetter VersionGetter, version string) VersionGetter {
165 return &OfflineVersionGetter{
166 VersionGetter: versionGetter,
167 version: version,
168 }
169 }
170
171
172 func (o *OfflineVersionGetter) VersionFromCILabel(ciVersionLabel, description string) (string, *versionutil.Version, error) {
173 if o.version == "" {
174 return o.VersionGetter.VersionFromCILabel(ciVersionLabel, description)
175 }
176 ver, err := versionutil.ParseSemantic(o.version)
177 if err != nil {
178 return "", nil, errors.Wrapf(err, "Couldn't parse version %s", description)
179 }
180 return o.version, ver, nil
181 }
182
View as plain text