From a3cbfb6605603db70b2fe96efcf5192afea26fbb Mon Sep 17 00:00:00 2001 From: Sebastian Florek Date: Mon, 17 Jun 2024 12:27:37 -0500 Subject: [PATCH] feat: scrape minimum kubelet version from k8s nodes (#223) * feat: scrape minimum kubelet version from k8s nodes * log pinger errors --- pkg/ping/build.go | 3 ++- pkg/ping/pinger.go | 51 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/pkg/ping/build.go b/pkg/ping/build.go index 8991c7b7..7b0d88e7 100644 --- a/pkg/ping/build.go +++ b/pkg/ping/build.go @@ -8,10 +8,11 @@ import ( "k8s.io/apimachinery/pkg/version" ) -func pingAttributes(info *version.Info, pods []string) console.ClusterPing { +func pingAttributes(info *version.Info, pods []string, minKubeletVersion *string) console.ClusterPing { vs := strings.Split(info.GitVersion, "-") return console.ClusterPing{ CurrentVersion: strings.TrimPrefix(vs[0], "v"), Distro: lo.ToPtr(findDistro(append(pods, info.GitVersion))), + KubeletVersion: minKubeletVersion, } } diff --git a/pkg/ping/pinger.go b/pkg/ping/pinger.go index 3149e605..985c962f 100644 --- a/pkg/ping/pinger.go +++ b/pkg/ping/pinger.go @@ -3,12 +3,16 @@ package ping import ( "context" - "github.com/pluralsh/deployment-operator/pkg/client" + "github.com/Masterminds/semver/v3" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" "k8s.io/kubectl/pkg/cmd/util" + + "github.com/pluralsh/deployment-operator/pkg/client" ) type Pinger struct { @@ -28,23 +32,27 @@ func New(console client.Client, discovery *discovery.DiscoveryClient, factory ut func (p *Pinger) Ping() error { info, err := p.discoveryClient.ServerVersion() if err != nil { + klog.ErrorS(err, "failed to get server version") return err } cs, err := p.factory.KubernetesClientSet() if err != nil { + klog.ErrorS(err, "failed to create kubernetes clientset") return nil } - podNames := []string{} + var podNames []string // can find some distro information by checking what's running in kube-system - if pods, err := cs.CoreV1().Pods("kube-system").List(context.TODO(), metav1.ListOptions{}); err == nil { + if pods, err := cs.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{}); err == nil { podNames = lo.Map(pods.Items, func(pod corev1.Pod, ind int) string { return pod.Name }) } - attrs := pingAttributes(info, podNames) + minKubeletVersion := p.minimumKubeletVersion(cs) + + attrs := pingAttributes(info, podNames, minKubeletVersion) if err := p.consoleClient.PingCluster(attrs); err != nil { attrs.Distro = nil return p.consoleClient.PingCluster(attrs) // fallback to no distro to support old console servers @@ -52,3 +60,38 @@ func (p *Pinger) Ping() error { return nil } + +// minimumKubeletVersion tries to scrape a minimum kubelet version across all nodes in the cluster. +// It is expected that the kubelet will report to the API a valid SemVer-ish version. +// If no parsable version is found across all nodes, nil will be returned. +func (p *Pinger) minimumKubeletVersion(client *kubernetes.Clientset) *string { + nodes, err := client.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) + if err != nil { + klog.ErrorS(err, "failed to list nodes") + return nil + } + + minKubeletVersion := new(semver.Version) + for _, node := range nodes.Items { + kubeletVersion, _ := semver.NewVersion(node.Status.NodeInfo.KubeletVersion) + if kubeletVersion == nil { + continue + } + + // Initialize with first correctly parsed version + if len(minKubeletVersion.Original()) == 0 { + minKubeletVersion = kubeletVersion + continue + } + + if kubeletVersion.LessThan(minKubeletVersion) { + minKubeletVersion = kubeletVersion + } + } + + if len(minKubeletVersion.Original()) == 0 { + return nil + } + + return lo.ToPtr(minKubeletVersion.Original()) +}