...

Source file src/k8s.io/kubernetes/test/e2e_node/environment/conformance.go

Documentation: k8s.io/kubernetes/test/e2e_node/environment

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Build the binary with `go build conformance.go`, then run the conformance binary on a node candidate.  If compiled
    18  // on a non-linux machine, must be cross compiled for the host.
    19  package main
    20  
    21  import (
    22  	"flag"
    23  	"fmt"
    24  	"net"
    25  	"os/exec"
    26  	"regexp"
    27  	"strings"
    28  
    29  	"errors"
    30  	"os"
    31  )
    32  
    33  const success = "\033[0;32mSUCCESS\033[0m"
    34  const failed = "\033[0;31mFAILED\033[0m"
    35  const notConfigured = "\033[0;34mNOT CONFIGURED\033[0m"
    36  const skipped = "\033[0;34mSKIPPED\033[0m"
    37  
    38  var checkFlag = flag.String(
    39  	"check", "all", "what to check for conformance.  One or more of all,container-runtime,daemons,dns,firewall,kernel")
    40  
    41  func init() {
    42  	// Set this to false to undo util/logs.go settings it to true.  Prevents cadvisor log spam.
    43  	// Remove this once util/logs.go stops setting the flag to true.
    44  	flag.Set("logtostderr", "false")
    45  }
    46  
    47  // TODO: Should we write an e2e test for this?
    48  func main() {
    49  	flag.Parse()
    50  	o := strings.Split(*checkFlag, ",")
    51  	errs := check(o...)
    52  	if len(errs) > 0 {
    53  		os.Exit(1)
    54  	} else {
    55  		os.Exit(0)
    56  	}
    57  }
    58  
    59  // check returns errors found while checking the provided components.  Will prevent errors to stdout.
    60  func check(options ...string) []error {
    61  	errs := []error{}
    62  	for _, c := range options {
    63  		switch c {
    64  		case "all":
    65  			errs = appendNotNil(errs, kernel())
    66  			errs = appendNotNil(errs, daemons())
    67  			errs = appendNotNil(errs, firewall())
    68  			errs = appendNotNil(errs, dns())
    69  		case "daemons":
    70  			errs = appendNotNil(errs, daemons())
    71  		case "dns":
    72  			errs = appendNotNil(errs, dns())
    73  		case "firewall":
    74  			errs = appendNotNil(errs, firewall())
    75  		case "kernel":
    76  			errs = appendNotNil(errs, kernel())
    77  		default:
    78  			fmt.Printf("Unrecognized option %s\n", c)
    79  			errs = append(errs, fmt.Errorf("Unrecognized option %s", c))
    80  		}
    81  	}
    82  	return errs
    83  }
    84  
    85  const kubeletClusterDNSRegexStr = `\/kubelet.*--cluster-dns=(\S+) `
    86  const kubeletClusterDomainRegexStr = `\/kubelet.*--cluster-domain=(\S+)`
    87  
    88  // dns checks that cluster dns has been properly configured and can resolve the kubernetes.default service
    89  func dns() error {
    90  	dnsRegex, err := regexp.Compile(kubeletClusterDNSRegexStr)
    91  	if err != nil {
    92  		// This should never happen and can only be fixed by changing the code
    93  		panic(err)
    94  	}
    95  	domainRegex, err := regexp.Compile(kubeletClusterDomainRegexStr)
    96  	if err != nil {
    97  		// This should never happen and can only be fixed by changing the code
    98  		panic(err)
    99  	}
   100  
   101  	h, err := net.LookupHost("kubernetes.default")
   102  	if err == nil {
   103  		return printSuccess("Dns Check (Optional): %s", success)
   104  	}
   105  	if len(h) > 0 {
   106  		return printSuccess("Dns Check (Optional): %s", success)
   107  	}
   108  
   109  	kubecmd, err := exec.Command("ps", "aux").CombinedOutput()
   110  	if err != nil {
   111  		// Executing ps aux shouldn't have failed
   112  		panic(err)
   113  	}
   114  
   115  	// look for the dns flag and parse the value
   116  	dns := dnsRegex.FindStringSubmatch(string(kubecmd))
   117  	if len(dns) < 2 {
   118  		return printSuccess(
   119  			"Dns Check (Optional): %s No hosts resolve to kubernetes.default.  kubelet will need to set "+
   120  				"--cluster-dns and --cluster-domain when run", notConfigured)
   121  	}
   122  
   123  	// look for the domain flag and parse the value
   124  	domain := domainRegex.FindStringSubmatch(string(kubecmd))
   125  	if len(domain) < 2 {
   126  		return printSuccess(
   127  			"Dns Check (Optional): %s No hosts resolve to kubernetes.default.  kubelet will need to set "+
   128  				"--cluster-dns and --cluster-domain when run", notConfigured)
   129  	}
   130  
   131  	// do a lookup with the flags the kubelet is running with
   132  	nsArgs := []string{"-q=a", fmt.Sprintf("kubernetes.default.%s", domain[1]), dns[1]}
   133  	if err = exec.Command("nslookup", nsArgs...).Run(); err != nil {
   134  		// Mark this as failed since there was a clear intention to set it up, but it is done so improperly
   135  		return printError(
   136  			"Dns Check (Optional): %s No hosts resolve to kubernetes.default  kubelet found, but cannot resolve "+
   137  				"kubernetes.default using nslookup %s error: %v", failed, strings.Join(nsArgs, " "), err)
   138  	}
   139  
   140  	// Can resolve kubernetes.default using the kubelete dns and domain values
   141  	return printSuccess("Dns Check (Optional): %s", success)
   142  }
   143  
   144  const cmdlineCGroupMemory = `cgroup_enable=memory`
   145  
   146  // kernel checks that the kernel has been configured correctly to support the required cgroup features
   147  func kernel() error {
   148  	cmdline, err := os.ReadFile("/proc/cmdline")
   149  	if err != nil {
   150  		return printError("Kernel Command Line Check %s: Could not check /proc/cmdline", failed)
   151  	}
   152  	if !strings.Contains(string(cmdline), cmdlineCGroupMemory) {
   153  		return printError("Kernel Command Line Check %s: cgroup_enable=memory not enabled in /proc/cmdline", failed)
   154  	}
   155  	return printSuccess("Kernel Command Line %s", success)
   156  }
   157  
   158  const iptablesInputRegexStr = `Chain INPUT \(policy DROP\)`
   159  const iptablesForwardRegexStr = `Chain FORWARD \(policy DROP\)`
   160  
   161  // firewall checks that iptables does not have common firewall rules setup that would disrupt traffic
   162  func firewall() error {
   163  	out, err := exec.Command("iptables", "-L", "INPUT").CombinedOutput()
   164  	if err != nil {
   165  		return printSuccess("Firewall IPTables Check %s: Could not run iptables", skipped)
   166  	}
   167  	inputRegex, err := regexp.Compile(iptablesInputRegexStr)
   168  	if err != nil {
   169  		// This should never happen and can only be fixed by changing the code
   170  		panic(err)
   171  	}
   172  	if inputRegex.Match(out) {
   173  		return printError("Firewall IPTables Check %s: Found INPUT rule matching %s", failed, iptablesInputRegexStr)
   174  	}
   175  
   176  	// Check GCE forward rules
   177  	out, err = exec.Command("iptables", "-L", "FORWARD").CombinedOutput()
   178  	if err != nil {
   179  		return printSuccess("Firewall IPTables Check %s: Could not run iptables", skipped)
   180  	}
   181  	forwardRegex, err := regexp.Compile(iptablesForwardRegexStr)
   182  	if err != nil {
   183  		// This should never happen and can only be fixed by changing the code
   184  		panic(err)
   185  	}
   186  	if forwardRegex.Match(out) {
   187  		return printError("Firewall IPTables Check %s: Found FORWARD rule matching %s", failed, iptablesInputRegexStr)
   188  	}
   189  
   190  	return printSuccess("Firewall IPTables Check %s", success)
   191  }
   192  
   193  // daemons checks that the required node programs are running: kubelet and kube-proxy
   194  func daemons() error {
   195  	if exec.Command("pgrep", "-f", "kubelet").Run() != nil {
   196  		return printError("Daemon Check %s: kubelet process not found", failed)
   197  	}
   198  
   199  	if exec.Command("pgrep", "-f", "kube-proxy").Run() != nil {
   200  		return printError("Daemon Check %s: kube-proxy process not found", failed)
   201  	}
   202  
   203  	return printSuccess("Daemon Check %s", success)
   204  }
   205  
   206  // printError provides its arguments to print a format string to the console (newline terminated) and returns an
   207  // error with the same string
   208  func printError(s string, args ...interface{}) error {
   209  	es := fmt.Sprintf(s, args...)
   210  	fmt.Println(es)
   211  	return errors.New(es)
   212  }
   213  
   214  // printSuccess provides its arguments to print a format string to the console (newline terminated) and returns nil
   215  func printSuccess(s string, args ...interface{}) error {
   216  	fmt.Println(fmt.Sprintf(s, args...))
   217  	return nil
   218  }
   219  
   220  // appendNotNil appends err to errs iff err is not nil
   221  func appendNotNil(errs []error, err error) []error {
   222  	if err != nil {
   223  		return append(errs, err)
   224  	}
   225  	return errs
   226  }
   227  

View as plain text