Detect major version mismatches between kubeadm and kubelet.

Kubeadm supports only one minor release back, thus for 1.9
it requires minimum kubelet from 1.8.

Fixes: kubernetes/kubeadm#430
pull/6/head
Alexander Kanevskiy 2017-09-21 21:00:48 +03:00
parent b188868fd9
commit 521c84aa89
6 changed files with 151 additions and 6 deletions

View File

@ -20,9 +20,7 @@ import (
"fmt"
"io"
"io/ioutil"
"os/exec"
"path/filepath"
"strings"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
@ -220,11 +218,11 @@ func (j *Join) Run(out io.Writer) error {
kubeconfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletBootstrapKubeConfigFileName)
// Depending on the kubelet version, we might perform the TLS bootstrap or not
kubeletVersionBytes, err := exec.Command("sh", "-c", "kubelet --version").Output()
kubeletVersion, err := preflight.GetKubeletVersion()
// In case the command executed successfully and returned v1.7-something, we'll perform TLS Bootstrapping
// Otherwise, just assume v1.8
// TODO: In the beginning of the v1.9 cycle, we can remove the logic as we then don't support v1.7 anymore
if err == nil && strings.HasPrefix(string(kubeletVersionBytes), "Kubernetes v1.7") {
if err == nil && kubeletVersion.Major() == 1 && kubeletVersion.Minor() == 7 {
hostname := nodeutil.GetHostname(j.cfg.NodeName)
if err := kubeadmnode.PerformTLSBootstrap(cfg, hostname); err != nil {
return err

View File

@ -211,6 +211,9 @@ var (
// UseEnableBootstrapTokenAuthFlagVersion defines the first version where the API server supports the --enable-bootstrap-token-auth flag instead of the old and deprecated flag.
// TODO: Remove this when the v1.9 cycle starts and we bump the minimum supported version to v1.8.0
UseEnableBootstrapTokenAuthFlagVersion = version.MustParseSemantic("v1.8.0-beta.0")
// MinimumKubeletVersion specifies the minimum version of kubelet which kubeadm supports
MinimumKubeletVersion = version.MustParseSemantic("v1.8.0-beta.0")
)
// GetStaticPodDirectory returns the location on the disk where the Static Pod should be present

View File

@ -8,7 +8,10 @@ load(
go_library(
name = "go_default_library",
srcs = ["checks.go"],
srcs = [
"checks.go",
"utils.go",
],
deps = [
"//cmd/kube-apiserver/app/options:go_default_library",
"//cmd/kube-controller-manager/app/options:go_default_library",
@ -29,7 +32,10 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["checks_test.go"],
srcs = [
"checks_test.go",
"utils_test.go",
],
library = ":go_default_library",
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",

View File

@ -449,6 +449,21 @@ func (kubever KubernetesVersionCheck) Check() (warnings, errors []error) {
return nil, nil
}
// KubeletVersionCheck validates installed kubelet version
type KubeletVersionCheck struct{}
// Check validates kubelet version. It should be not less than minimal supported version
func (kubever KubeletVersionCheck) Check() (warnings, errors []error) {
kubeletVersion, err := GetKubeletVersion()
if err != nil {
return nil, []error{fmt.Errorf("couldn't get kubelet version: %v", err)}
}
if kubeletVersion.LessThan(kubeadmconstants.MinimumKubeletVersion) {
return nil, []error{fmt.Errorf("Kubelet version %q is lower than kubadm can support. Please upgrade kubelet", kubeletVersion)}
}
return nil, []error{}
}
// SwapCheck warns if swap is enabled
type SwapCheck struct{}
@ -625,6 +640,7 @@ func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error {
SystemVerificationCheck{},
IsRootCheck{},
HostnameCheck{nodeName: cfg.NodeName},
KubeletVersionCheck{},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
ServiceCheck{Service: "docker", CheckIfActive: true},
FirewalldCheck{ports: []int{int(cfg.API.BindPort), 10250}},
@ -690,6 +706,7 @@ func RunJoinNodeChecks(cfg *kubeadmapi.NodeConfiguration) error {
SystemVerificationCheck{},
IsRootCheck{},
HostnameCheck{cfg.NodeName},
KubeletVersionCheck{},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
ServiceCheck{Service: "docker", CheckIfActive: true},
PortOpenCheck{port: 10250},

View File

@ -0,0 +1,43 @@
/*
Copyright 2017 The Kubernetes Authors.
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 preflight
import (
"fmt"
"os/exec"
"regexp"
"strings"
"k8s.io/kubernetes/pkg/util/version"
)
// GetKubeletVersion is helper function that returns version of kubelet available in $PATH
func GetKubeletVersion() (*version.Version, error) {
kubeletVersionRegex := regexp.MustCompile(`^\s*Kubernetes v((0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)([-0-9a-zA-Z_\.+]*)?)\s*$`)
out, err := exec.Command("kubelet", "--version").Output()
if err != nil {
return nil, err
}
cleanOutput := strings.TrimSpace(string(out))
subs := kubeletVersionRegex.FindAllStringSubmatch(cleanOutput, -1)
if len(subs) != 1 || len(subs[0]) < 2 {
return nil, fmt.Errorf("Unable to parse output from Kubelet: %q", cleanOutput)
}
return version.ParseSemantic(subs[0][1])
}

View File

@ -0,0 +1,78 @@
/*
Copyright 2017 The Kubernetes Authors.
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 preflight
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestGetKubeletVersion(t *testing.T) {
type T struct {
output string
expected string
valid bool
}
cases := []T{
{"v1.7.0", "1.7.0", true},
{"v1.8.0-alpha.2.1231+afabd012389d53a", "1.8.0-alpha.2.1231+afabd012389d53a", true},
{"something-invalid", "", false},
}
dir, err := ioutil.TempDir("", "test-kubelet-version")
if err != nil {
t.Errorf("Failed to create directory for testing GetKubeletVersion: %v", err)
}
defer os.RemoveAll(dir)
// We don't want to call real kubelet or something else in $PATH
oldPATH := os.Getenv("PATH")
defer os.Setenv("PATH", oldPATH)
os.Setenv("PATH", dir)
// First test case, kubelet not present, should be getting error
ver, err := GetKubeletVersion()
if err == nil {
t.Errorf("failed GetKubeletVersion: expected failure when kubelet not in PATH. Result: %v", ver)
}
kubeletFn := filepath.Join(dir, "kubelet")
for _, tc := range cases {
content := []byte(fmt.Sprintf("#!/bin/sh\necho 'Kubernetes %s'", tc.output))
if err := ioutil.WriteFile(kubeletFn, content, 0755); err != nil {
t.Errorf("Error creating test stub file %s: %v", kubeletFn, err)
}
ver, err := GetKubeletVersion()
switch {
case err != nil && tc.valid:
t.Errorf("GetKubeletVersion: unexpected error for %q. Error: %v", tc.output, err)
case err == nil && !tc.valid:
t.Errorf("GetKubeletVersion: error expected for key %q, but result is %q", tc.output, ver)
case ver != nil && ver.String() != tc.expected:
t.Errorf("GetKubeletVersion: unexpected version result for key %q. Expected: %q Actual: %q", tc.output, tc.expected, ver)
}
}
}