From 7aea06fce8386b44b8f5fca68b479f9656b8abc3 Mon Sep 17 00:00:00 2001 From: Simone Gotti Date: Mon, 8 Jul 2019 13:51:31 +0200 Subject: [PATCH] k8s driver: use right node selector on old k8s versions Before kubernetes 1.14 nodes were labeled with the "beta.kubernetes.io/arch" label instead of the "kubernetes.io/arch". Current k8s version (v1.15) labels nodes with both labels but it's deprecated and will removed in future versions. At driver start get the current k8s api version and choose the right label to use as node selector based on it. --- internal/services/executor/driver/k8s.go | 62 ++++++++++++++++--- internal/services/executor/driver/k8s_test.go | 49 +++++++++++++++ 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/internal/services/executor/driver/k8s.go b/internal/services/executor/driver/k8s.go index 2caa578..dc9344c 100644 --- a/internal/services/executor/driver/k8s.go +++ b/internal/services/executor/driver/k8s.go @@ -21,6 +21,8 @@ import ( "fmt" "io" "path/filepath" + "regexp" + "strconv" "strings" "time" @@ -62,6 +64,8 @@ const ( renewExecutorLeaseInterval = 10 * time.Second staleExecutorLeaseInterval = 1 * time.Minute informerResyncInterval = 10 * time.Second + + k8sLabelArchBeta = "beta.kubernetes.io/arch" ) type K8sDriver struct { @@ -77,6 +81,7 @@ type K8sDriver struct { podLister listerscorev1.PodLister cmLister listerscorev1.ConfigMapLister leaseLister coordinationlistersv1.LeaseLister + k8sLabelArch string } type K8sPod struct { @@ -106,12 +111,29 @@ func NewK8sDriver(logger *zap.Logger, executorID, toolboxPath string) (*K8sDrive } d := &K8sDriver{ - log: logger.Sugar(), - restconfig: kubecfg, - client: kubecli, - toolboxPath: toolboxPath, - namespace: namespace, - executorID: executorID, + log: logger.Sugar(), + restconfig: kubecfg, + client: kubecli, + toolboxPath: toolboxPath, + namespace: namespace, + executorID: executorID, + k8sLabelArch: corev1.LabelArchStable, + } + + serverVersion, err := d.client.Discovery().ServerVersion() + if err != nil { + return nil, err + } + sv, err := parseGitVersion(serverVersion.GitVersion) + // if server version parsing fails just warn but ignore it + if err != nil { + d.log.Warnf("failed to parse k8s server version: %v", err) + } + if sv != nil { + // for k8s version < v1.14.x use old arch label + if sv.Major == 1 && sv.Minor < 14 { + d.k8sLabelArch = k8sLabelArchBeta + } } lists, err := d.client.Discovery().ServerPreferredResources() @@ -399,7 +421,7 @@ func (d *K8sDriver) NewPod(ctx context.Context, podConfig *PodConfig, out io.Wri if podConfig.Arch != "" { pod.Spec.NodeSelector = map[string]string{ - corev1.LabelArchStable: string(podConfig.Arch), + d.k8sLabelArch: string(podConfig.Arch), } } @@ -736,3 +758,29 @@ func genEnvVars(env map[string]string) []corev1.EnvVar { } return envVars } + +type serverVersion struct { + Major int + Minor int +} + +// k8s version is in this format: v0.0.0(-master+$Format:%h$) +var gitVersionRegex = regexp.MustCompile("v([0-9]+).([0-9]+).[0-9]+.*") + +func parseGitVersion(gitVersion string) (*serverVersion, error) { + parsedVersion := gitVersionRegex.FindStringSubmatch(gitVersion) + if len(parsedVersion) != 3 { + return nil, fmt.Errorf("cannot parse git version %s", gitVersion) + } + sv := &serverVersion{} + var err error + sv.Major, err = strconv.Atoi(parsedVersion[1]) + if err != nil { + return nil, err + } + sv.Minor, err = strconv.Atoi(parsedVersion[2]) + if err != nil { + return nil, err + } + return sv, nil +} diff --git a/internal/services/executor/driver/k8s_test.go b/internal/services/executor/driver/k8s_test.go index 4073fa2..4b099c9 100644 --- a/internal/services/executor/driver/k8s_test.go +++ b/internal/services/executor/driver/k8s_test.go @@ -19,6 +19,7 @@ import ( "context" "io/ioutil" "os" + "reflect" "testing" "time" @@ -253,3 +254,51 @@ func TestK8sPod(t *testing.T) { }) } + +func TestParseGitVersion(t *testing.T) { + tests := []struct { + gitVersion string + out *serverVersion + err bool + }{ + { + gitVersion: "v1.8.0", + out: &serverVersion{Major: 1, Minor: 8}, + }, + { + gitVersion: "v1.12.0", + out: &serverVersion{Major: 1, Minor: 12}, + }, + { + gitVersion: "v1.12.20", + out: &serverVersion{Major: 1, Minor: 12}, + }, + { + gitVersion: "v1.12.8-test.10", + out: &serverVersion{Major: 1, Minor: 12}, + }, + { + gitVersion: "v1.a", + err: true, + }, + } + + for _, tt := range tests { + t.Run("", func(t *testing.T) { + sv, err := parseGitVersion(tt.gitVersion) + if tt.err { + if err == nil { + t.Errorf("expected error, got nil error") + } + return + } + if err != nil { + t.Errorf("unexpected err: %v", err) + return + } + if !reflect.DeepEqual(sv, tt.out) { + t.Errorf("expected %v, got %v", tt.out, sv) + } + }) + } +}