You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// TODO: support other CRIs for this check eventually
LicensedundertheApacheLicense, Version2.0 (the"License");
youmaynotusethisfileexceptincompliancewith the License.
YoumayobtainacopyoftheLicenseat
http://www.apache.org/licenses/LICENSE-2.0Unlessrequiredbyapplicablelaworagreedtoinwriting, softwaredistributedundertheLicenseisdistributedonan"AS IS"BASIS,
WITHOUTWARRANTIESORCONDITIONSOFANYKIND, eitherexpressor implied.
SeetheLicenseforthespecificlanguagegoverningpermissionsandlimitationsundertheLicense.
*/packagepreflightimport (
"bufio""bytes""crypto/tls""crypto/x509""encoding/json""fmt""io""io/ioutil""net""net/http""net/url""os""path/filepath""runtime""strings""time""github.com/PuerkitoBio/purell""github.com/pkg/errors"netutil"k8s.io/apimachinery/pkg/util/net""k8s.io/apimachinery/pkg/util/sets"versionutil"k8s.io/apimachinery/pkg/util/version"kubeadmversion"k8s.io/component-base/version""k8s.io/klog/v2"kubeadmapi"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"kubeadmconstants"k8s.io/kubernetes/cmd/kubeadm/app/constants""k8s.io/kubernetes/cmd/kubeadm/app/images""k8s.io/kubernetes/cmd/kubeadm/app/util/initsystem"utilruntime"k8s.io/kubernetes/cmd/kubeadm/app/util/runtime"system"k8s.io/system-validators/validators"utilsexec"k8s.io/utils/exec"utilsnet"k8s.io/utils/net"
)
const (
bridgenf="/proc/sys/net/bridge/bridge-nf-call-iptables"bridgenf6="/proc/sys/net/bridge/bridge-nf-call-ip6tables"ipv4Forward="/proc/sys/net/ipv4/ip_forward"ipv6DefaultForwarding="/proc/sys/net/ipv6/conf/default/forwarding"externalEtcdRequestTimeout=time.Duration(10*time.Second)
externalEtcdRequestRetries=3externalEtcdRequestInterval=time.Duration(5*time.Second)
)
var (
minExternalEtcdVersion=versionutil.MustParseSemantic(kubeadmconstants.MinExternalEtcdVersion)
)
// Error defines struct for communicating error messages generated by preflight checkstypeErrorstruct {
Msgstring
}
// Error implements the standard error interfacefunc (e*Error) Error() string {
returnfmt.Sprintf("[preflight] Some fatal errors occurred:\n%s%s", e.Msg, "[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`")
}
// Preflight identifies this error as a preflight errorfunc (e*Error) Preflight() bool {
returntrue
}
// Checker validates the state of the system to ensure kubeadm will be// successful as often as possible.typeCheckerinterface {
Check() (warnings, errorList []error)
Name() string
}
// ContainerRuntimeCheck verifies the container runtime.typeContainerRuntimeCheckstruct {
runtime utilruntime.ContainerRuntime
}
// Name returns label for RuntimeCheck.func (ContainerRuntimeCheck) Name() string {
return"CRI"
}
// Check validates the container runtimefunc (crcContainerRuntimeCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating the container runtime")
iferr:=crc.runtime.IsRunning(); err!=nil {
errorList=append(errorList, err)
}
returnwarnings, errorList
}
// ServiceCheck verifies that the given service is enabled and active. If we do not// detect a supported init system however, all checks are skipped and a warning is// returned.typeServiceCheckstruct {
ServicestringCheckIfActiveboolLabelstring
}
// Name returns label for ServiceCheck. If not provided, will return based on the service parameterfunc (scServiceCheck) Name() string {
ifsc.Label!="" {
returnsc.Label
}
returnfmt.Sprintf("Service-%s", strings.Title(sc.Service))
}
// Check validates if the service is enabled and active.func (scServiceCheck) Check() (warnings, errorList []error) {
klog.V(1).Infof("validating if the %q service is enabled and active", sc.Service)
initSystem, err:=initsystem.GetInitSystem()
iferr!=nil {
return []error{err}, nil
}
if!initSystem.ServiceExists(sc.Service) {
return []error{errors.Errorf("%s service does not exist", sc.Service)}, nil
}
if!initSystem.ServiceIsEnabled(sc.Service) {
warnings=append(warnings,
errors.Errorf("%s service is not enabled, please run '%s'",
sc.Service, initSystem.EnableCommand(sc.Service)))
}
ifsc.CheckIfActive&&!initSystem.ServiceIsActive(sc.Service) {
errorList=append(errorList,
errors.Errorf("%s service is not active, please run 'systemctl start %s.service'",
sc.Service, sc.Service))
}
returnwarnings, errorList
}
// FirewalldCheck checks if firewalld is enabled or active. If it is, warn the user that there may be problems// if no actions are taken.typeFirewalldCheckstruct {
ports []int
}
// Name returns label for FirewalldCheck.func (FirewalldCheck) Name() string {
return"Firewalld"
}
// Check validates if the firewall is enabled and active.func (fcFirewalldCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating if the firewall is enabled and active")
initSystem, err:=initsystem.GetInitSystem()
iferr!=nil {
return []error{err}, nil
}
if!initSystem.ServiceExists("firewalld") {
returnnil, nil
}
ifinitSystem.ServiceIsActive("firewalld") {
err:=errors.Errorf("firewalld is active, please ensure ports %v are open or your cluster may not function correctly",
fc.ports)
return []error{err}, nil
}
returnnil, nil
}
// PortOpenCheck ensures the given port is available for use.typePortOpenCheckstruct {
portintlabelstring
}
// Name returns name for PortOpenCheck. If not known, will return "PortXXXX" based on port numberfunc (pocPortOpenCheck) Name() string {
ifpoc.label!="" {
returnpoc.label
}
returnfmt.Sprintf("Port-%d", poc.port)
}
// Check validates if the particular port is available.func (pocPortOpenCheck) Check() (warnings, errorList []error) {
klog.V(1).Infof("validating availability of port %d", poc.port)
ln, err:=net.Listen("tcp", fmt.Sprintf(":%d", poc.port))
iferr!=nil {
errorList= []error{errors.Errorf("Port %d is in use", poc.port)}
}
ifln!=nil {
iferr=ln.Close(); err!=nil {
warnings=append(warnings,
errors.Errorf("when closing port %d, encountered %v", poc.port, err))
}
}
returnwarnings, errorList
}
// IsPrivilegedUserCheck verifies user is privileged (linux - root, windows - Administrator)typeIsPrivilegedUserCheckstruct{}
// Name returns name for IsPrivilegedUserCheckfunc (IsPrivilegedUserCheck) Name() string {
return"IsPrivilegedUser"
}
// IsDockerSystemdCheck verifies if Docker is setup to use systemd as the cgroup driver.typeIsDockerSystemdCheckstruct{}
// Name returns name for IsDockerSystemdCheckfunc (IsDockerSystemdCheck) Name() string {
return"IsDockerSystemdCheck"
}
// DirAvailableCheck checks if the given directory either does not exist, or is empty.typeDirAvailableCheckstruct {
PathstringLabelstring
}
// Name returns label for individual DirAvailableChecks. If not known, will return based on path.func (dacDirAvailableCheck) Name() string {
ifdac.Label!="" {
returndac.Label
}
returnfmt.Sprintf("DirAvailable-%s", strings.Replace(dac.Path, "/", "-", -1))
}
// Check validates if a directory does not exist or empty.func (dacDirAvailableCheck) Check() (warnings, errorList []error) {
klog.V(1).Infof("validating the existence and emptiness of directory %s", dac.Path)
// If it doesn't exist we are good:if_, err:=os.Stat(dac.Path); os.IsNotExist(err) {
returnnil, nil
}
f, err:=os.Open(dac.Path)
iferr!=nil {
returnnil, []error{errors.Wrapf(err, "unable to check if %s is empty", dac.Path)}
}
deferf.Close()
_, err=f.Readdirnames(1)
iferr!=io.EOF {
returnnil, []error{errors.Errorf("%s is not empty", dac.Path)}
}
returnnil, nil
}
// FileAvailableCheck checks that the given file does not already exist.typeFileAvailableCheckstruct {
PathstringLabelstring
}
// Name returns label for individual FileAvailableChecks. If not known, will return based on path.func (facFileAvailableCheck) Name() string {
iffac.Label!="" {
returnfac.Label
}
returnfmt.Sprintf("FileAvailable-%s", strings.Replace(fac.Path, "/", "-", -1))
}
// Check validates if the given file does not already exist.func (facFileAvailableCheck) Check() (warnings, errorList []error) {
klog.V(1).Infof("validating the existence of file %s", fac.Path)
if_, err:=os.Stat(fac.Path); err==nil {
returnnil, []error{errors.Errorf("%s already exists", fac.Path)}
}
returnnil, nil
}
// FileExistingCheck checks that the given file does not already exist.typeFileExistingCheckstruct {
PathstringLabelstring
}
// Name returns label for individual FileExistingChecks. If not known, will return based on path.func (facFileExistingCheck) Name() string {
iffac.Label!="" {
returnfac.Label
}
returnfmt.Sprintf("FileExisting-%s", strings.Replace(fac.Path, "/", "-", -1))
}
// Check validates if the given file already exists.func (facFileExistingCheck) Check() (warnings, errorList []error) {
klog.V(1).Infof("validating the existence of file %s", fac.Path)
if_, err:=os.Stat(fac.Path); err!=nil {
returnnil, []error{errors.Errorf("%s doesn't exist", fac.Path)}
}
returnnil, nil
}
// FileContentCheck checks that the given file contains the string Content.typeFileContentCheckstruct {
PathstringContent []byteLabelstring
}
// Name returns label for individual FileContentChecks. If not known, will return based on path.func (fccFileContentCheck) Name() string {
iffcc.Label!="" {
returnfcc.Label
}
returnfmt.Sprintf("FileContent-%s", strings.Replace(fcc.Path, "/", "-", -1))
}
// Check validates if the given file contains the given content.func (fccFileContentCheck) Check() (warnings, errorList []error) {
klog.V(1).Infof("validating the contents of file %s", fcc.Path)
f, err:=os.Open(fcc.Path)
iferr!=nil {
returnnil, []error{errors.Errorf("%s does not exist", fcc.Path)}
}
lr:=io.LimitReader(f, int64(len(fcc.Content)))
deferf.Close()
buf:=&bytes.Buffer{}
_, err=io.Copy(buf, lr)
iferr!=nil {
returnnil, []error{errors.Errorf("%s could not be read", fcc.Path)}
}
if!bytes.Equal(buf.Bytes(), fcc.Content) {
returnnil, []error{errors.Errorf("%s contents are not set to %s", fcc.Path, fcc.Content)}
}
returnnil, []error{}
}
// InPathCheck checks if the given executable is present in $PATHtypeInPathCheckstruct {
executablestringmandatoryboolexec utilsexec.Interfacelabelstringsuggestionstring
}
// Name returns label for individual InPathCheck. If not known, will return based on path.func (ipcInPathCheck) Name() string {
ifipc.label!="" {
returnipc.label
}
returnfmt.Sprintf("FileExisting-%s", strings.Replace(ipc.executable, "/", "-", -1))
}
// Check validates if the given executable is present in the path.func (ipcInPathCheck) Check() (warnings, errs []error) {
klog.V(1).Infof("validating the presence of executable %s", ipc.executable)
_, err:=ipc.exec.LookPath(ipc.executable)
iferr!=nil {
ifipc.mandatory {
// Return as an error:returnnil, []error{errors.Errorf("%s not found in system path", ipc.executable)}
}
// Return as a warning:warningMessage:=fmt.Sprintf("%s not found in system path", ipc.executable)
ifipc.suggestion!="" {
warningMessage+=fmt.Sprintf("\nSuggestion: %s", ipc.suggestion)
}
return []error{errors.New(warningMessage)}, nil
}
returnnil, nil
}
// HostnameCheck checks if hostname match dns sub domain regex.// If hostname doesn't match this regex, kubelet will not launch static pods like kube-apiserver/kube-controller-manager and so on.typeHostnameCheckstruct {
nodeNamestring
}
// Name will return Hostname as name for HostnameCheckfunc (HostnameCheck) Name() string {
return"Hostname"
}
// Check validates if hostname match dns sub domain regex.func (hcHostnameCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("checking whether the given node name is reachable using net.LookupHost")
addr, err:=net.LookupHost(hc.nodeName)
ifaddr==nil {
warnings=append(warnings, errors.Errorf("hostname \"%s\" could not be reached", hc.nodeName))
}
iferr!=nil {
warnings=append(warnings, errors.Wrapf(err, "hostname \"%s\"", hc.nodeName))
}
returnwarnings, errorList
}
// HTTPProxyCheck checks if https connection to specific host is going// to be done directly or over proxy. If proxy detected, it will return warning.typeHTTPProxyCheckstruct {
ProtostringHoststring
}
// Name returns HTTPProxy as name for HTTPProxyCheckfunc (hstHTTPProxyCheck) Name() string {
return"HTTPProxy"
}
// Check validates http connectivity type, direct or via proxy.func (hstHTTPProxyCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating if the connectivity type is via proxy or direct")
u:=&url.URL{Scheme: hst.Proto, Host: hst.Host}
ifutilsnet.IsIPv6String(hst.Host) {
u.Host=net.JoinHostPort(hst.Host, "1234")
}
req, err:=http.NewRequest("GET", u.String(), nil)
iferr!=nil {
returnnil, []error{err}
}
proxy, err:=netutil.SetOldTransportDefaults(&http.Transport{}).Proxy(req)
iferr!=nil {
returnnil, []error{err}
}
ifproxy!=nil {
return []error{errors.Errorf("Connection to %q uses proxy %q. If that is not intended, adjust your proxy settings", u, proxy)}, nil
}
returnnil, nil
}
// HTTPProxyCIDRCheck checks if https connection to specific subnet is going// to be done directly or over proxy. If proxy detected, it will return warning.// Similar to HTTPProxyCheck above, but operates with subnets and uses API// machinery transport defaults to simulate kube-apiserver accessing cluster// services and pods.typeHTTPProxyCIDRCheckstruct {
ProtostringCIDRstring
}
// Name will return HTTPProxyCIDR as name for HTTPProxyCIDRCheckfunc (HTTPProxyCIDRCheck) Name() string {
return"HTTPProxyCIDR"
}
// 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.func (subnetHTTPProxyCIDRCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating http connectivity to first IP address in the CIDR")
iflen(subnet.CIDR) ==0 {
returnnil, nil
}
_, cidr, err:=net.ParseCIDR(subnet.CIDR)
iferr!=nil {
returnnil, []error{errors.Wrapf(err, "error parsing CIDR %q", subnet.CIDR)}
}
testIP, err:=utilsnet.GetIndexedIP(cidr, 1)
iferr!=nil {
returnnil, []error{errors.Wrapf(err, "unable to get first IP address from the given CIDR (%s)", cidr.String())}
}
testIPstring:=testIP.String()
iflen(testIP) ==net.IPv6len {
testIPstring=fmt.Sprintf("[%s]:1234", testIP)
}
url:=fmt.Sprintf("%s://%s/", subnet.Proto, testIPstring)
req, err:=http.NewRequest("GET", url, nil)
iferr!=nil {
returnnil, []error{err}
}
// Utilize same transport defaults as it will be used by API serverproxy, err:=netutil.SetOldTransportDefaults(&http.Transport{}).Proxy(req)
iferr!=nil {
returnnil, []error{err}
}
ifproxy!=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
}
returnnil, nil
}
// SystemVerificationCheck defines struct used for running the system verification node check in test/e2e_node/systemtypeSystemVerificationCheckstruct {
IsDockerbool
}
// Name will return SystemVerification as name for SystemVerificationCheckfunc (SystemVerificationCheck) Name() string {
return"SystemVerification"
}
// Check runs all individual checksfunc (sysverSystemVerificationCheck) Check() (warnings, errorList []error) {
klog.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// Run the system verification check, but write to out buffered writer instead of stdoutbufw:=bufio.NewWriterSize(os.Stdout, 1*1024*1024)
reporter:=&system.StreamReporter{WriteStream: bufw}
varerrs []errorvarwarns []error// All the common validators we'd like to run:varvalidators= []system.Validator{
&system.KernelValidator{Reporter: reporter}}
// run the docker validator only with docker runtimeifsysver.IsDocker {
validators=append(validators, &system.DockerValidator{Reporter: reporter})
}
ifruntime.GOOS=="linux" {
//add linux validatorsvalidators=append(validators,
&system.OSValidator{Reporter: reporter},
&system.CgroupsValidator{Reporter: reporter})
}
// Run all validatorsfor_, v:=rangevalidators {
warn, err:=v.Validate(system.DefaultSysSpec)
iferr!=nil {
errs=append(errs, err...)
}
ifwarn!=nil {
warns=append(warns, warn...)
}
}
iflen(errs) !=0 {
// Only print the output from the system verification check if the check failedfmt.Println("[preflight] The system verification failed. Printing the output from the verification:")
bufw.Flush()
returnwarns, errs
}
returnwarns, nil
}
// KubernetesVersionCheck validates Kubernetes and kubeadm versionstypeKubernetesVersionCheckstruct {
KubeadmVersionstringKubernetesVersionstring
}
// Name will return KubernetesVersion as name for KubernetesVersionCheckfunc (KubernetesVersionCheck) Name() string {
return"KubernetesVersion"
}
// Check validates Kubernetes and kubeadm versionsfunc (kubeverKubernetesVersionCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating Kubernetes and kubeadm version")
// Skip this check for "super-custom builds", where apimachinery/the overall codebase version is not set.ifstrings.HasPrefix(kubever.KubeadmVersion, "v0.0.0") {
returnnil, nil
}
kadmVersion, err:=versionutil.ParseSemantic(kubever.KubeadmVersion)
iferr!=nil {
returnnil, []error{errors.Wrapf(err, "couldn't parse kubeadm version %q", kubever.KubeadmVersion)}
}
k8sVersion, err:=versionutil.ParseSemantic(kubever.KubernetesVersion)
iferr!=nil {
returnnil, []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,// that is major.minor+1 (all patch and pre-releases versions included)// NB. in semver patches number is a numeric, while prerelease is a string where numeric identifiers always have lower precedence than non-numeric identifiers.// 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"))
ifk8sVersion.AtLeast(firstUnsupportedVersion) {
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
}
returnnil, nil
}
// KubeletVersionCheck validates installed kubelet versiontypeKubeletVersionCheckstruct {
KubernetesVersionstringexec utilsexec.Interface
}
// Name will return KubeletVersion as name for KubeletVersionCheckfunc (KubeletVersionCheck) Name() string {
return"KubeletVersion"
}
// Check validates kubelet version. It should be not less than minimal supported versionfunc (kubeverKubeletVersionCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating kubelet version")
kubeletVersion, err:=GetKubeletVersion(kubever.exec)
iferr!=nil {
returnnil, []error{errors.Wrap(err, "couldn't get kubelet version")}
}
ifkubeletVersion.LessThan(kubeadmconstants.MinimumKubeletVersion) {
returnnil, []error{errors.Errorf("Kubelet version %q is lower than kubeadm can support. Please upgrade kubelet", kubeletVersion)}
}
ifkubever.KubernetesVersion!="" {
k8sVersion, err:=versionutil.ParseSemantic(kubever.KubernetesVersion)
iferr!=nil {
returnnil, []error{errors.Wrapf(err, "couldn't parse Kubernetes version %q", kubever.KubernetesVersion)}
}
ifkubeletVersion.Major() >k8sVersion.Major() ||kubeletVersion.Minor() >k8sVersion.Minor() {
returnnil, []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)}
}
}
returnnil, nil
}
// SwapCheck warns if swap is enabledtypeSwapCheckstruct{}
// Name will return Swap as name for SwapCheckfunc (SwapCheck) Name() string {
return"Swap"
}
// Check validates whether swap is enabled or notfunc (swcSwapCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating whether swap is enabled or not")
f, err:=os.Open("/proc/swaps")
iferr!=nil {
// /proc/swaps not available, thus no reasons to warnreturnnil, nil
}
deferf.Close()
varbuf []stringscanner:=bufio.NewScanner(f)
forscanner.Scan() {
buf=append(buf, scanner.Text())
}
iferr:=scanner.Err(); err!=nil {
returnnil, []error{errors.Wrap(err, "error parsing /proc/swaps")}
}
iflen(buf) >1 {
returnnil, []error{errors.New("running with swap on is not supported. Please disable swap")}
}
returnnil, nil
}
typeetcdVersionResponsestruct {
Etcdserverstring`json:"etcdserver"`Etcdclusterstring`json:"etcdcluster"`
}
// ExternalEtcdVersionCheck checks if version of external etcd meets the demand of kubeadmtypeExternalEtcdVersionCheckstruct {
Etcd kubeadmapi.Etcd
}
// Name will return ExternalEtcdVersion as name for ExternalEtcdVersionCheckfunc (ExternalEtcdVersionCheck) Name() string {
return"ExternalEtcdVersion"
}
// Check validates external etcd version// TODO: Use the official etcd Golang client for this instead?func (evcExternalEtcdVersionCheck) Check() (warnings, errorList []error) {
klog.V(1).Infoln("validating the external etcd version")
// Return quickly if the user isn't using external etcdifevc.Etcd.External.Endpoints==nil {
returnnil, nil
}
varconfig*tls.Configvarerrerrorifconfig, err=evc.configRootCAs(config); err!=nil {
errorList=append(errorList, err)
returnnil, errorList
}
ifconfig, err=evc.configCertAndKey(config); err!=nil {
errorList=append(errorList, err)
returnnil, errorList
}
client:=evc.getHTTPClient(config)
for_, endpoint:=rangeevc.Etcd.External.Endpoints {
if_, err:=url.Parse(endpoint); err!=nil {
errorList=append(errorList, errors.Wrapf(err, "failed to parse external etcd endpoint %s", endpoint))
continue
}
resp:=etcdVersionResponse{}
varerrerrorversionURL:=fmt.Sprintf("%s/%s", endpoint, "version")
iftmpVersionURL, err:=purell.NormalizeURLString(versionURL, purell.FlagRemoveDuplicateSlashes); err!=nil {
errorList=append(errorList, errors.Wrapf(err, "failed to normalize external etcd version url %s", versionURL))
continue
} else {
versionURL=tmpVersionURL
}
iferr=getEtcdVersionResponse(client, versionURL, &resp); err!=nil {
errorList=append(errorList, err)
continue
}
etcdVersion, err:=versionutil.ParseSemantic(resp.Etcdserver)
iferr!=nil {
errorList=append(errorList, errors.Wrapf(err, "couldn't parse external etcd version %q", resp.Etcdserver))
continue
}
ifetcdVersion.LessThan(minExternalEtcdVersion) {
errorList=append(errorList, errors.Errorf("this version of kubeadm only supports external etcd version >= %s. Current version: %s", kubeadmconstants.MinExternalEtcdVersion, resp.Etcdserver))
continue
}
}
returnnil, errorList
}
// configRootCAs configures and returns a reference to tls.Config instance if CAFile is providedfunc (evcExternalEtcdVersionCheck) configRootCAs(config*tls.Config) (*tls.Config, error) {
varCACertPool*x509.CertPoolifevc.Etcd.External.CAFile!="" {
CACert, err:=ioutil.ReadFile(evc.Etcd.External.CAFile)
iferr!=nil {
returnnil, errors.Wrapf(err, "couldn't load external etcd's server certificate %s", evc.Etcd.External.CAFile)
}
CACertPool=x509.NewCertPool()
CACertPool.AppendCertsFromPEM(CACert)
}
ifCACertPool!=nil {
ifconfig==nil {
config=&tls.Config{}
}
config.RootCAs=CACertPool
}
returnconfig, nil
}
// configCertAndKey configures and returns a reference to tls.Config instance if CertFile and KeyFile pair is providedfunc (evcExternalEtcdVersionCheck) configCertAndKey(config*tls.Config) (*tls.Config, error) {
varcert tls.Certificateifevc.Etcd.External.CertFile!=""&&evc.Etcd.External.KeyFile!="" {
varerrerrorcert, err=tls.LoadX509KeyPair(evc.Etcd.External.CertFile, evc.Etcd.External.KeyFile)
iferr!=nil {
returnnil, errors.Wrapf(err, "couldn't load external etcd's certificate and key pair %s, %s", evc.Etcd.External.CertFile, evc.Etcd.External.KeyFile)
}
ifconfig==nil {
config=&tls.Config{}
}
config.Certificates= []tls.Certificate{cert}
}
returnconfig, nil
}
func (evcExternalEtcdVersionCheck) getHTTPClient(config*tls.Config) *http.Client {
ifconfig!=nil {
transport:=netutil.SetOldTransportDefaults(&http.Transport{
TLSClientConfig: config,
})
return&http.Client{
Transport: transport,
Timeout: externalEtcdRequestTimeout,
}
}
return&http.Client{Timeout: externalEtcdRequestTimeout, Transport: netutil.SetOldTransportDefaults(&http.Transport{})}
}
funcgetEtcdVersionResponse(client*http.Client, urlstring, targetinterface{}) error {
loopCount:=externalEtcdRequestRetries+1varerrerrorvarstopRetryboolforloopCount>0 {
ifloopCount<=externalEtcdRequestRetries {
time.Sleep(externalEtcdRequestInterval)
}
stopRetry, err=func() (stopRetrybool, errerror) {
r, err:=client.Get(url)
iferr!=nil {
loopCount--returnfalse, err
}
//lint:ignore SA5011 If err != nil we are already returning.deferr.Body.Close()
ifr!=nil&&r.StatusCode>=500&&r.StatusCode<=599 {
loopCount--returnfalse, errors.Errorf("server responded with non-successful status: %s", r.Status)
}
returntrue, json.NewDecoder(r.Body).Decode(target)
}()
ifstopRetry {
break
}
}
returnerr
}
// ImagePullCheck will pull container images used by kubeadmtypeImagePullCheckstruct {
runtime utilruntime.ContainerRuntimeimageList []string
}
// Name returns the label for ImagePullCheckfunc (ImagePullCheck) Name() string {
return"ImagePull"
}
// Check pulls images required by kubeadm. This is a mutating checkfunc (ipcImagePullCheck) Check() (warnings, errorList []error) {
for_, image:=rangeipc.imageList {
ret, err:=ipc.runtime.ImageExists(image)
ifret&&err==nil {
klog.V(1).Infof("image exists: %s", image)
continue
}
iferr!=nil {
errorList=append(errorList, errors.Wrapf(err, "failed to check if image %s exists", image))
}
klog.V(1).Infof("pulling %s", image)
iferr:=ipc.runtime.PullImage(image); err!=nil {
errorList=append(errorList, errors.Wrapf(err, "failed to pull image %s", image))
}
}
returnwarnings, errorList
}
// NumCPUCheck checks if current number of CPUs is not less than requiredtypeNumCPUCheckstruct {
NumCPUint
}
// Name returns the label for NumCPUCheckfunc (NumCPUCheck) Name() string {
return"NumCPU"
}
// Check number of CPUs required by kubeadmfunc (nccNumCPUCheck) Check() (warnings, errorList []error) {
numCPU:=runtime.NumCPU()
ifnumCPU<ncc.NumCPU {
errorList=append(errorList, errors.Errorf("the number of available CPUs %d is less than the required %d", numCPU, ncc.NumCPU))
}
returnwarnings, errorList
}
// MemCheck checks if the number of megabytes of memory is not less than requiredtypeMemCheckstruct {
Memuint64
}
// Name returns the label for memoryfunc (MemCheck) Name() string {
return"Mem"
}
// RunInitNodeChecks executes all individual, applicable to control-plane node checks.// The boolean flag 'isSecondaryControlPlane' controls whether we are running checks in a --join-control-plane scenario.// The boolean flag 'downloadCerts' controls whether we should skip checks on certificates because we are downloading them.// If the flag is set to true we should skip checks already executed by RunJoinNodeChecks.funcRunInitNodeChecks(execer utilsexec.Interface, cfg*kubeadmapi.InitConfiguration, ignorePreflightErrors sets.String, isSecondaryControlPlanebool, downloadCertsbool) error {
if!isSecondaryControlPlane {
// First, check if we're root separately from the other preflight checks and fail fastiferr:=RunRootCheckOnly(ignorePreflightErrors); err!=nil {
returnerr
}
}
manifestsDir:=filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName)
checks:= []Checker{
NumCPUCheck{NumCPU: kubeadmconstants.ControlPlaneNumCPU},
// Linux only// TODO: support other OS, if control-plane is supported on it.MemCheck{Mem: kubeadmconstants.ControlPlaneMem},
KubernetesVersionCheck{KubernetesVersion: cfg.KubernetesVersion, KubeadmVersion: kubeadmversion.Get().GitVersion},
FirewalldCheck{ports: []int{int(cfg.LocalAPIEndpoint.BindPort), kubeadmconstants.KubeletPort}},
PortOpenCheck{port: int(cfg.LocalAPIEndpoint.BindPort)},
PortOpenCheck{port: kubeadmconstants.KubeSchedulerPort},
PortOpenCheck{port: kubeadmconstants.KubeControllerManagerPort},
FileAvailableCheck{Path: kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.KubeAPIServer, manifestsDir)},
FileAvailableCheck{Path: kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.KubeControllerManager, manifestsDir)},
FileAvailableCheck{Path: kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.KubeScheduler, manifestsDir)},
FileAvailableCheck{Path: kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.Etcd, manifestsDir)},
HTTPProxyCheck{Proto: "https", Host: cfg.LocalAPIEndpoint.AdvertiseAddress},
}
cidrs:=strings.Split(cfg.Networking.ServiceSubnet, ",")
for_, cidr:=rangecidrs {
checks=append(checks, HTTPProxyCIDRCheck{Proto: "https", CIDR: cidr})
}
cidrs=strings.Split(cfg.Networking.PodSubnet, ",")
for_, cidr:=rangecidrs {
checks=append(checks, HTTPProxyCIDRCheck{Proto: "https", CIDR: cidr})
}
if!isSecondaryControlPlane {
checks=addCommonChecks(execer, cfg.KubernetesVersion, &cfg.NodeRegistration, checks)
// Check if Bridge-netfilter and IPv6 relevant flags are setifip:=net.ParseIP(cfg.LocalAPIEndpoint.AdvertiseAddress); ip!=nil {
ifutilsnet.IsIPv6(ip) {
checks=append(checks,
FileContentCheck{Path: bridgenf6, Content: []byte{'1'}},
FileContentCheck{Path: ipv6DefaultForwarding, Content: []byte{'1'}},
)
}
}
// if using an external etcdifcfg.Etcd.External!=nil {
// Check external etcd version before creating the clusterchecks=append(checks, ExternalEtcdVersionCheck{Etcd: cfg.Etcd})
}
}
ifcfg.Etcd.Local!=nil {
// Only do etcd related checks when required to install a local etcdchecks=append(checks,
PortOpenCheck{port: kubeadmconstants.EtcdListenClientPort},
PortOpenCheck{port: kubeadmconstants.EtcdListenPeerPort},
DirAvailableCheck{Path: cfg.Etcd.Local.DataDir},
)
}
ifcfg.Etcd.External!=nil&&!(isSecondaryControlPlane&&downloadCerts) {
// Only check etcd certificates when using an external etcd and not joining with automatic download of certsifcfg.Etcd.External.CAFile!="" {
checks=append(checks, FileExistingCheck{Path: cfg.Etcd.External.CAFile, Label: "ExternalEtcdClientCertificates"})
}
ifcfg.Etcd.External.CertFile!="" {
checks=append(checks, FileExistingCheck{Path: cfg.Etcd.External.CertFile, Label: "ExternalEtcdClientCertificates"})
}
ifcfg.Etcd.External.KeyFile!="" {
checks=append(checks, FileExistingCheck{Path: cfg.Etcd.External.KeyFile, Label: "ExternalEtcdClientCertificates"})
}
}
returnRunChecks(checks, os.Stderr, ignorePreflightErrors)
}
// RunJoinNodeChecks executes all individual, applicable to node checks.funcRunJoinNodeChecks(execer utilsexec.Interface, cfg*kubeadmapi.JoinConfiguration, ignorePreflightErrors sets.String) error {
// First, check if we're root separately from the other preflight checks and fail fastiferr:=RunRootCheckOnly(ignorePreflightErrors); err!=nil {
returnerr
}
checks:= []Checker{
DirAvailableCheck{Path: filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName)},
FileAvailableCheck{Path: filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletKubeConfigFileName)},
FileAvailableCheck{Path: filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletBootstrapKubeConfigFileName)},
}
checks=addCommonChecks(execer, "", &cfg.NodeRegistration, checks)
ifcfg.ControlPlane==nil {
checks=append(checks, FileAvailableCheck{Path: cfg.CACertPath})
}
addIPv6Checks:=falseifcfg.Discovery.BootstrapToken!=nil {
ipstr, _, err:=net.SplitHostPort(cfg.Discovery.BootstrapToken.APIServerEndpoint)
iferr==nil {
checks=append(checks,
HTTPProxyCheck{Proto: "https", Host: ipstr},
)
ifip:=net.ParseIP(ipstr); ip!=nil {
ifutilsnet.IsIPv6(ip) {
addIPv6Checks=true
}
}
}
}
ifaddIPv6Checks {
checks=append(checks,
FileContentCheck{Path: bridgenf6, Content: []byte{'1'}},
FileContentCheck{Path: ipv6DefaultForwarding, Content: []byte{'1'}},
)
}
returnRunChecks(checks, os.Stderr, ignorePreflightErrors)
}
// addCommonChecks is a helper function to duplicate checks that are common between both the// kubeadm init and join commandsfuncaddCommonChecks(execer utilsexec.Interface, k8sVersionstring, nodeReg*kubeadmapi.NodeRegistrationOptions, checks []Checker) []Checker {
containerRuntime, err:=utilruntime.NewContainerRuntime(execer, nodeReg.CRISocket)
isDocker:=falseiferr!=nil {
fmt.Printf("[preflight] WARNING: Couldn't create the interface used for talking to the container runtime: %v\n", err)
} else {
checks=append(checks, ContainerRuntimeCheck{runtime: containerRuntime})
ifcontainerRuntime.IsDocker() {
isDocker=truechecks=append(checks, ServiceCheck{Service: "docker", CheckIfActive: true})
// Linux only// TODO: support other CRIs for this check eventually// https://github.com/kubernetes/kubeadm/issues/874checks=append(checks, IsDockerSystemdCheck{})
}
}
// non-windows checksifruntime.GOOS=="linux" {
if!isDocker {
checks=append(checks, InPathCheck{executable: "crictl", mandatory: true, exec: execer})
}
checks=append(checks,
FileContentCheck{Path: bridgenf, Content: []byte{'1'}},
FileContentCheck{Path: ipv4Forward, Content: []byte{'1'}},
SwapCheck{},
InPathCheck{executable: "conntrack", mandatory: true, exec: execer},
InPathCheck{executable: "ip", mandatory: true, exec: execer},
InPathCheck{executable: "iptables", mandatory: true, exec: execer},
InPathCheck{executable: "mount", mandatory: true, exec: execer},
InPathCheck{executable: "nsenter", mandatory: true, exec: execer},
InPathCheck{executable: "ebtables", mandatory: false, exec: execer},
InPathCheck{executable: "ethtool", mandatory: false, exec: execer},
InPathCheck{executable: "socat", mandatory: false, exec: execer},
InPathCheck{executable: "tc", mandatory: false, exec: execer},
InPathCheck{executable: "touch", mandatory: false, exec: execer})
}
checks=append(checks,
SystemVerificationCheck{IsDocker: isDocker},
HostnameCheck{nodeName: nodeReg.Name},
KubeletVersionCheck{KubernetesVersion: k8sVersion, exec: execer},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
PortOpenCheck{port: kubeadmconstants.KubeletPort})
returnchecks
}
// RunRootCheckOnly initializes checks slice of structs and call RunChecksfuncRunRootCheckOnly(ignorePreflightErrors sets.String) error {
checks:= []Checker{
IsPrivilegedUserCheck{},
}
returnRunChecks(checks, os.Stderr, ignorePreflightErrors)
}
// RunPullImagesCheck will pull images kubeadm needs if they are not found on the systemfuncRunPullImagesCheck(execer utilsexec.Interface, cfg*kubeadmapi.InitConfiguration, ignorePreflightErrors sets.String) error {
containerRuntime, err:=utilruntime.NewContainerRuntime(utilsexec.New(), cfg.NodeRegistration.CRISocket)
iferr!=nil {
returnerr
}
checks:= []Checker{
ImagePullCheck{runtime: containerRuntime, imageList: images.GetControlPlaneImages(&cfg.ClusterConfiguration)},
}
returnRunChecks(checks, os.Stderr, ignorePreflightErrors)
}
// RunChecks runs each check, displays it's warnings/errors, and once all// are processed will exit if any errors occurred.funcRunChecks(checks []Checker, ww io.Writer, ignorePreflightErrors sets.String) error {
varerrsBuffer bytes.Bufferfor_, c:=rangechecks {
name:=c.Name()
warnings, errs:=c.Check()
ifsetHasItemOrAll(ignorePreflightErrors, name) {
// Decrease severity of errors to warnings for this checkwarnings=append(warnings, errs...)
errs= []error{}
}
for_, w:=rangewarnings {
io.WriteString(ww, fmt.Sprintf("\t[WARNING %s]: %v\n", name, w))
}
for_, i:=rangeerrs {
errsBuffer.WriteString(fmt.Sprintf("\t[ERROR %s]: %v\n", name, i.Error()))
}
}
iferrsBuffer.Len() >0 {
return&Error{Msg: errsBuffer.String()}
}
returnnil
}
// setHasItemOrAll is helper function that return true if item is present in the set (case insensitive) or special key 'all' is presentfuncsetHasItemOrAll(s sets.String, itemstring) bool {
ifs.Has("all") ||s.Has(strings.ToLower(item)) {
returntrue
}
returnfalse
}
ewfilemode100644ndex0000000000000..b6508b5ec446a++b/cmd/kubeadm/app/preflight/checks_darwin.go
bc3b052950a8161c4e5edb5ac1316550b1dc8a42
The text was updated successfully, but these errors were encountered:
support other CRIs for this check eventually
kubernetes/kubeadm#874
kubernetes/cmd/kubeadm/app/preflight/checks.go
Line 1019 in f444ffd
bc3b052950a8161c4e5edb5ac1316550b1dc8a42
The text was updated successfully, but these errors were encountered: