mirror of https://github.com/k3s-io/k3s
Initial revision of conformance node environment conformance tests
parent
6b358e43dd
commit
0e2ade336a
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Build the binary with `go build conformance.go`, then run the conformance binary on a node candidate. If compiled
|
||||
// on a non-linux machine, must be cross compiled for the host.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"errors"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
|
||||
"os"
|
||||
)
|
||||
|
||||
const success = "\033[0;32mSUCESS\033[0m"
|
||||
const failed = "\033[0;31mFAILED\033[0m"
|
||||
const notConfigured = "\033[0;34mNOT CONFIGURED\033[0m"
|
||||
const skipped = "\033[0;34mSKIPPED\033[0m"
|
||||
|
||||
var checkFlag = flag.String(
|
||||
"check", "all", "what to check for conformance. One or more of all,container-runtime,daemons,dns,firewall,kernel")
|
||||
|
||||
func init() {
|
||||
// Set this to false to undo util/logs.go settings it to true. Prevents cadvisor log spam.
|
||||
// Remove this once util/logs.go stops setting the flag to true.
|
||||
flag.Set("logtostderr", "false")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// TODO: Should we write an e2e test for this?
|
||||
func main() {
|
||||
o := strings.Split(*checkFlag, ",")
|
||||
errs := check(o...)
|
||||
if len(errs) > 0 {
|
||||
os.Exit(1)
|
||||
} else {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// check returns errors found while checking the provided components. Will prevent errors to stdout.
|
||||
func check(options ...string) []error {
|
||||
errs := []error{}
|
||||
for _, c := range options {
|
||||
switch c {
|
||||
case "all":
|
||||
errs = appendNotNil(errs, kernel())
|
||||
errs = appendNotNil(errs, containerRuntime())
|
||||
errs = appendNotNil(errs, daemons())
|
||||
errs = appendNotNil(errs, firewall())
|
||||
errs = appendNotNil(errs, dns())
|
||||
case "containerruntime":
|
||||
errs = appendNotNil(errs, containerRuntime())
|
||||
case "daemons":
|
||||
errs = appendNotNil(errs, daemons())
|
||||
case "dns":
|
||||
errs = appendNotNil(errs, dns())
|
||||
case "firewall":
|
||||
errs = appendNotNil(errs, firewall())
|
||||
case "kernel":
|
||||
errs = appendNotNil(errs, kernel())
|
||||
default:
|
||||
fmt.Printf("Unrecognized option %s", c)
|
||||
errs = append(errs, errors.New(fmt.Sprintf("Unrecognized option %s", c)))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
const dockerVersionRegex = `1\.[7-9]\.[0-9]+`
|
||||
|
||||
// containerRuntime checks that a suitable container runtime is installed and recognized by cadvisor: docker 1.7-1.9
|
||||
func containerRuntime() error {
|
||||
dockerRegex, err := regexp.Compile(dockerVersionRegex)
|
||||
if err != nil {
|
||||
// This should never happen and can only be fixed by changing the code
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Setup cadvisor to check the container environment
|
||||
c, err := cadvisor.New(0 /*don't start the http server*/)
|
||||
if err != nil {
|
||||
return printError("Container Runtime Check: %s Could not start cadvisor %v", failed, err)
|
||||
}
|
||||
|
||||
vi, err := c.VersionInfo()
|
||||
if err != nil {
|
||||
return printError("Container Runtime Check: %s Could not get VersionInfo %v", failed, err)
|
||||
}
|
||||
|
||||
d := vi.DockerVersion
|
||||
if !dockerRegex.Match([]byte(d)) {
|
||||
return printError(
|
||||
"Container Runtime Check: %s Docker version %s does not matching %s. You may need to run as root or the "+
|
||||
"user the kubelet will run under.", failed, d, dockerVersionRegex)
|
||||
}
|
||||
|
||||
return printSuccess("Container Runtime Check: %s", success)
|
||||
}
|
||||
|
||||
const kubeletClusterDnsRegexStr = `\/kubelet.*--cluster-dns=(\S+) `
|
||||
const kubeletClusterDomainRegexStr = `\/kubelet.*--cluster-domain=(\S+)`
|
||||
|
||||
// dns checks that cluster dns has been properly configured and can resolve the kubernetes.default service
|
||||
func dns() error {
|
||||
dnsRegex, err := regexp.Compile(kubeletClusterDnsRegexStr)
|
||||
if err != nil {
|
||||
// This should never happen and can only be fixed by changing the code
|
||||
panic(err)
|
||||
}
|
||||
domainRegex, err := regexp.Compile(kubeletClusterDomainRegexStr)
|
||||
if err != nil {
|
||||
// This should never happen and can only be fixed by changing the code
|
||||
panic(err)
|
||||
}
|
||||
|
||||
h, err := net.LookupHost("kubernetes.default")
|
||||
if err == nil {
|
||||
return printSuccess("Dns Check (Optional): %s", success)
|
||||
}
|
||||
if len(h) > 0 {
|
||||
return printSuccess("Dns Check (Optional): %s", success)
|
||||
}
|
||||
|
||||
kubecmd, err := exec.Command("ps", "aux").CombinedOutput()
|
||||
|
||||
// look for the dns flag and parse the value
|
||||
dns := dnsRegex.FindStringSubmatch(string(kubecmd))
|
||||
if len(dns) < 2 {
|
||||
return printSuccess(
|
||||
"Dns Check (Optional): %s No hosts resolve to kubernetes.default. kubelet will need to set "+
|
||||
"--cluster-dns and --cluster-domain when run", notConfigured)
|
||||
}
|
||||
|
||||
// look for the domain flag and parse the value
|
||||
domain := domainRegex.FindStringSubmatch(string(kubecmd))
|
||||
if len(domain) < 2 {
|
||||
return printSuccess(
|
||||
"Dns Check (Optional): %s No hosts resolve to kubernetes.default. kubelet will need to set "+
|
||||
"--cluster-dns and --cluster-domain when run", notConfigured)
|
||||
}
|
||||
|
||||
// do a lookup with the flags the kubelet is running with
|
||||
nsArgs := []string{"-q=a", fmt.Sprintf("kubernetes.default.%s", domain[1]), dns[1]}
|
||||
if err = exec.Command("nslookup", nsArgs...).Run(); err != nil {
|
||||
// Mark this as failed since there was a clear intention to set it up, but it is done so improperly
|
||||
return printError(
|
||||
"Dns Check (Optional): %s No hosts resolve to kubernetes.default kubelet found, but cannot resolve "+
|
||||
"kubernetes.default using nslookup %s error: %v", failed, strings.Join(nsArgs, " "), err)
|
||||
}
|
||||
|
||||
// Can resolve kubernetes.default using the kubelete dns and domain values
|
||||
return printSuccess("Dns Check (Optional): %s", success)
|
||||
}
|
||||
|
||||
const cmdlineCGroupMemory = `cgroup_enable=memory`
|
||||
|
||||
// kernel checks that the kernel has been configured correctly to support the required cgroup features
|
||||
func kernel() error {
|
||||
cmdline, err := ioutil.ReadFile("/proc/cmdline")
|
||||
if err != nil {
|
||||
return printError("Kernel Command Line Check %s: Could not check /proc/cmdline", failed)
|
||||
}
|
||||
if !strings.Contains(string(cmdline), cmdlineCGroupMemory) {
|
||||
return printError("Kernel Command Line Check %s: cgroup_enable=memory not enabled in /proc/cmdline", failed)
|
||||
}
|
||||
return printSuccess("Kernel Command Line %s", success)
|
||||
}
|
||||
|
||||
const iptablesInputRegexStr = `Chain INPUT \(policy DROP\)`
|
||||
const iptablesForwardRegexStr = `Chain FORWARD \(policy DROP\)`
|
||||
|
||||
// firewall checks that iptables does not have common firewall rules setup that would disrupt traffic
|
||||
func firewall() error {
|
||||
out, err := exec.Command("iptables", "-L", "INPUT").CombinedOutput()
|
||||
if err != nil {
|
||||
return printSuccess("Firewall Iptables Check %s: Could not run iptables", skipped)
|
||||
}
|
||||
inputRegex, err := regexp.Compile(iptablesInputRegexStr)
|
||||
if err != nil {
|
||||
// This should never happen and can only be fixed by changing the code
|
||||
panic(err)
|
||||
}
|
||||
if inputRegex.Match(out) {
|
||||
return printError("Firewall Iptables Check %s: Found INPUT rule matching %s", failed, iptablesInputRegexStr)
|
||||
}
|
||||
|
||||
// Check GCE forward rules
|
||||
out, err = exec.Command("iptables", "-L", "FORWARD").CombinedOutput()
|
||||
if err != nil {
|
||||
return printSuccess("Firewall Iptables Check %s: Could not run iptables", skipped)
|
||||
}
|
||||
forwardRegex, err := regexp.Compile(iptablesForwardRegexStr)
|
||||
if err != nil {
|
||||
// This should never happen and can only be fixed by changing the code
|
||||
panic(err)
|
||||
}
|
||||
if forwardRegex.Match(out) {
|
||||
return printError("Firewall Iptables Check %s: Found FORWARD rule matching %s", failed, iptablesInputRegexStr)
|
||||
}
|
||||
|
||||
return printSuccess("Firewall Iptables Check %s", success)
|
||||
}
|
||||
|
||||
// daemons checks that the required node programs are running: kubelet, kube-proxy, and docker
|
||||
func daemons() error {
|
||||
if exec.Command("pgrep", "-f", "kubelet").Run() != nil {
|
||||
return printError("Daemon Check %s: kubelet process not found", failed)
|
||||
}
|
||||
|
||||
if exec.Command("pgrep", "-f", "kube-proxy").Run() != nil {
|
||||
return printError("Daemon Check %s: kube-proxy process not found", failed)
|
||||
}
|
||||
|
||||
return printSuccess("Daemon Check %s", success)
|
||||
}
|
||||
|
||||
// printError provides its arguments to print a format string to the console (newline terminated) and returns an
|
||||
// error with the same string
|
||||
func printError(s string, args ...interface{}) error {
|
||||
es := fmt.Sprintf(s, args...)
|
||||
fmt.Println(es)
|
||||
return errors.New(es)
|
||||
}
|
||||
|
||||
// printSuccess provides its arguments to print a format string to the console (newline terminated) and returns nil
|
||||
func printSuccess(s string, args ...interface{}) error {
|
||||
fmt.Println(fmt.Sprintf(s, args...))
|
||||
return nil
|
||||
}
|
||||
|
||||
// appendNotNil appends err to errs iff err is not nil
|
||||
func appendNotNil(errs []error, err error) []error {
|
||||
if err != nil {
|
||||
return append(errs, err)
|
||||
}
|
||||
return errs
|
||||
}
|
Loading…
Reference in New Issue