vendor updates

This commit is contained in:
Serguei Bezverkhi
2018-03-06 17:33:18 -05:00
parent 4b3ebc171b
commit e9033989a0
5854 changed files with 248382 additions and 119809 deletions

View File

@ -10,10 +10,39 @@ go_library(
name = "go_default_library",
srcs = [
"checks.go",
"checks_unix.go",
"utils.go",
] + select({
"@io_bazel_rules_go//go/platform:windows_amd64": [
"@io_bazel_rules_go//go/platform:android": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:darwin": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:dragonfly": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:linux": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:nacl": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:netbsd": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:openbsd": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:plan9": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:solaris": [
"checks_unix.go",
],
"@io_bazel_rules_go//go/platform:windows": [
"checks_windows.go",
],
"//conditions:default": [],
@ -22,6 +51,7 @@ go_library(
deps = [
"//cmd/kube-apiserver/app/options:go_default_library",
"//cmd/kube-controller-manager/app/options:go_default_library",
"//cmd/kube-scheduler/app:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//pkg/apis/core/validation:go_default_library",
@ -30,7 +60,6 @@ go_library(
"//pkg/util/initsystem:go_default_library",
"//pkg/util/version:go_default_library",
"//pkg/version:go_default_library",
"//plugin/cmd/kube-scheduler/app:go_default_library",
"//test/e2e_node/system:go_default_library",
"//vendor/github.com/PuerkitoBio/purell:go_default_library",
"//vendor/github.com/blang/semver:go_default_library",
@ -47,13 +76,13 @@ go_test(
"checks_test.go",
"utils_test.go",
],
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/preflight",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//vendor/github.com/renstrom/dedent:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library",
],
)

View File

@ -20,6 +20,7 @@ import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@ -44,6 +45,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
apiservoptions "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
cmoptions "k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
schedulerapp "k8s.io/kubernetes/cmd/kube-scheduler/app"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/pkg/apis/core/validation"
@ -52,7 +54,6 @@ import (
"k8s.io/kubernetes/pkg/util/initsystem"
versionutil "k8s.io/kubernetes/pkg/util/version"
kubeadmversion "k8s.io/kubernetes/pkg/version"
schedulerapp "k8s.io/kubernetes/plugin/cmd/kube-scheduler/app"
"k8s.io/kubernetes/test/e2e_node/system"
utilsexec "k8s.io/utils/exec"
)
@ -98,7 +99,12 @@ func (CRICheck) Name() string {
// Check validates the container runtime through the CRI.
func (criCheck CRICheck) Check() (warnings, errors []error) {
if err := criCheck.exec.Command("sh", "-c", fmt.Sprintf("crictl -r %s info", criCheck.socket)).Run(); err != nil {
crictlPath, err := criCheck.exec.LookPath("crictl")
if err != nil {
errors = append(errors, fmt.Errorf("unable to find command crictl: %s", err))
return warnings, errors
}
if err := criCheck.exec.Command(fmt.Sprintf("%s -r %s info", crictlPath, criCheck.socket)).Run(); err != nil {
errors = append(errors, fmt.Errorf("unable to check if the container runtime at %q is running: %s", criCheck.socket, err))
return warnings, errors
}
@ -347,6 +353,7 @@ type InPathCheck struct {
mandatory bool
exec utilsexec.Interface
label string
suggestion string
}
// Name returns label for individual InPathCheck. If not known, will return based on path.
@ -358,7 +365,7 @@ func (ipc InPathCheck) Name() string {
}
// Check validates if the given executable is present in the path.
func (ipc InPathCheck) Check() (warnings, errors []error) {
func (ipc InPathCheck) Check() (warnings, errs []error) {
_, err := ipc.exec.LookPath(ipc.executable)
if err != nil {
if ipc.mandatory {
@ -366,7 +373,11 @@ func (ipc InPathCheck) Check() (warnings, errors []error) {
return nil, []error{fmt.Errorf("%s not found in system path", ipc.executable)}
}
// Return as a warning:
return []error{fmt.Errorf("%s not found in system path", ipc.executable)}, nil
warningMessage := fmt.Sprintf("%s not found in system path", ipc.executable)
if ipc.suggestion != "" {
warningMessage += fmt.Sprintf("\nSuggestion: %s", ipc.suggestion)
}
return []error{errors.New(warningMessage)}, nil
}
return nil, nil
}
@ -404,7 +415,6 @@ func (hc HostnameCheck) Check() (warnings, errors []error) {
type HTTPProxyCheck struct {
Proto string
Host string
Port int
}
// Name returns HTTPProxy as name for HTTPProxyCheck
@ -415,19 +425,19 @@ func (hst HTTPProxyCheck) Name() string {
// Check validates http connectivity type, direct or via proxy.
func (hst HTTPProxyCheck) Check() (warnings, errors []error) {
url := fmt.Sprintf("%s://%s:%d", hst.Proto, hst.Host, hst.Port)
u := (&url.URL{Scheme: hst.Proto, Host: hst.Host}).String()
req, err := http.NewRequest("GET", url, nil)
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return nil, []error{err}
}
proxy, err := http.DefaultTransport.(*http.Transport).Proxy(req)
proxy, err := netutil.SetOldTransportDefaults(&http.Transport{}).Proxy(req)
if err != nil {
return nil, []error{err}
}
if proxy != nil {
return []error{fmt.Errorf("Connection to %q uses proxy %q. If that is not intended, adjust your proxy settings", url, proxy)}, nil
return []error{fmt.Errorf("Connection to %q uses proxy %q. If that is not intended, adjust your proxy settings", u, proxy)}, nil
}
return nil, nil
}
@ -520,14 +530,17 @@ func (eac ExtraArgsCheck) Check() (warnings, errors []error) {
}
if len(eac.ControllerManagerExtraArgs) > 0 {
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
s := cmoptions.NewCMServer()
s := cmoptions.NewKubeControllerManagerOptions()
s.AddFlags(flags, []string{}, []string{})
warnings = append(warnings, argsCheck("kube-controller-manager", eac.ControllerManagerExtraArgs, flags)...)
}
if len(eac.SchedulerExtraArgs) > 0 {
command := schedulerapp.NewSchedulerCommand()
opts, err := schedulerapp.NewOptions()
if err != nil {
warnings = append(warnings, err)
}
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
flags.AddFlagSet(command.Flags())
opts.AddFlags(flags)
warnings = append(warnings, argsCheck("kube-scheduler", eac.SchedulerExtraArgs, flags)...)
}
return warnings, nil
@ -633,6 +646,7 @@ func (kubever KubernetesVersionCheck) Check() (warnings, errors []error) {
// KubeletVersionCheck validates installed kubelet version
type KubeletVersionCheck struct {
KubernetesVersion string
exec utilsexec.Interface
}
// Name will return KubeletVersion as name for KubeletVersionCheck
@ -642,7 +656,7 @@ func (KubeletVersionCheck) Name() string {
// Check validates kubelet version. It should be not less than minimal supported version
func (kubever KubeletVersionCheck) Check() (warnings, errors []error) {
kubeletVersion, err := GetKubeletVersion()
kubeletVersion, err := GetKubeletVersion(kubever.exec)
if err != nil {
return nil, []error{fmt.Errorf("couldn't get kubelet version: %v", err)}
}
@ -795,15 +809,15 @@ func (evc ExternalEtcdVersionCheck) configCertAndKey(config *tls.Config) (*tls.C
func (evc ExternalEtcdVersionCheck) getHTTPClient(config *tls.Config) *http.Client {
if config != nil {
transport := &http.Transport{
transport := netutil.SetOldTransportDefaults(&http.Transport{
TLSClientConfig: config,
}
})
return &http.Client{
Transport: transport,
Timeout: externalEtcdRequestTimeout,
}
}
return &http.Client{Timeout: externalEtcdRequestTimeout}
return &http.Client{Timeout: externalEtcdRequestTimeout, Transport: netutil.SetOldTransportDefaults(&http.Transport{})}
}
func getEtcdVersionResponse(client *http.Client, url string, target interface{}) error {
@ -837,26 +851,30 @@ func getEtcdVersionResponse(client *http.Client, url string, target interface{})
}
// RunInitMasterChecks executes all individual, applicable to Master node checks.
func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfiguration, criSocket string, ignorePreflightErrors sets.String) error {
func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfiguration, ignorePreflightErrors sets.String) error {
// First, check if we're root separately from the other preflight checks and fail fast
if err := RunRootCheckOnly(ignorePreflightErrors); err != nil {
return err
}
// check if we can use crictl to perform checks via the CRI
criCtlChecker := InPathCheck{executable: "crictl", mandatory: false, exec: execer}
warns, _ := criCtlChecker.Check()
useCRI := len(warns) == 0
criCtlChecker := InPathCheck{
executable: "crictl",
mandatory: false,
exec: execer,
suggestion: fmt.Sprintf("go get %v", kubeadmconstants.CRICtlPackage),
}
manifestsDir := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName)
checks := []Checker{
KubernetesVersionCheck{KubernetesVersion: cfg.KubernetesVersion, KubeadmVersion: kubeadmversion.Get().GitVersion},
SystemVerificationCheck{CRISocket: criSocket},
SystemVerificationCheck{CRISocket: cfg.CRISocket},
IsPrivilegedUserCheck{},
HostnameCheck{nodeName: cfg.NodeName},
KubeletVersionCheck{KubernetesVersion: cfg.KubernetesVersion},
KubeletVersionCheck{KubernetesVersion: cfg.KubernetesVersion, exec: execer},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
ServiceCheck{Service: "docker", CheckIfActive: true}, // assume docker
FirewalldCheck{ports: []int{int(cfg.API.BindPort), 10250}},
PortOpenCheck{port: int(cfg.API.BindPort)},
PortOpenCheck{port: 10250},
@ -883,18 +901,11 @@ func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfi
ControllerManagerExtraArgs: cfg.ControllerManagerExtraArgs,
SchedulerExtraArgs: cfg.SchedulerExtraArgs,
},
HTTPProxyCheck{Proto: "https", Host: cfg.API.AdvertiseAddress, Port: int(cfg.API.BindPort)},
HTTPProxyCheck{Proto: "https", Host: cfg.API.AdvertiseAddress},
HTTPProxyCIDRCheck{Proto: "https", CIDR: cfg.Networking.ServiceSubnet},
HTTPProxyCIDRCheck{Proto: "https", CIDR: cfg.Networking.PodSubnet},
}
if useCRI {
checks = append(checks, CRICheck{socket: criSocket, exec: execer})
} else {
// assume docker
checks = append(checks, ServiceCheck{Service: "docker", CheckIfActive: true})
}
if len(cfg.Etcd.Endpoints) == 0 {
// Only do etcd related checks when no external endpoints were specified
checks = append(checks,
@ -938,30 +949,36 @@ func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfi
}
// RunJoinNodeChecks executes all individual, applicable to node checks.
func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.NodeConfiguration, criSocket string, ignorePreflightErrors sets.String) error {
func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.NodeConfiguration, ignorePreflightErrors sets.String) error {
// First, check if we're root separately from the other preflight checks and fail fast
if err := RunRootCheckOnly(ignorePreflightErrors); err != nil {
return err
}
// check if we can use crictl to perform checks via the CRI
criCtlChecker := InPathCheck{executable: "crictl", mandatory: false, exec: execer}
criCtlChecker := InPathCheck{
executable: "crictl",
mandatory: false,
exec: execer,
suggestion: fmt.Sprintf("go get %v", kubeadmconstants.CRICtlPackage),
}
warns, _ := criCtlChecker.Check()
useCRI := len(warns) == 0
checks := []Checker{
SystemVerificationCheck{CRISocket: criSocket},
SystemVerificationCheck{CRISocket: cfg.CRISocket},
IsPrivilegedUserCheck{},
HostnameCheck{cfg.NodeName},
KubeletVersionCheck{},
KubeletVersionCheck{exec: execer},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
PortOpenCheck{port: 10250},
DirAvailableCheck{Path: filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName)},
FileAvailableCheck{Path: cfg.CACertPath},
FileAvailableCheck{Path: filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletKubeConfigFileName)},
FileAvailableCheck{Path: filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletBootstrapKubeConfigFileName)},
}
if useCRI {
checks = append(checks, CRICheck{socket: criSocket, exec: execer})
checks = append(checks, CRICheck{socket: cfg.CRISocket, exec: execer})
} else {
// assume docker
checks = append(checks, ServiceCheck{Service: "docker", CheckIfActive: true})
@ -983,15 +1000,27 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.NodeConfigura
criCtlChecker)
}
if len(cfg.DiscoveryTokenAPIServers) > 0 {
if ip := net.ParseIP(cfg.DiscoveryTokenAPIServers[0]); ip != nil {
if ip.To4() == nil && ip.To16() != nil {
checks = append(checks,
FileContentCheck{Path: bridgenf6, Content: []byte{'1'}},
)
var bridgenf6Check Checker
for _, server := range cfg.DiscoveryTokenAPIServers {
ipstr, _, err := net.SplitHostPort(server)
if err == nil {
checks = append(checks,
HTTPProxyCheck{Proto: "https", Host: ipstr},
)
if bridgenf6Check == nil {
if ip := net.ParseIP(ipstr); ip != nil {
if ip.To4() == nil && ip.To16() != nil {
// This check should be added only once
bridgenf6Check = FileContentCheck{Path: bridgenf6, Content: []byte{'1'}}
}
}
}
}
}
if bridgenf6Check != nil {
checks = append(checks, bridgenf6Check)
}
return RunChecks(checks, os.Stderr, ignorePreflightErrors)
}

View File

@ -20,7 +20,6 @@ import (
"bytes"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"testing"
@ -32,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing"
)
var (
@ -223,7 +223,7 @@ func TestRunInitMasterChecks(t *testing.T) {
}
for _, rt := range tests {
actual := RunInitMasterChecks(exec.New(), rt.cfg, "", sets.NewString())
actual := RunInitMasterChecks(exec.New(), rt.cfg, sets.NewString())
if (actual == nil) != rt.expected {
t.Errorf(
"failed RunInitMasterChecks:\n\texpected: %t\n\t actual: %t\n\t error: %v",
@ -259,7 +259,7 @@ func TestRunJoinNodeChecks(t *testing.T) {
}
for _, rt := range tests {
actual := RunJoinNodeChecks(exec.New(), rt.cfg, "", sets.NewString())
actual := RunJoinNodeChecks(exec.New(), rt.cfg, sets.NewString())
if (actual == nil) != rt.expected {
t.Errorf(
"failed RunJoinNodeChecks:\n\texpected: %t\n\t actual: %t",
@ -299,6 +299,7 @@ func TestRunChecks(t *testing.T) {
{[]Checker{ExtraArgsCheck{
APIServerExtraArgs: map[string]string{"invalid-argument": "foo"},
}}, true, "\t[WARNING ExtraArgs]: kube-apiserver: failed to parse extra argument --invalid-argument=foo\n"},
{[]Checker{InPathCheck{executable: "foobar", mandatory: false, exec: exec.New(), suggestion: "install foobar"}}, true, "\t[WARNING FileExisting-foobar]: foobar not found in system path\nSuggestion: install foobar\n"},
}
for _, rt := range tokenTest {
buf := new(bytes.Buffer)
@ -503,13 +504,106 @@ func TestHTTPProxyCIDRCheck(t *testing.T) {
}
// Save current content of *_proxy and *_PROXY variables.
savedEnv := resetProxyEnv()
savedEnv := resetProxyEnv(t)
defer restoreEnv(savedEnv)
for _, rt := range tests {
warning, _ := rt.check.Check()
if (warning != nil) != rt.expectWarnings {
t.Errorf(
"failed HTTPProxyCIDRCheck:\n\texpected: %t\n\t actual: %t (CIDR:%s). Warnings: %v",
rt.expectWarnings,
(warning != nil),
rt.check.CIDR,
warning,
)
}
}
}
func TestHTTPProxyCheck(t *testing.T) {
var tests = []struct {
name string
check HTTPProxyCheck
expectWarnings bool
}{
{
name: "Loopback address",
check: HTTPProxyCheck{
Proto: "https",
Host: "127.0.0.1",
}, // Loopback addresses never should produce proxy warnings
expectWarnings: false,
},
{
name: "IPv4 direct access",
check: HTTPProxyCheck{
Proto: "https",
Host: "10.96.0.1",
}, // Expected to be accessed directly, we set NO_PROXY to 10.0.0.0/8
expectWarnings: false,
},
{
name: "IPv4 via proxy",
check: HTTPProxyCheck{
Proto: "https",
Host: "192.168.0.1",
}, // Expected to go via proxy as this range is not listed in NO_PROXY
expectWarnings: true,
},
{
name: "IPv6 direct access",
check: HTTPProxyCheck{
Proto: "https",
Host: "[2001:db8::1:15]",
}, // Expected to be accessed directly, part of 2001:db8::/48 in NO_PROXY
expectWarnings: false,
},
{
name: "IPv6 via proxy",
check: HTTPProxyCheck{
Proto: "https",
Host: "[2001:db8:1::1:15]",
}, // Expected to go via proxy, range is not in 2001:db8::/48
expectWarnings: true,
},
}
// Save current content of *_proxy and *_PROXY variables.
savedEnv := resetProxyEnv(t)
defer restoreEnv(savedEnv)
for _, rt := range tests {
warning, _ := rt.check.Check()
if (warning != nil) != rt.expectWarnings {
t.Errorf(
"%s failed HTTPProxyCheck:\n\texpected: %t\n\t actual: %t (Host:%s). Warnings: %v",
rt.name,
rt.expectWarnings,
(warning != nil),
rt.check.Host,
warning,
)
}
}
}
// resetProxyEnv is helper function that unsets all *_proxy variables
// and return previously set values as map. This can be used to restore
// original state of the environment.
func resetProxyEnv(t *testing.T) map[string]string {
savedEnv := make(map[string]string)
for _, e := range os.Environ() {
pair := strings.Split(e, "=")
if strings.HasSuffix(strings.ToLower(pair[0]), "_proxy") {
savedEnv[pair[0]] = pair[1]
os.Unsetenv(pair[0])
}
}
t.Log("Saved environment: ", savedEnv)
os.Setenv("HTTP_PROXY", "http://proxy.example.com:3128")
os.Setenv("NO_PROXY", "example.com,10.0.0.0/8,2001:db8::/48")
// Check if we can reliably execute tests:
// ProxyFromEnvironment caches the *_proxy environment variables and
// if ProxyFromEnvironment already executed before our test with empty
@ -526,33 +620,6 @@ func TestHTTPProxyCIDRCheck(t *testing.T) {
t.Skip("test skipped as ProxyFromEnvironment already initialized in environment without defined HTTP proxy")
}
t.Log("http.ProxyFromEnvironment is usable, continue executing test")
for _, rt := range tests {
warning, _ := rt.check.Check()
if (warning != nil) != rt.expectWarnings {
t.Errorf(
"failed HTTPProxyCIDRCheck:\n\texpected: %t\n\t actual: %t (CIDR:%s). Warnings: %v",
rt.expectWarnings,
(warning != nil),
rt.check.CIDR,
warning,
)
}
}
}
// resetProxyEnv is helper function that unsets all *_proxy variables
// and return previously set values as map. This can be used to restore
// original state of the environment.
func resetProxyEnv() map[string]string {
savedEnv := make(map[string]string)
for _, e := range os.Environ() {
pair := strings.Split(e, "=")
if strings.HasSuffix(strings.ToLower(pair[0]), "_proxy") {
savedEnv[pair[0]] = pair[1]
os.Unsetenv(pair[0])
}
}
return savedEnv
}
@ -565,14 +632,12 @@ func restoreEnv(e map[string]string) {
}
func TestKubeletVersionCheck(t *testing.T) {
type T struct {
cases := []struct {
kubeletVersion string
k8sVersion string
expectErrors bool
expectWarnings bool
}
cases := []T{
}{
{"v1.10.2", "", false, false}, // check minimally supported version when there is no information about control plane
{"v1.7.3", "v1.7.8", true, false}, // too old kubelet (older than kubeadmconstants.MinimumKubeletVersion), should fail.
{"v1.9.0", "v1.9.5", false, false}, // kubelet within same major.minor as control plane
@ -582,27 +647,19 @@ func TestKubeletVersionCheck(t *testing.T) {
{"v1.10.0", "v1.9.5", true, false}, // kubelet is newer (release) than control plane, should fail.
}
dir, err := ioutil.TempDir("", "test-kubelet-version-check")
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)
kubeletFn := filepath.Join(dir, "kubelet")
for _, tc := range cases {
content := []byte(fmt.Sprintf("#!/bin/sh\necho 'Kubernetes %s'", tc.kubeletVersion))
if err := ioutil.WriteFile(kubeletFn, content, 0755); err != nil {
t.Errorf("Error creating test stub file %s: %v", kubeletFn, err)
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
func() ([]byte, error) { return []byte("Kubernetes " + tc.kubeletVersion), nil },
},
}
fexec := &fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
check := KubeletVersionCheck{KubernetesVersion: tc.k8sVersion}
check := KubeletVersionCheck{KubernetesVersion: tc.k8sVersion, exec: fexec}
warnings, errors := check.Check()
switch {

View File

@ -18,18 +18,19 @@ package preflight
import (
"fmt"
"os/exec"
"regexp"
"strings"
"k8s.io/kubernetes/pkg/util/version"
utilsexec "k8s.io/utils/exec"
)
// GetKubeletVersion is helper function that returns version of kubelet available in $PATH
func GetKubeletVersion() (*version.Version, error) {
func GetKubeletVersion(execer utilsexec.Interface) (*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()
command := execer.Command("kubelet", "--version")
out, err := command.CombinedOutput()
if err != nil {
return nil, err
}

View File

@ -18,52 +18,38 @@ package preflight
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
utilsexec "k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing"
)
func TestGetKubeletVersion(t *testing.T) {
type T struct {
cases := []struct {
output string
expected string
err error
valid bool
}{
{"Kubernetes v1.7.0", "1.7.0", nil, true},
{"Kubernetes v1.8.0-alpha.2.1231+afabd012389d53a", "1.8.0-alpha.2.1231+afabd012389d53a", nil, true},
{"something-invalid", "", nil, false},
{"command not found", "", fmt.Errorf("kubelet not found"), false},
{"", "", nil, false},
}
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)
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
func() ([]byte, error) { return []byte(tc.output), tc.err },
},
}
ver, err := GetKubeletVersion()
fexec := &fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) utilsexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
ver, err := GetKubeletVersion(fexec)
switch {
case err != nil && tc.valid:
t.Errorf("GetKubeletVersion: unexpected error for %q. Error: %v", tc.output, err)