mirror of https://github.com/k3s-io/k3s
Merge pull request #36334 from luxas/add_preflight
Automatic merge from submit-queue Add the system verification test to the kubeadm preflight checks And refactor the system verification test to accept to write to a specific writer in order to customize the output This PR is targeting v1.5, PTAL cc @Random-Liu @dchen1107 @kubernetes/sig-cluster-lifecyclepull/6/head
commit
2fab199390
|
@ -19,6 +19,7 @@ go_library(
|
|||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/util/initsystem:go_default_library",
|
||||
"//pkg/util/node:go_default_library",
|
||||
"//test/e2e_node/system:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package preflight
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -29,6 +30,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/util/initsystem"
|
||||
"k8s.io/kubernetes/pkg/util/node"
|
||||
"k8s.io/kubernetes/test/e2e_node/system"
|
||||
)
|
||||
|
||||
type PreFlightError struct {
|
||||
|
@ -213,9 +215,26 @@ func (hst HttpProxyCheck) Check() (warnings, errors []error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
type SystemVerificationCheck struct{}
|
||||
|
||||
func (sysver SystemVerificationCheck) Check() (warnings, errors []error) {
|
||||
// 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
|
||||
bufw := bufio.NewWriterSize(os.Stdout, 1*1024*1024)
|
||||
|
||||
// Run the system verification check, but write to out buffered writer instead of stdout
|
||||
err := system.Validate(system.DefaultSysSpec, &system.StreamReporter{WriteStream: bufw})
|
||||
if err != nil {
|
||||
// Only print the output from the system verification check if the check failed
|
||||
fmt.Println("System verification failed. Printing the output from the verification...")
|
||||
bufw.Flush()
|
||||
return nil, []error{err}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error {
|
||||
// TODO: Some of these ports should come from kubeadm config eventually:
|
||||
checks := []PreFlightCheck{
|
||||
SystemVerificationCheck{},
|
||||
IsRootCheck{root: true},
|
||||
HostnameCheck{},
|
||||
ServiceCheck{Service: "kubelet"},
|
||||
|
@ -249,8 +268,8 @@ func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error {
|
|||
}
|
||||
|
||||
func RunJoinNodeChecks(cfg *kubeadmapi.NodeConfiguration) error {
|
||||
// TODO: Some of these ports should come from kubeadm config eventually:
|
||||
checks := []PreFlightCheck{
|
||||
SystemVerificationCheck{},
|
||||
IsRootCheck{root: true},
|
||||
HostnameCheck{},
|
||||
ServiceCheck{Service: "docker"},
|
||||
|
|
|
@ -92,7 +92,7 @@ func TestE2eNode(t *testing.T) {
|
|||
glog.Exitf("chroot %q failed: %v", rootfs, err)
|
||||
}
|
||||
}
|
||||
if err := system.Validate(); err != nil {
|
||||
if err := system.ValidateDefault(); err != nil {
|
||||
glog.Exitf("system validation failed: %v", err)
|
||||
}
|
||||
return
|
||||
|
|
|
@ -17,8 +17,8 @@ go_library(
|
|||
"docker_validator.go",
|
||||
"kernel_validator.go",
|
||||
"os_validator.go",
|
||||
"report.go",
|
||||
"types.go",
|
||||
"util.go",
|
||||
"validators.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
|
|
|
@ -25,7 +25,9 @@ import (
|
|||
|
||||
var _ Validator = &CgroupsValidator{}
|
||||
|
||||
type CgroupsValidator struct{}
|
||||
type CgroupsValidator struct {
|
||||
Reporter Reporter
|
||||
}
|
||||
|
||||
func (c *CgroupsValidator) Name() string {
|
||||
return "cgroups"
|
||||
|
@ -55,9 +57,9 @@ func (c *CgroupsValidator) validateCgroupSubsystems(cgroupSpec, subsystems []str
|
|||
}
|
||||
item := cgroupsConfigPrefix + strings.ToUpper(cgroup)
|
||||
if found {
|
||||
report(item, "enabled", good)
|
||||
c.Reporter.Report(item, "enabled", good)
|
||||
} else {
|
||||
report(item, "missing", bad)
|
||||
c.Reporter.Report(item, "missing", bad)
|
||||
missing = append(missing, cgroup)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,9 @@ import (
|
|||
)
|
||||
|
||||
func TestValidateCgroupSubsystem(t *testing.T) {
|
||||
v := &CgroupsValidator{}
|
||||
v := &CgroupsValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
cgroupSpec := []string{"system1", "system2"}
|
||||
for desc, test := range map[string]struct {
|
||||
cgroupSpec []string
|
||||
|
|
|
@ -28,7 +28,9 @@ import (
|
|||
var _ Validator = &DockerValidator{}
|
||||
|
||||
// DockerValidator validates docker configuration.
|
||||
type DockerValidator struct{}
|
||||
type DockerValidator struct {
|
||||
Reporter Reporter
|
||||
}
|
||||
|
||||
func (d *DockerValidator) Name() string {
|
||||
return "docker"
|
||||
|
@ -63,22 +65,22 @@ func (d *DockerValidator) validateDockerInfo(spec *DockerSpec, info types.Info)
|
|||
for _, v := range spec.Version {
|
||||
r := regexp.MustCompile(v)
|
||||
if r.MatchString(info.ServerVersion) {
|
||||
report(dockerConfigPrefix+"VERSION", info.ServerVersion, good)
|
||||
d.Reporter.Report(dockerConfigPrefix+"VERSION", info.ServerVersion, good)
|
||||
matched = true
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
report(dockerConfigPrefix+"VERSION", info.ServerVersion, bad)
|
||||
d.Reporter.Report(dockerConfigPrefix+"VERSION", info.ServerVersion, bad)
|
||||
return fmt.Errorf("unsupported docker version: %s", info.ServerVersion)
|
||||
}
|
||||
// Validate graph driver.
|
||||
item := dockerConfigPrefix + "GRAPH_DRIVER"
|
||||
for _, d := range spec.GraphDriver {
|
||||
if info.Driver == d {
|
||||
report(item, info.Driver, good)
|
||||
for _, gd := range spec.GraphDriver {
|
||||
if info.Driver == gd {
|
||||
d.Reporter.Report(item, info.Driver, good)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
report(item, info.Driver, bad)
|
||||
d.Reporter.Report(item, info.Driver, bad)
|
||||
return fmt.Errorf("unsupported graph driver: %s", info.Driver)
|
||||
}
|
||||
|
|
|
@ -24,7 +24,9 @@ import (
|
|||
)
|
||||
|
||||
func TestValidateDockerInfo(t *testing.T) {
|
||||
v := &DockerValidator{}
|
||||
v := &DockerValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
spec := &DockerSpec{
|
||||
Version: []string{`1\.(9|\d{2,})\..*`},
|
||||
GraphDriver: []string{"driver_1", "driver_2"},
|
||||
|
|
|
@ -39,6 +39,7 @@ var _ Validator = &KernelValidator{}
|
|||
// and kernel configuration.
|
||||
type KernelValidator struct {
|
||||
kernelRelease string
|
||||
Reporter Reporter
|
||||
}
|
||||
|
||||
func (k *KernelValidator) Name() string {
|
||||
|
@ -60,11 +61,11 @@ const (
|
|||
)
|
||||
|
||||
func (k *KernelValidator) Validate(spec SysSpec) error {
|
||||
out, err := exec.Command("uname", "-r").CombinedOutput()
|
||||
release, err := exec.Command("uname", "-r").CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get kernel release: %v", err)
|
||||
}
|
||||
k.kernelRelease = strings.TrimSpace(string(out))
|
||||
k.kernelRelease = strings.TrimSpace(string(release))
|
||||
var errs []error
|
||||
errs = append(errs, k.validateKernelVersion(spec.KernelSpec))
|
||||
errs = append(errs, k.validateKernelConfig(spec.KernelSpec))
|
||||
|
@ -78,11 +79,11 @@ func (k *KernelValidator) validateKernelVersion(kSpec KernelSpec) error {
|
|||
for _, versionRegexp := range versionRegexps {
|
||||
r := regexp.MustCompile(versionRegexp)
|
||||
if r.MatchString(k.kernelRelease) {
|
||||
report("KERNEL_VERSION", k.kernelRelease, good)
|
||||
k.Reporter.Report("KERNEL_VERSION", k.kernelRelease, good)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
report("KERNEL_VERSION", k.kernelRelease, bad)
|
||||
k.Reporter.Report("KERNEL_VERSION", k.kernelRelease, bad)
|
||||
return fmt.Errorf("unsupported kernel release: %s", k.kernelRelease)
|
||||
}
|
||||
|
||||
|
@ -101,7 +102,7 @@ func (k *KernelValidator) validateCachedKernelConfig(allConfig map[string]kConfi
|
|||
badConfigs := []string{}
|
||||
// reportAndRecord is a helper function to record bad config when
|
||||
// report.
|
||||
reportAndRecord := func(name, msg, desc string, result resultType) {
|
||||
reportAndRecord := func(name, msg, desc string, result ValidationResultType) {
|
||||
if result == bad {
|
||||
badConfigs = append(badConfigs, name)
|
||||
}
|
||||
|
@ -109,7 +110,7 @@ func (k *KernelValidator) validateCachedKernelConfig(allConfig map[string]kConfi
|
|||
if result != good && desc != "" {
|
||||
msg = msg + " - " + desc
|
||||
}
|
||||
report(name, msg, result)
|
||||
k.Reporter.Report(name, msg, result)
|
||||
}
|
||||
const (
|
||||
required = iota
|
||||
|
@ -117,7 +118,7 @@ func (k *KernelValidator) validateCachedKernelConfig(allConfig map[string]kConfi
|
|||
forbidden
|
||||
)
|
||||
validateOpt := func(config KernelConfig, expect int) {
|
||||
var found, missing resultType
|
||||
var found, missing ValidationResultType
|
||||
switch expect {
|
||||
case required:
|
||||
found, missing = good, bad
|
||||
|
|
|
@ -24,7 +24,9 @@ import (
|
|||
)
|
||||
|
||||
func TestValidateKernelVersion(t *testing.T) {
|
||||
v := &KernelValidator{}
|
||||
v := &KernelValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
// Currently, testRegex is align with DefaultSysSpec.KernelVersion, but in the future
|
||||
// they may be different.
|
||||
// This is fine, because the test mainly tests the kernel version validation logic,
|
||||
|
@ -69,7 +71,9 @@ func TestValidateKernelVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestValidateCachedKernelConfig(t *testing.T) {
|
||||
v := &KernelValidator{}
|
||||
v := &KernelValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
testKernelSpec := KernelSpec{
|
||||
Required: []KernelConfig{{Name: "REQUIRED_1"}, {Name: "REQUIRED_2", Aliases: []string{"ALIASE_REQUIRED_2"}}},
|
||||
Optional: []KernelConfig{{Name: "OPTIONAL_1"}, {Name: "OPTIONAL_2"}},
|
||||
|
@ -184,7 +188,9 @@ CONFIG_3=n`
|
|||
"CONFIG_2": asModule,
|
||||
"CONFIG_3": leftOut,
|
||||
}
|
||||
v := &KernelValidator{}
|
||||
v := &KernelValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
got, err := v.parseKernelConfig(bytes.NewReader([]byte(config)))
|
||||
assert.Nil(t, err, "Expect error not to occur when parse kernel configuration %q", config)
|
||||
assert.Equal(t, expected, got)
|
||||
|
|
|
@ -24,25 +24,27 @@ import (
|
|||
|
||||
var _ Validator = &OSValidator{}
|
||||
|
||||
type OSValidator struct{}
|
||||
type OSValidator struct {
|
||||
Reporter Reporter
|
||||
}
|
||||
|
||||
func (c *OSValidator) Name() string {
|
||||
func (o *OSValidator) Name() string {
|
||||
return "os"
|
||||
}
|
||||
|
||||
func (c *OSValidator) Validate(spec SysSpec) error {
|
||||
out, err := exec.Command("uname").CombinedOutput()
|
||||
func (o *OSValidator) Validate(spec SysSpec) error {
|
||||
os, err := exec.Command("uname").CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get os name: %v", err)
|
||||
}
|
||||
return c.validateOS(strings.TrimSpace(string(out)), spec.OS)
|
||||
return o.validateOS(strings.TrimSpace(string(os)), spec.OS)
|
||||
}
|
||||
|
||||
func (c *OSValidator) validateOS(os, specOS string) error {
|
||||
func (o *OSValidator) validateOS(os, specOS string) error {
|
||||
if os != specOS {
|
||||
report("OS", os, bad)
|
||||
o.Reporter.Report("OS", os, bad)
|
||||
return fmt.Errorf("unsupported operating system: %s", os)
|
||||
}
|
||||
report("OS", os, good)
|
||||
o.Reporter.Report("OS", os, good)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -23,7 +23,9 @@ import (
|
|||
)
|
||||
|
||||
func TestValidateOS(t *testing.T) {
|
||||
v := &OSValidator{}
|
||||
v := &OSValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
specOS := "Linux"
|
||||
for _, test := range []struct {
|
||||
os string
|
||||
|
|
|
@ -18,20 +18,22 @@ package system
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// resultType is type of the validation result. Different validation results
|
||||
// ValidationResultType is type of the validation result. Different validation results
|
||||
// corresponds to different colors.
|
||||
type resultType int
|
||||
type ValidationResultType int32
|
||||
|
||||
const (
|
||||
good resultType = iota
|
||||
good ValidationResultType = iota
|
||||
bad
|
||||
warn
|
||||
)
|
||||
|
||||
// color is the color of the message.
|
||||
type color int
|
||||
type color int32
|
||||
|
||||
const (
|
||||
red color = 31
|
||||
|
@ -40,15 +42,19 @@ const (
|
|||
white = 37
|
||||
)
|
||||
|
||||
func wrap(s string, c color) string {
|
||||
func colorize(s string, c color) string {
|
||||
return fmt.Sprintf("\033[0;%dm%s\033[0m", c, s)
|
||||
}
|
||||
|
||||
// report reports "item: r". item is white, and the color of r depends on the
|
||||
// result type.
|
||||
func report(item, r string, t resultType) {
|
||||
// The default reporter for the system verification test
|
||||
type StreamReporter struct {
|
||||
// The stream that this reporter is writing to
|
||||
WriteStream io.Writer
|
||||
}
|
||||
|
||||
func (dr *StreamReporter) Report(key, value string, resultType ValidationResultType) error {
|
||||
var c color
|
||||
switch t {
|
||||
switch resultType {
|
||||
case good:
|
||||
c = green
|
||||
case bad:
|
||||
|
@ -58,5 +64,15 @@ func report(item, r string, t resultType) {
|
|||
default:
|
||||
c = white
|
||||
}
|
||||
fmt.Printf("%s: %s\n", wrap(item, white), wrap(r, c))
|
||||
if dr.WriteStream == nil {
|
||||
return fmt.Errorf("WriteStream has to be defined for this reporter")
|
||||
}
|
||||
|
||||
fmt.Fprintf(dr.WriteStream, "%s: %s\n", colorize(key, white), colorize(value, c))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultReporter is the default Reporter
|
||||
var DefaultReporter = &StreamReporter{
|
||||
WriteStream: os.Stdout,
|
||||
}
|
|
@ -29,21 +29,31 @@ type Validator interface {
|
|||
Validate(SysSpec) error
|
||||
}
|
||||
|
||||
// validators are all the validators.
|
||||
var validators = []Validator{
|
||||
&OSValidator{},
|
||||
&KernelValidator{},
|
||||
&CgroupsValidator{},
|
||||
&DockerValidator{},
|
||||
// Reporter is the interface for the reporters for the validators.
|
||||
type Reporter interface {
|
||||
// Report reports the results of the system verification
|
||||
Report(string, string, ValidationResultType) error
|
||||
}
|
||||
|
||||
// Validate uses all validators to validate the system.
|
||||
func Validate() error {
|
||||
func Validate(spec SysSpec, report Reporter) error {
|
||||
var errs []error
|
||||
spec := DefaultSysSpec
|
||||
// validators are all the validators.
|
||||
var validators = []Validator{
|
||||
&OSValidator{Reporter: report},
|
||||
&KernelValidator{Reporter: report},
|
||||
&CgroupsValidator{Reporter: report},
|
||||
&DockerValidator{Reporter: report},
|
||||
}
|
||||
|
||||
for _, v := range validators {
|
||||
glog.Infof("Validating %s...", v.Name())
|
||||
errs = append(errs, v.Validate(spec))
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// ValidateDefault uses all default validators to validate the system and writes to stdout.
|
||||
func ValidateDefault() error {
|
||||
return Validate(DefaultSysSpec, DefaultReporter)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue