[kubeadm/app/..add other packages]Switch to github.com/pkg/errors

This commit is contained in:
qingsenLi 2018-11-09 18:54:03 +08:00
parent 66989e8bf3
commit e94dd19e03
12 changed files with 133 additions and 117 deletions

View File

@ -15,6 +15,7 @@ go_library(
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
"//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library", "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
], ],
) )
@ -35,5 +36,8 @@ go_test(
name = "go_default_test", name = "go_default_test",
srcs = ["constants_test.go"], srcs = ["constants_test.go"],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = ["//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library"], deps = [
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
) )

View File

@ -25,6 +25,8 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/pkg/errors"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/version"
bootstrapapi "k8s.io/cluster-bootstrap/token/api" bootstrapapi "k8s.io/cluster-bootstrap/token/api"
@ -382,7 +384,7 @@ func EtcdSupportedVersion(versionString string) (*version.Version, error) {
} }
return etcdVersion, nil return etcdVersion, nil
} }
return nil, fmt.Errorf("Unsupported or unknown Kubernetes version(%v)", kubernetesVersion) return nil, errors.Errorf("Unsupported or unknown Kubernetes version(%v)", kubernetesVersion)
} }
// GetStaticPodDirectory returns the location on the disk where the Static Pod should be present // GetStaticPodDirectory returns the location on the disk where the Static Pod should be present
@ -420,12 +422,12 @@ func CreateTempDirForKubeadm(dirName string) (string, error) {
tempDir := path.Join(KubernetesDir, TempDirForKubeadm) tempDir := path.Join(KubernetesDir, TempDirForKubeadm)
// creates target folder if not already exists // creates target folder if not already exists
if err := os.MkdirAll(tempDir, 0700); err != nil { if err := os.MkdirAll(tempDir, 0700); err != nil {
return "", fmt.Errorf("failed to create directory %q: %v", tempDir, err) return "", errors.Wrapf(err, "failed to create directory %q", tempDir)
} }
tempDir, err := ioutil.TempDir(tempDir, dirName) tempDir, err := ioutil.TempDir(tempDir, dirName)
if err != nil { if err != nil {
return "", fmt.Errorf("couldn't create a temporary directory: %v", err) return "", errors.Wrap(err, "couldn't create a temporary directory")
} }
return tempDir, nil return tempDir, nil
} }
@ -435,13 +437,13 @@ func CreateTimestampDirForKubeadm(dirName string) (string, error) {
tempDir := path.Join(KubernetesDir, TempDirForKubeadm) tempDir := path.Join(KubernetesDir, TempDirForKubeadm)
// creates target folder if not already exists // creates target folder if not already exists
if err := os.MkdirAll(tempDir, 0700); err != nil { if err := os.MkdirAll(tempDir, 0700); err != nil {
return "", fmt.Errorf("failed to create directory %q: %v", tempDir, err) return "", errors.Wrapf(err, "failed to create directory %q", tempDir)
} }
timestampDirName := fmt.Sprintf("%s-%s", dirName, time.Now().Format("2006-01-02-15-04-05")) timestampDirName := fmt.Sprintf("%s-%s", dirName, time.Now().Format("2006-01-02-15-04-05"))
timestampDir := path.Join(tempDir, timestampDirName) timestampDir := path.Join(tempDir, timestampDirName)
if err := os.Mkdir(timestampDir, 0700); err != nil { if err := os.Mkdir(timestampDir, 0700); err != nil {
return "", fmt.Errorf("could not create timestamp directory: %v", err) return "", errors.Wrap(err, "could not create timestamp directory")
} }
return timestampDir, nil return timestampDir, nil
@ -452,13 +454,13 @@ func GetDNSIP(svcSubnet string) (net.IP, error) {
// Get the service subnet CIDR // Get the service subnet CIDR
_, svcSubnetCIDR, err := net.ParseCIDR(svcSubnet) _, svcSubnetCIDR, err := net.ParseCIDR(svcSubnet)
if err != nil { if err != nil {
return nil, fmt.Errorf("couldn't parse service subnet CIDR %q: %v", svcSubnet, err) return nil, errors.Wrapf(err, "couldn't parse service subnet CIDR %q", svcSubnet)
} }
// Selects the 10th IP in service subnet CIDR range as dnsIP // Selects the 10th IP in service subnet CIDR range as dnsIP
dnsIP, err := ipallocator.GetIndexedIP(svcSubnetCIDR, 10) dnsIP, err := ipallocator.GetIndexedIP(svcSubnetCIDR, 10)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to get tenth IP address from service subnet CIDR %s: %v", svcSubnetCIDR.String(), err) return nil, errors.Wrapf(err, "unable to get tenth IP address from service subnet CIDR %s", svcSubnetCIDR.String())
} }
return dnsIP, nil return dnsIP, nil

View File

@ -17,11 +17,12 @@ limitations under the License.
package constants package constants
import ( import (
"fmt"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/version"
) )
@ -151,7 +152,7 @@ func TestEtcdSupportedVersion(t *testing.T) {
{ {
kubernetesVersion: "1.99.0", kubernetesVersion: "1.99.0",
expectedVersion: nil, expectedVersion: nil,
expectedError: fmt.Errorf("Unsupported or unknown Kubernetes version(1.99.0)"), expectedError: errors.New("Unsupported or unknown Kubernetes version(1.99.0)"),
}, },
{ {
kubernetesVersion: "1.10.0", kubernetesVersion: "1.10.0",

View File

@ -13,6 +13,7 @@ go_library(
deps = [ deps = [
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
], ],
) )

View File

@ -22,6 +22,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/version"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
) )
@ -63,12 +65,12 @@ func ValidateVersion(allFeatures FeatureList, requestedFeatures map[string]bool,
} }
parsedExpVersion, err := version.ParseSemantic(requestedVersion) parsedExpVersion, err := version.ParseSemantic(requestedVersion)
if err != nil { if err != nil {
return fmt.Errorf("Error parsing version %s: %v", requestedVersion, err) return errors.Wrapf(err, "error parsing version %s", requestedVersion)
} }
for k := range requestedFeatures { for k := range requestedFeatures {
if minVersion := allFeatures[k].MinimumVersion; minVersion != nil { if minVersion := allFeatures[k].MinimumVersion; minVersion != nil {
if !parsedExpVersion.AtLeast(minVersion) { if !parsedExpVersion.AtLeast(minVersion) {
return fmt.Errorf( return errors.Errorf(
"the requested Kubernetes version (%s) is incompatible with the %s feature gate, which needs %s as a minimum", "the requested Kubernetes version (%s) is incompatible with the %s feature gate, which needs %s as a minimum",
requestedVersion, k, minVersion) requestedVersion, k, minVersion)
} }
@ -134,7 +136,7 @@ func NewFeatureGate(f *FeatureList, value string) (map[string]bool, error) {
arr := strings.SplitN(s, "=", 2) arr := strings.SplitN(s, "=", 2)
if len(arr) != 2 { if len(arr) != 2 {
return nil, fmt.Errorf("missing bool value for feature-gate key:%s", s) return nil, errors.Errorf("missing bool value for feature-gate key:%s", s)
} }
k := strings.TrimSpace(arr[0]) k := strings.TrimSpace(arr[0])
@ -142,16 +144,16 @@ func NewFeatureGate(f *FeatureList, value string) (map[string]bool, error) {
featureSpec, ok := (*f)[k] featureSpec, ok := (*f)[k]
if !ok { if !ok {
return nil, fmt.Errorf("unrecognized feature-gate key: %s", k) return nil, errors.Errorf("unrecognized feature-gate key: %s", k)
} }
if featureSpec.PreRelease == utilfeature.Deprecated { if featureSpec.PreRelease == utilfeature.Deprecated {
return nil, fmt.Errorf("feature-gate key is deprecated: %s", k) return nil, errors.Errorf("feature-gate key is deprecated: %s", k)
} }
boolValue, err := strconv.ParseBool(v) boolValue, err := strconv.ParseBool(v)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid value %v for feature-gate key: %s, use true|false instead", v, k) return nil, errors.Errorf("invalid value %v for feature-gate key: %s, use true|false instead", v, k)
} }
featureGate[k] = boolValue featureGate[k] = boolValue
} }

View File

@ -31,6 +31,7 @@ go_library(
"//vendor/github.com/PuerkitoBio/purell:go_default_library", "//vendor/github.com/PuerkitoBio/purell:go_default_library",
"//vendor/github.com/blang/semver:go_default_library", "//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
], ],
) )
@ -47,6 +48,7 @@ go_test(
"//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
"//cmd/kubeadm/app/util/runtime:go_default_library", "//cmd/kubeadm/app/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/github.com/renstrom/dedent:go_default_library", "//vendor/github.com/renstrom/dedent:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library", "//vendor/k8s.io/utils/exec/testing:go_default_library",

View File

@ -22,7 +22,6 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -38,6 +37,7 @@ import (
"github.com/PuerkitoBio/purell" "github.com/PuerkitoBio/purell"
"github.com/blang/semver" "github.com/blang/semver"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/pkg/errors"
netutil "k8s.io/apimachinery/pkg/util/net" netutil "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
@ -86,7 +86,7 @@ func (e *Error) Preflight() bool {
// Checker validates the state of the system to ensure kubeadm will be // Checker validates the state of the system to ensure kubeadm will be
// successful as often as possible. // successful as often as possible.
type Checker interface { type Checker interface {
Check() (warnings, errors []error) Check() (warnings, errorList []error)
Name() string Name() string
} }
@ -101,12 +101,12 @@ func (ContainerRuntimeCheck) Name() string {
} }
// Check validates the container runtime // Check validates the container runtime
func (crc ContainerRuntimeCheck) Check() (warnings, errors []error) { func (crc ContainerRuntimeCheck) Check() (warnings, errorList []error) {
glog.V(1).Infoln("validating the container runtime") glog.V(1).Infoln("validating the container runtime")
if err := crc.runtime.IsRunning(); err != nil { if err := crc.runtime.IsRunning(); err != nil {
errors = append(errors, err) errorList = append(errorList, err)
} }
return warnings, errors return warnings, errorList
} }
// ServiceCheck verifies that the given service is enabled and active. If we do not // ServiceCheck verifies that the given service is enabled and active. If we do not
@ -127,7 +127,7 @@ func (sc ServiceCheck) Name() string {
} }
// Check validates if the service is enabled and active. // Check validates if the service is enabled and active.
func (sc ServiceCheck) Check() (warnings, errors []error) { func (sc ServiceCheck) Check() (warnings, errorList []error) {
glog.V(1).Infoln("validating if the service is enabled and active") glog.V(1).Infoln("validating if the service is enabled and active")
initSystem, err := initsystem.GetInitSystem() initSystem, err := initsystem.GetInitSystem()
if err != nil { if err != nil {
@ -137,23 +137,23 @@ func (sc ServiceCheck) Check() (warnings, errors []error) {
warnings = []error{} warnings = []error{}
if !initSystem.ServiceExists(sc.Service) { if !initSystem.ServiceExists(sc.Service) {
warnings = append(warnings, fmt.Errorf("%s service does not exist", sc.Service)) warnings = append(warnings, errors.Errorf("%s service does not exist", sc.Service))
return warnings, nil return warnings, nil
} }
if !initSystem.ServiceIsEnabled(sc.Service) { if !initSystem.ServiceIsEnabled(sc.Service) {
warnings = append(warnings, warnings = append(warnings,
fmt.Errorf("%s service is not enabled, please run 'systemctl enable %s.service'", errors.Errorf("%s service is not enabled, please run 'systemctl enable %s.service'",
sc.Service, sc.Service)) sc.Service, sc.Service))
} }
if sc.CheckIfActive && !initSystem.ServiceIsActive(sc.Service) { if sc.CheckIfActive && !initSystem.ServiceIsActive(sc.Service) {
errors = append(errors, errorList = append(errorList,
fmt.Errorf("%s service is not active, please run 'systemctl start %s.service'", errors.Errorf("%s service is not active, please run 'systemctl start %s.service'",
sc.Service, sc.Service)) sc.Service, sc.Service))
} }
return warnings, errors return warnings, errorList
} }
// FirewalldCheck checks if firewalld is enabled or active. If it is, warn the user that there may be problems // FirewalldCheck checks if firewalld is enabled or active. If it is, warn the user that there may be problems
@ -168,7 +168,7 @@ func (FirewalldCheck) Name() string {
} }
// Check validates if the firewall is enabled and active. // Check validates if the firewall is enabled and active.
func (fc FirewalldCheck) Check() (warnings, errors []error) { func (fc FirewalldCheck) Check() (warnings, errorList []error) {
glog.V(1).Infoln("validating if the firewall is enabled and active") glog.V(1).Infoln("validating if the firewall is enabled and active")
initSystem, err := initsystem.GetInitSystem() initSystem, err := initsystem.GetInitSystem()
if err != nil { if err != nil {
@ -183,11 +183,11 @@ func (fc FirewalldCheck) Check() (warnings, errors []error) {
if initSystem.ServiceIsActive("firewalld") { if initSystem.ServiceIsActive("firewalld") {
warnings = append(warnings, warnings = append(warnings,
fmt.Errorf("firewalld is active, please ensure ports %v are open or your cluster may not function correctly", errors.Errorf("firewalld is active, please ensure ports %v are open or your cluster may not function correctly",
fc.ports)) fc.ports))
} }
return warnings, errors return warnings, errorList
} }
// PortOpenCheck ensures the given port is available for use. // PortOpenCheck ensures the given port is available for use.
@ -205,18 +205,18 @@ func (poc PortOpenCheck) Name() string {
} }
// Check validates if the particular port is available. // Check validates if the particular port is available.
func (poc PortOpenCheck) Check() (warnings, errors []error) { func (poc PortOpenCheck) Check() (warnings, errorList []error) {
glog.V(1).Infof("validating availability of port %d", poc.port) glog.V(1).Infof("validating availability of port %d", poc.port)
errors = []error{} errorList = []error{}
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", poc.port)) ln, err := net.Listen("tcp", fmt.Sprintf(":%d", poc.port))
if err != nil { if err != nil {
errors = append(errors, fmt.Errorf("Port %d is in use", poc.port)) errorList = append(errorList, errors.Errorf("Port %d is in use", poc.port))
} }
if ln != nil { if ln != nil {
ln.Close() ln.Close()
} }
return nil, errors return nil, errorList
} }
// IsPrivilegedUserCheck verifies user is privileged (linux - root, windows - Administrator) // IsPrivilegedUserCheck verifies user is privileged (linux - root, windows - Administrator)
@ -242,9 +242,9 @@ func (dac DirAvailableCheck) Name() string {
} }
// Check validates if a directory does not exist or empty. // Check validates if a directory does not exist or empty.
func (dac DirAvailableCheck) Check() (warnings, errors []error) { func (dac DirAvailableCheck) Check() (warnings, errorList []error) {
glog.V(1).Infof("validating the existence and emptiness of directory %s", dac.Path) glog.V(1).Infof("validating the existence and emptiness of directory %s", dac.Path)
errors = []error{} errorList = []error{}
// If it doesn't exist we are good: // If it doesn't exist we are good:
if _, err := os.Stat(dac.Path); os.IsNotExist(err) { if _, err := os.Stat(dac.Path); os.IsNotExist(err) {
return nil, nil return nil, nil
@ -252,17 +252,17 @@ func (dac DirAvailableCheck) Check() (warnings, errors []error) {
f, err := os.Open(dac.Path) f, err := os.Open(dac.Path)
if err != nil { if err != nil {
errors = append(errors, fmt.Errorf("unable to check if %s is empty: %s", dac.Path, err)) errorList = append(errorList, errors.Wrapf(err, "unable to check if %s is empty", dac.Path))
return nil, errors return nil, errorList
} }
defer f.Close() defer f.Close()
_, err = f.Readdirnames(1) _, err = f.Readdirnames(1)
if err != io.EOF { if err != io.EOF {
errors = append(errors, fmt.Errorf("%s is not empty", dac.Path)) errorList = append(errorList, errors.Errorf("%s is not empty", dac.Path))
} }
return nil, errors return nil, errorList
} }
// FileAvailableCheck checks that the given file does not already exist. // FileAvailableCheck checks that the given file does not already exist.
@ -280,13 +280,13 @@ func (fac FileAvailableCheck) Name() string {
} }
// Check validates if the given file does not already exist. // Check validates if the given file does not already exist.
func (fac FileAvailableCheck) Check() (warnings, errors []error) { func (fac FileAvailableCheck) Check() (warnings, errorList []error) {
glog.V(1).Infof("validating the existence of file %s", fac.Path) glog.V(1).Infof("validating the existence of file %s", fac.Path)
errors = []error{} errorList = []error{}
if _, err := os.Stat(fac.Path); err == nil { if _, err := os.Stat(fac.Path); err == nil {
errors = append(errors, fmt.Errorf("%s already exists", fac.Path)) errorList = append(errorList, errors.Errorf("%s already exists", fac.Path))
} }
return nil, errors return nil, errorList
} }
// FileExistingCheck checks that the given file does not already exist. // FileExistingCheck checks that the given file does not already exist.
@ -304,13 +304,13 @@ func (fac FileExistingCheck) Name() string {
} }
// Check validates if the given file already exists. // Check validates if the given file already exists.
func (fac FileExistingCheck) Check() (warnings, errors []error) { func (fac FileExistingCheck) Check() (warnings, errorList []error) {
glog.V(1).Infof("validating the existence of file %s", fac.Path) glog.V(1).Infof("validating the existence of file %s", fac.Path)
errors = []error{} errorList = []error{}
if _, err := os.Stat(fac.Path); err != nil { if _, err := os.Stat(fac.Path); err != nil {
errors = append(errors, fmt.Errorf("%s doesn't exist", fac.Path)) errorList = append(errorList, errors.Errorf("%s doesn't exist", fac.Path))
} }
return nil, errors return nil, errorList
} }
// FileContentCheck checks that the given file contains the string Content. // FileContentCheck checks that the given file contains the string Content.
@ -329,11 +329,11 @@ func (fcc FileContentCheck) Name() string {
} }
// Check validates if the given file contains the given content. // Check validates if the given file contains the given content.
func (fcc FileContentCheck) Check() (warnings, errors []error) { func (fcc FileContentCheck) Check() (warnings, errorList []error) {
glog.V(1).Infof("validating the contents of file %s", fcc.Path) glog.V(1).Infof("validating the contents of file %s", fcc.Path)
f, err := os.Open(fcc.Path) f, err := os.Open(fcc.Path)
if err != nil { if err != nil {
return nil, []error{fmt.Errorf("%s does not exist", fcc.Path)} return nil, []error{errors.Errorf("%s does not exist", fcc.Path)}
} }
lr := io.LimitReader(f, int64(len(fcc.Content))) lr := io.LimitReader(f, int64(len(fcc.Content)))
@ -342,11 +342,11 @@ func (fcc FileContentCheck) Check() (warnings, errors []error) {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
_, err = io.Copy(buf, lr) _, err = io.Copy(buf, lr)
if err != nil { if err != nil {
return nil, []error{fmt.Errorf("%s could not be read", fcc.Path)} return nil, []error{errors.Errorf("%s could not be read", fcc.Path)}
} }
if !bytes.Equal(buf.Bytes(), fcc.Content) { if !bytes.Equal(buf.Bytes(), fcc.Content) {
return nil, []error{fmt.Errorf("%s contents are not set to %s", fcc.Path, fcc.Content)} return nil, []error{errors.Errorf("%s contents are not set to %s", fcc.Path, fcc.Content)}
} }
return nil, []error{} return nil, []error{}
@ -376,7 +376,7 @@ func (ipc InPathCheck) Check() (warnings, errs []error) {
if err != nil { if err != nil {
if ipc.mandatory { if ipc.mandatory {
// Return as an error: // Return as an error:
return nil, []error{fmt.Errorf("%s not found in system path", ipc.executable)} return nil, []error{errors.Errorf("%s not found in system path", ipc.executable)}
} }
// Return as a warning: // Return as a warning:
warningMessage := fmt.Sprintf("%s not found in system path", ipc.executable) warningMessage := fmt.Sprintf("%s not found in system path", ipc.executable)
@ -400,18 +400,18 @@ func (HostnameCheck) Name() string {
} }
// Check validates if hostname match dns sub domain regex. // Check validates if hostname match dns sub domain regex.
func (hc HostnameCheck) Check() (warnings, errors []error) { func (hc HostnameCheck) Check() (warnings, errorList []error) {
glog.V(1).Infof("checking whether the given node name is reachable using net.LookupHost") glog.V(1).Infof("checking whether the given node name is reachable using net.LookupHost")
errors = []error{} errorList = []error{}
warnings = []error{} warnings = []error{}
addr, err := net.LookupHost(hc.nodeName) addr, err := net.LookupHost(hc.nodeName)
if addr == nil { if addr == nil {
warnings = append(warnings, fmt.Errorf("hostname \"%s\" could not be reached", hc.nodeName)) warnings = append(warnings, errors.Errorf("hostname \"%s\" could not be reached", hc.nodeName))
} }
if err != nil { if err != nil {
warnings = append(warnings, fmt.Errorf("hostname \"%s\" %s", hc.nodeName, err)) warnings = append(warnings, errors.Wrapf(err, "hostname \"%s\"", hc.nodeName))
} }
return warnings, errors return warnings, errorList
} }
// HTTPProxyCheck checks if https connection to specific host is going // HTTPProxyCheck checks if https connection to specific host is going
@ -427,7 +427,7 @@ func (hst HTTPProxyCheck) Name() string {
} }
// Check validates http connectivity type, direct or via proxy. // Check validates http connectivity type, direct or via proxy.
func (hst HTTPProxyCheck) Check() (warnings, errors []error) { func (hst HTTPProxyCheck) Check() (warnings, errorList []error) {
glog.V(1).Infof("validating if the connectivity type is via proxy or direct") glog.V(1).Infof("validating if the connectivity type is via proxy or direct")
u := (&url.URL{Scheme: hst.Proto, Host: hst.Host}).String() u := (&url.URL{Scheme: hst.Proto, Host: hst.Host}).String()
@ -441,7 +441,7 @@ func (hst HTTPProxyCheck) Check() (warnings, errors []error) {
return nil, []error{err} return nil, []error{err}
} }
if proxy != nil { if 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 []error{errors.Errorf("Connection to %q uses proxy %q. If that is not intended, adjust your proxy settings", u, proxy)}, nil
} }
return nil, nil return nil, nil
} }
@ -463,7 +463,7 @@ func (HTTPProxyCIDRCheck) Name() string {
// Check validates http connectivity to first IP address in the CIDR. // Check validates http connectivity to first IP address in the CIDR.
// If it is not directly connected and goes via proxy it will produce warning. // If it is not directly connected and goes via proxy it will produce warning.
func (subnet HTTPProxyCIDRCheck) Check() (warnings, errors []error) { func (subnet HTTPProxyCIDRCheck) Check() (warnings, errorList []error) {
glog.V(1).Infoln("validating http connectivity to first IP address in the CIDR") glog.V(1).Infoln("validating http connectivity to first IP address in the CIDR")
if len(subnet.CIDR) == 0 { if len(subnet.CIDR) == 0 {
return nil, nil return nil, nil
@ -471,12 +471,12 @@ func (subnet HTTPProxyCIDRCheck) Check() (warnings, errors []error) {
_, cidr, err := net.ParseCIDR(subnet.CIDR) _, cidr, err := net.ParseCIDR(subnet.CIDR)
if err != nil { if err != nil {
return nil, []error{fmt.Errorf("error parsing CIDR %q: %v", subnet.CIDR, err)} return nil, []error{errors.Wrapf(err, "error parsing CIDR %q", subnet.CIDR)}
} }
testIP, err := ipallocator.GetIndexedIP(cidr, 1) testIP, err := ipallocator.GetIndexedIP(cidr, 1)
if err != nil { if err != nil {
return nil, []error{fmt.Errorf("unable to get first IP address from the given CIDR (%s): %v", cidr.String(), err)} return nil, []error{errors.Wrapf(err, "unable to get first IP address from the given CIDR (%s)", cidr.String())}
} }
testIPstring := testIP.String() testIPstring := testIP.String()
@ -496,7 +496,7 @@ func (subnet HTTPProxyCIDRCheck) Check() (warnings, errors []error) {
return nil, []error{err} return nil, []error{err}
} }
if proxy != nil { if proxy != nil {
return []error{fmt.Errorf("connection to %q uses proxy %q. This may lead to malfunctional cluster setup. Make sure that Pod and Services IP ranges specified correctly as exceptions in proxy configuration", subnet.CIDR, proxy)}, nil return []error{errors.Errorf("connection to %q uses proxy %q. This may lead to malfunctional cluster setup. Make sure that Pod and Services IP ranges specified correctly as exceptions in proxy configuration", subnet.CIDR, proxy)}, nil
} }
return nil, nil return nil, nil
} }
@ -512,7 +512,7 @@ func (SystemVerificationCheck) Name() string {
} }
// Check runs all individual checks // Check runs all individual checks
func (sysver SystemVerificationCheck) Check() (warnings, errors []error) { func (sysver SystemVerificationCheck) Check() (warnings, errorList []error) {
glog.V(1).Infoln("running all checks") glog.V(1).Infoln("running all checks")
// Create a buffered writer and choose a quite large value (1M) and suppose the output from the system verification test won't exceed the limit // Create a buffered writer and choose a quite large value (1M) and suppose the output from the system verification test won't exceed the limit
// Run the system verification check, but write to out buffered writer instead of stdout // Run the system verification check, but write to out buffered writer instead of stdout
@ -569,7 +569,7 @@ func (KubernetesVersionCheck) Name() string {
} }
// Check validates Kubernetes and kubeadm versions // Check validates Kubernetes and kubeadm versions
func (kubever KubernetesVersionCheck) Check() (warnings, errors []error) { func (kubever KubernetesVersionCheck) Check() (warnings, errorList []error) {
glog.V(1).Infoln("validating Kubernetes and kubeadm version") glog.V(1).Infoln("validating Kubernetes and kubeadm version")
// Skip this check for "super-custom builds", where apimachinery/the overall codebase version is not set. // Skip this check for "super-custom builds", where apimachinery/the overall codebase version is not set.
if strings.HasPrefix(kubever.KubeadmVersion, "v0.0.0") { if strings.HasPrefix(kubever.KubeadmVersion, "v0.0.0") {
@ -578,12 +578,12 @@ func (kubever KubernetesVersionCheck) Check() (warnings, errors []error) {
kadmVersion, err := versionutil.ParseSemantic(kubever.KubeadmVersion) kadmVersion, err := versionutil.ParseSemantic(kubever.KubeadmVersion)
if err != nil { if err != nil {
return nil, []error{fmt.Errorf("couldn't parse kubeadm version %q: %v", kubever.KubeadmVersion, err)} return nil, []error{errors.Wrapf(err, "couldn't parse kubeadm version %q", kubever.KubeadmVersion)}
} }
k8sVersion, err := versionutil.ParseSemantic(kubever.KubernetesVersion) k8sVersion, err := versionutil.ParseSemantic(kubever.KubernetesVersion)
if err != nil { if err != nil {
return nil, []error{fmt.Errorf("couldn't parse Kubernetes version %q: %v", kubever.KubernetesVersion, err)} return nil, []error{errors.Wrapf(err, "couldn't parse Kubernetes version %q", kubever.KubernetesVersion)}
} }
// Checks if k8sVersion greater or equal than the first unsupported versions by current version of kubeadm, // Checks if k8sVersion greater or equal than the first unsupported versions by current version of kubeadm,
@ -592,7 +592,7 @@ func (kubever KubernetesVersionCheck) Check() (warnings, errors []error) {
// thus setting the value to x.y.0-0 we are defining the very first patch - prereleases within x.y minor release. // thus setting the value to x.y.0-0 we are defining the very first patch - prereleases within x.y minor release.
firstUnsupportedVersion := versionutil.MustParseSemantic(fmt.Sprintf("%d.%d.%s", kadmVersion.Major(), kadmVersion.Minor()+1, "0-0")) firstUnsupportedVersion := versionutil.MustParseSemantic(fmt.Sprintf("%d.%d.%s", kadmVersion.Major(), kadmVersion.Minor()+1, "0-0"))
if k8sVersion.AtLeast(firstUnsupportedVersion) { if k8sVersion.AtLeast(firstUnsupportedVersion) {
return []error{fmt.Errorf("Kubernetes version is greater than kubeadm version. Please consider to upgrade kubeadm. Kubernetes version: %s. Kubeadm version: %d.%d.x", k8sVersion, kadmVersion.Components()[0], kadmVersion.Components()[1])}, nil return []error{errors.Errorf("Kubernetes version is greater than kubeadm version. Please consider to upgrade kubeadm. Kubernetes version: %s. Kubeadm version: %d.%d.x", k8sVersion, kadmVersion.Components()[0], kadmVersion.Components()[1])}, nil
} }
return nil, nil return nil, nil
@ -610,23 +610,23 @@ func (KubeletVersionCheck) Name() string {
} }
// Check validates kubelet version. It should be not less than minimal supported version // Check validates kubelet version. It should be not less than minimal supported version
func (kubever KubeletVersionCheck) Check() (warnings, errors []error) { func (kubever KubeletVersionCheck) Check() (warnings, errorList []error) {
glog.V(1).Infoln("validating kubelet version") glog.V(1).Infoln("validating kubelet version")
kubeletVersion, err := GetKubeletVersion(kubever.exec) kubeletVersion, err := GetKubeletVersion(kubever.exec)
if err != nil { if err != nil {
return nil, []error{fmt.Errorf("couldn't get kubelet version: %v", err)} return nil, []error{errors.Wrap(err, "couldn't get kubelet version")}
} }
if kubeletVersion.LessThan(kubeadmconstants.MinimumKubeletVersion) { 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{errors.Errorf("Kubelet version %q is lower than kubadm can support. Please upgrade kubelet", kubeletVersion)}
} }
if kubever.KubernetesVersion != "" { if kubever.KubernetesVersion != "" {
k8sVersion, err := versionutil.ParseSemantic(kubever.KubernetesVersion) k8sVersion, err := versionutil.ParseSemantic(kubever.KubernetesVersion)
if err != nil { if err != nil {
return nil, []error{fmt.Errorf("couldn't parse Kubernetes version %q: %v", kubever.KubernetesVersion, err)} return nil, []error{errors.Wrapf(err, "couldn't parse Kubernetes version %q", kubever.KubernetesVersion)}
} }
if kubeletVersion.Major() > k8sVersion.Major() || kubeletVersion.Minor() > k8sVersion.Minor() { if kubeletVersion.Major() > k8sVersion.Major() || kubeletVersion.Minor() > k8sVersion.Minor() {
return nil, []error{fmt.Errorf("the kubelet version is higher than the control plane version. This is not a supported version skew and may lead to a malfunctional cluster. Kubelet version: %q Control plane version: %q", kubeletVersion, k8sVersion)} return nil, []error{errors.Errorf("the kubelet version is higher than the control plane version. This is not a supported version skew and may lead to a malfunctional cluster. Kubelet version: %q Control plane version: %q", kubeletVersion, k8sVersion)}
} }
} }
return nil, nil return nil, nil
@ -641,7 +641,7 @@ func (SwapCheck) Name() string {
} }
// Check validates whether swap is enabled or not // Check validates whether swap is enabled or not
func (swc SwapCheck) Check() (warnings, errors []error) { func (swc SwapCheck) Check() (warnings, errorList []error) {
glog.V(1).Infoln("validating whether swap is enabled or not") glog.V(1).Infoln("validating whether swap is enabled or not")
f, err := os.Open("/proc/swaps") f, err := os.Open("/proc/swaps")
if err != nil { if err != nil {
@ -655,11 +655,11 @@ func (swc SwapCheck) Check() (warnings, errors []error) {
buf = append(buf, scanner.Text()) buf = append(buf, scanner.Text())
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return nil, []error{fmt.Errorf("error parsing /proc/swaps: %v", err)} return nil, []error{errors.Wrap(err, "error parsing /proc/swaps")}
} }
if len(buf) > 1 { if len(buf) > 1 {
return nil, []error{fmt.Errorf("running with swap on is not supported. Please disable swap")} return nil, []error{errors.New("running with swap on is not supported. Please disable swap")}
} }
return nil, nil return nil, nil
@ -682,7 +682,7 @@ func (ExternalEtcdVersionCheck) Name() string {
// Check validates external etcd version // Check validates external etcd version
// TODO: Use the official etcd Golang client for this instead? // TODO: Use the official etcd Golang client for this instead?
func (evc ExternalEtcdVersionCheck) Check() (warnings, errors []error) { func (evc ExternalEtcdVersionCheck) Check() (warnings, errorList []error) {
glog.V(1).Infoln("validating the external etcd version") glog.V(1).Infoln("validating the external etcd version")
// Return quickly if the user isn't using external etcd // Return quickly if the user isn't using external etcd
@ -693,46 +693,46 @@ func (evc ExternalEtcdVersionCheck) Check() (warnings, errors []error) {
var config *tls.Config var config *tls.Config
var err error var err error
if config, err = evc.configRootCAs(config); err != nil { if config, err = evc.configRootCAs(config); err != nil {
errors = append(errors, err) errorList = append(errorList, err)
return nil, errors return nil, errorList
} }
if config, err = evc.configCertAndKey(config); err != nil { if config, err = evc.configCertAndKey(config); err != nil {
errors = append(errors, err) errorList = append(errorList, err)
return nil, errors return nil, errorList
} }
client := evc.getHTTPClient(config) client := evc.getHTTPClient(config)
for _, endpoint := range evc.Etcd.External.Endpoints { for _, endpoint := range evc.Etcd.External.Endpoints {
if _, err := url.Parse(endpoint); err != nil { if _, err := url.Parse(endpoint); err != nil {
errors = append(errors, fmt.Errorf("failed to parse external etcd endpoint %s : %v", endpoint, err)) errorList = append(errorList, errors.Wrapf(err, "failed to parse external etcd endpoint %s", endpoint))
continue continue
} }
resp := etcdVersionResponse{} resp := etcdVersionResponse{}
var err error var err error
versionURL := fmt.Sprintf("%s/%s", endpoint, "version") versionURL := fmt.Sprintf("%s/%s", endpoint, "version")
if tmpVersionURL, err := purell.NormalizeURLString(versionURL, purell.FlagRemoveDuplicateSlashes); err != nil { if tmpVersionURL, err := purell.NormalizeURLString(versionURL, purell.FlagRemoveDuplicateSlashes); err != nil {
errors = append(errors, fmt.Errorf("failed to normalize external etcd version url %s : %v", versionURL, err)) errorList = append(errorList, errors.Wrapf(err, "failed to normalize external etcd version url %s", versionURL))
continue continue
} else { } else {
versionURL = tmpVersionURL versionURL = tmpVersionURL
} }
if err = getEtcdVersionResponse(client, versionURL, &resp); err != nil { if err = getEtcdVersionResponse(client, versionURL, &resp); err != nil {
errors = append(errors, err) errorList = append(errorList, err)
continue continue
} }
etcdVersion, err := semver.Parse(resp.Etcdserver) etcdVersion, err := semver.Parse(resp.Etcdserver)
if err != nil { if err != nil {
errors = append(errors, fmt.Errorf("couldn't parse external etcd version %q: %v", resp.Etcdserver, err)) errorList = append(errorList, errors.Wrapf(err, "couldn't parse external etcd version %q", resp.Etcdserver))
continue continue
} }
if etcdVersion.LT(minExternalEtcdVersion) { if etcdVersion.LT(minExternalEtcdVersion) {
errors = append(errors, fmt.Errorf("this version of kubeadm only supports external etcd version >= %s. Current version: %s", kubeadmconstants.MinExternalEtcdVersion, resp.Etcdserver)) errorList = append(errorList, errors.Errorf("this version of kubeadm only supports external etcd version >= %s. Current version: %s", kubeadmconstants.MinExternalEtcdVersion, resp.Etcdserver))
continue continue
} }
} }
return nil, errors return nil, errorList
} }
// configRootCAs configures and returns a reference to tls.Config instance if CAFile is provided // configRootCAs configures and returns a reference to tls.Config instance if CAFile is provided
@ -741,7 +741,7 @@ func (evc ExternalEtcdVersionCheck) configRootCAs(config *tls.Config) (*tls.Conf
if evc.Etcd.External.CAFile != "" { if evc.Etcd.External.CAFile != "" {
CACert, err := ioutil.ReadFile(evc.Etcd.External.CAFile) CACert, err := ioutil.ReadFile(evc.Etcd.External.CAFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("couldn't load external etcd's server certificate %s: %v", evc.Etcd.External.CAFile, err) return nil, errors.Wrapf(err, "couldn't load external etcd's server certificate %s", evc.Etcd.External.CAFile)
} }
CACertPool = x509.NewCertPool() CACertPool = x509.NewCertPool()
CACertPool.AppendCertsFromPEM(CACert) CACertPool.AppendCertsFromPEM(CACert)
@ -762,7 +762,7 @@ func (evc ExternalEtcdVersionCheck) configCertAndKey(config *tls.Config) (*tls.C
var err error var err error
cert, err = tls.LoadX509KeyPair(evc.Etcd.External.CertFile, evc.Etcd.External.KeyFile) cert, err = tls.LoadX509KeyPair(evc.Etcd.External.CertFile, evc.Etcd.External.KeyFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("couldn't load external etcd's certificate and key pair %s, %s: %v", evc.Etcd.External.CertFile, evc.Etcd.External.KeyFile, err) return nil, errors.Wrapf(err, "couldn't load external etcd's certificate and key pair %s, %s", evc.Etcd.External.CertFile, evc.Etcd.External.KeyFile)
} }
if config == nil { if config == nil {
config = &tls.Config{} config = &tls.Config{}
@ -803,7 +803,7 @@ func getEtcdVersionResponse(client *http.Client, url string, target interface{})
if r != nil && r.StatusCode >= 500 && r.StatusCode <= 599 { if r != nil && r.StatusCode >= 500 && r.StatusCode <= 599 {
loopCount-- loopCount--
return false, fmt.Errorf("server responded with non-successful status: %s", r.Status) return false, errors.Errorf("server responded with non-successful status: %s", r.Status)
} }
return true, json.NewDecoder(r.Body).Decode(target) return true, json.NewDecoder(r.Body).Decode(target)
@ -827,7 +827,7 @@ func (ImagePullCheck) Name() string {
} }
// Check pulls images required by kubeadm. This is a mutating check // Check pulls images required by kubeadm. This is a mutating check
func (ipc ImagePullCheck) Check() (warnings, errors []error) { func (ipc ImagePullCheck) Check() (warnings, errorList []error) {
for _, image := range ipc.imageList { for _, image := range ipc.imageList {
ret, err := ipc.runtime.ImageExists(image) ret, err := ipc.runtime.ImageExists(image)
if ret && err == nil { if ret && err == nil {
@ -835,14 +835,14 @@ func (ipc ImagePullCheck) Check() (warnings, errors []error) {
continue continue
} }
if err != nil { if err != nil {
errors = append(errors, fmt.Errorf("failed to check if image %s exists: %v", image, err)) errorList = append(errorList, errors.Wrapf(err, "failed to check if image %s exists", image))
} }
glog.V(1).Infof("pulling %s", image) glog.V(1).Infof("pulling %s", image)
if err := ipc.runtime.PullImage(image); err != nil { if err := ipc.runtime.PullImage(image); err != nil {
errors = append(errors, fmt.Errorf("failed to pull image %s: %v", image, err)) errorList = append(errorList, errors.Wrapf(err, "failed to pull image %s", image))
} }
} }
return warnings, errors return warnings, errorList
} }
// NumCPUCheck checks if current number of CPUs is not less than required // NumCPUCheck checks if current number of CPUs is not less than required
@ -856,12 +856,12 @@ func (NumCPUCheck) Name() string {
} }
// Check number of CPUs required by kubeadm // Check number of CPUs required by kubeadm
func (ncc NumCPUCheck) Check() (warnings, errors []error) { func (ncc NumCPUCheck) Check() (warnings, errorList []error) {
numCPU := runtime.NumCPU() numCPU := runtime.NumCPU()
if numCPU < ncc.NumCPU { if numCPU < ncc.NumCPU {
errors = append(errors, fmt.Errorf("the number of available CPUs %d is less than the required %d", numCPU, ncc.NumCPU)) errorList = append(errorList, errors.Errorf("the number of available CPUs %d is less than the required %d", numCPU, ncc.NumCPU))
} }
return warnings, errors return warnings, errorList
} }
// RunInitMasterChecks executes all individual, applicable to Master node checks. // RunInitMasterChecks executes all individual, applicable to Master node checks.

View File

@ -18,11 +18,11 @@ package preflight
import ( import (
"bytes" "bytes"
"fmt"
"io/ioutil" "io/ioutil"
"strings" "strings"
"testing" "testing"
"github.com/pkg/errors"
"github.com/renstrom/dedent" "github.com/renstrom/dedent"
"net/http" "net/http"
@ -173,12 +173,12 @@ func (pfct preflightCheckTest) Name() string {
return "preflightCheckTest" return "preflightCheckTest"
} }
func (pfct preflightCheckTest) Check() (warning, errors []error) { func (pfct preflightCheckTest) Check() (warning, errorList []error) {
if pfct.msg == "warning" { if pfct.msg == "warning" {
return []error{fmt.Errorf("warning")}, nil return []error{errors.New("warning")}, nil
} }
if pfct.msg != "" { if pfct.msg != "" {
return nil, []error{fmt.Errorf("fake error")} return nil, []error{errors.New("fake error")}
} }
return return
} }

View File

@ -19,16 +19,17 @@ limitations under the License.
package preflight package preflight
import ( import (
"fmt"
"os" "os"
"github.com/pkg/errors"
) )
// Check validates if an user has elevated (root) privileges. // Check validates if an user has elevated (root) privileges.
func (ipuc IsPrivilegedUserCheck) Check() (warnings, errors []error) { func (ipuc IsPrivilegedUserCheck) Check() (warnings, errorList []error) {
errors = []error{} errorList = []error{}
if os.Getuid() != 0 { if os.Getuid() != 0 {
errors = append(errors, fmt.Errorf("user is not running as root")) errorList = append(errorList, errors.New("user is not running as root"))
} }
return nil, errors return nil, errorList
} }

View File

@ -19,14 +19,15 @@ limitations under the License.
package preflight package preflight
import ( import (
"fmt"
"os/exec" "os/exec"
"strings" "strings"
"github.com/pkg/errors"
) )
// Check validates if an user has elevated (administrator) privileges. // Check validates if an user has elevated (administrator) privileges.
func (ipuc IsPrivilegedUserCheck) Check() (warnings, errors []error) { func (ipuc IsPrivilegedUserCheck) Check() (warnings, errorList []error) {
errors = []error{} errorList = []error{}
// The "Well-known SID" of Administrator group is S-1-5-32-544 // The "Well-known SID" of Administrator group is S-1-5-32-544
// The following powershell will return "True" if run as an administrator, "False" otherwise // The following powershell will return "True" if run as an administrator, "False" otherwise
@ -35,10 +36,10 @@ func (ipuc IsPrivilegedUserCheck) Check() (warnings, errors []error) {
isAdmin, err := exec.Command("powershell", args...).Output() isAdmin, err := exec.Command("powershell", args...).Output()
if err != nil { if err != nil {
errors = append(errors, fmt.Errorf("unable to determine if user is running as administrator: %s", err)) errorList = append(errorList, errors.Wrap(err, "unable to determine if user is running as administrator"))
} else if strings.EqualFold(strings.TrimSpace(string(isAdmin)), "false") { } else if strings.EqualFold(strings.TrimSpace(string(isAdmin)), "false") {
errors = append(errors, fmt.Errorf("user is not running as administrator")) errorList = append(errorList, errors.New("user is not running as administrator"))
} }
return nil, errors return nil, errorList
} }

View File

@ -17,10 +17,11 @@ limitations under the License.
package preflight package preflight
import ( import (
"fmt"
"regexp" "regexp"
"strings" "strings"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/version"
utilsexec "k8s.io/utils/exec" utilsexec "k8s.io/utils/exec"
) )
@ -38,7 +39,7 @@ func GetKubeletVersion(execer utilsexec.Interface) (*version.Version, error) {
cleanOutput := strings.TrimSpace(string(out)) cleanOutput := strings.TrimSpace(string(out))
subs := kubeletVersionRegex.FindAllStringSubmatch(cleanOutput, -1) subs := kubeletVersionRegex.FindAllStringSubmatch(cleanOutput, -1)
if len(subs) != 1 || len(subs[0]) < 2 { if len(subs) != 1 || len(subs[0]) < 2 {
return nil, fmt.Errorf("Unable to parse output from Kubelet: %q", cleanOutput) return nil, errors.Errorf("Unable to parse output from Kubelet: %q", cleanOutput)
} }
return version.ParseSemantic(subs[0][1]) return version.ParseSemantic(subs[0][1])
} }

View File

@ -17,9 +17,10 @@ limitations under the License.
package preflight package preflight
import ( import (
"fmt"
"testing" "testing"
"github.com/pkg/errors"
utilsexec "k8s.io/utils/exec" utilsexec "k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing" fakeexec "k8s.io/utils/exec/testing"
) )
@ -34,7 +35,7 @@ func TestGetKubeletVersion(t *testing.T) {
{"Kubernetes v1.7.0", "1.7.0", nil, true}, {"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}, {"Kubernetes v1.8.0-alpha.2.1231+afabd012389d53a", "1.8.0-alpha.2.1231+afabd012389d53a", nil, true},
{"something-invalid", "", nil, false}, {"something-invalid", "", nil, false},
{"command not found", "", fmt.Errorf("kubelet not found"), false}, {"command not found", "", errors.New("kubelet not found"), false},
{"", "", nil, false}, {"", "", nil, false},
} }