github.com/zhyoulun/cilium@v1.6.12/test/test_suite_test.go (about)

     1  // Copyright 2017-2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ciliumTest
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"os/exec"
    21  	"path/filepath"
    22  	"strconv"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/cilium/cilium/pkg/logging"
    27  	"github.com/cilium/cilium/test/config"
    28  	. "github.com/cilium/cilium/test/ginkgo-ext"
    29  	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
    30  	"github.com/cilium/cilium/test/helpers"
    31  	gops "github.com/google/gops/agent"
    32  	"github.com/onsi/ginkgo"
    33  	ginkgoconfig "github.com/onsi/ginkgo/config"
    34  	. "github.com/onsi/gomega"
    35  	"github.com/onsi/gomega/format"
    36  	"github.com/sirupsen/logrus"
    37  )
    38  
    39  var (
    40  	log             = logging.DefaultLogger
    41  	DefaultSettings = map[string]string{
    42  		"K8S_VERSION": "1.16",
    43  	}
    44  	k8sNodesEnv         = "K8S_NODES"
    45  	commandsLogFileName = "cmds.log"
    46  )
    47  
    48  func init() {
    49  
    50  	// Open socket for using gops to get stacktraces in case the tests deadlock.
    51  	if err := gops.Listen(gops.Options{ShutdownCleanup: true}); err != nil {
    52  		errorString := fmt.Sprintf("unable to start gops: %s", err)
    53  		fmt.Println(errorString)
    54  		os.Exit(-1)
    55  	}
    56  
    57  	for k, v := range DefaultSettings {
    58  		getOrSetEnvVar(k, v)
    59  	}
    60  	config.CiliumTestConfig.ParseFlags()
    61  
    62  	os.RemoveAll(helpers.TestResultsPath)
    63  
    64  	format.UseStringerRepresentation = true
    65  }
    66  
    67  func configLogsOutput() {
    68  	log.SetLevel(logrus.DebugLevel)
    69  	log.Out = &config.TestLogWriter
    70  	logrus.SetFormatter(&config.Formatter)
    71  	log.Formatter = &config.Formatter
    72  	log.Hooks.Add(&config.LogHook{})
    73  
    74  	ginkgoext.GinkgoWriter = NewWriter(log.Out)
    75  }
    76  
    77  func ShowCommands() {
    78  	if !config.CiliumTestConfig.ShowCommands {
    79  		return
    80  	}
    81  
    82  	helpers.SSHMetaLogs = ginkgoext.NewWriter(os.Stdout)
    83  }
    84  
    85  func TestTest(t *testing.T) {
    86  	if config.CiliumTestConfig.TestScope != "" {
    87  		helpers.UserDefinedScope = config.CiliumTestConfig.TestScope
    88  		fmt.Printf("User specified the scope:  %q\n", config.CiliumTestConfig.TestScope)
    89  	}
    90  
    91  	configLogsOutput()
    92  	ShowCommands()
    93  
    94  	if config.CiliumTestConfig.HoldEnvironment {
    95  		RegisterFailHandler(helpers.Fail)
    96  	} else {
    97  		RegisterFailHandler(Fail)
    98  	}
    99  	junitReporter := ginkgoext.NewJUnitReporter(fmt.Sprintf(
   100  		"%s.xml", helpers.GetScopeWithVersion()))
   101  	RunSpecsWithDefaultAndCustomReporters(
   102  		t, fmt.Sprintf("Suite-%s", helpers.GetScopeWithVersion()),
   103  		[]ginkgo.Reporter{junitReporter})
   104  }
   105  
   106  func goReportVagrantStatus() chan bool {
   107  	if ginkgoconfig.DefaultReporterConfig.Verbose ||
   108  		ginkgoconfig.DefaultReporterConfig.Succinct {
   109  		// Dev told us they want more/less information than default. Skip.
   110  		return nil
   111  	}
   112  
   113  	exit := make(chan bool)
   114  	go func() {
   115  		done := false
   116  		iter := 0
   117  		for {
   118  			var out string
   119  			select {
   120  			case ok := <-exit:
   121  				if ok {
   122  					out = "●\n"
   123  				} else {
   124  					out = "◌\n"
   125  				}
   126  				done = true
   127  			default:
   128  				out = string(rune(int('◜') + iter%4))
   129  			}
   130  			fmt.Printf("\rSpinning up vagrant VMs... %s", out)
   131  			if done {
   132  				return
   133  			}
   134  			time.Sleep(250 * time.Millisecond)
   135  			iter++
   136  		}
   137  	}()
   138  	return exit
   139  }
   140  
   141  func reportCreateVMFailure(vm string, err error) {
   142  	failmsg := fmt.Sprintf(`
   143          ===================== ERROR - VM PROVISION FAILED =====================
   144  
   145          Unable to provision and start VM %q: %s", vm, err
   146  
   147          =======================================================================
   148          `, vm, err)
   149  	ginkgoext.GinkgoPrint(failmsg)
   150  	Fail(failmsg)
   151  }
   152  
   153  var _ = BeforeAll(func() {
   154  	go func() {
   155  		defer GinkgoRecover()
   156  		time.Sleep(config.CiliumTestConfig.Timeout)
   157  		msg := fmt.Sprintf("Test suite timed out after %s", config.CiliumTestConfig.Timeout)
   158  		By(msg)
   159  		Fail(msg)
   160  	}()
   161  
   162  	var err error
   163  
   164  	logger := log.WithFields(logrus.Fields{"testName": "BeforeAll"})
   165  	scope, err := helpers.GetScope()
   166  	if err != nil {
   167  		Fail(fmt.Sprintf(
   168  			"Cannot get the scope for running test, please use --cilium.testScope option: %s",
   169  			err))
   170  	}
   171  
   172  	switch helpers.GetCurrentIntegration() {
   173  	case helpers.CIIntegrationFlannel:
   174  		switch helpers.GetCurrentK8SEnv() {
   175  		case "1.8":
   176  			log.Infof("Cilium in %q mode is not supported in Kubernets 1.8 due CNI < 0.6.0", helpers.CIIntegrationFlannel)
   177  			os.Exit(0)
   178  			return
   179  		}
   180  	}
   181  
   182  	if config.CiliumTestConfig.SSHConfig != "" {
   183  		// If we set a different VM that it's not in our test environment
   184  		// ginkgo cannot provision it, so skip setup below.
   185  		return
   186  	}
   187  
   188  	if progressChan := goReportVagrantStatus(); progressChan != nil {
   189  		defer func() { progressChan <- err == nil }()
   190  	}
   191  
   192  	switch scope {
   193  	case helpers.Runtime:
   194  		var err error
   195  
   196  		// Boot / provision VMs if specified by configuration.
   197  		if config.CiliumTestConfig.Reprovision {
   198  			err = helpers.CreateVM(helpers.Runtime)
   199  			if err != nil {
   200  				log.WithError(err).Error("Error starting VM")
   201  				reportCreateVMFailure(helpers.Runtime, err)
   202  			}
   203  		}
   204  
   205  		vm := helpers.InitRuntimeHelper(helpers.Runtime, logger)
   206  		err = vm.SetUpCilium()
   207  
   208  		if err != nil {
   209  			// AfterFailed function is not defined in this scope, fired the
   210  			// ReportFailed manually for this assert to gather cilium logs Fix
   211  			// #3428
   212  			vm.ReportFailed()
   213  			log.WithError(err).Error("Cilium was unable to be set up correctly")
   214  			reportCreateVMFailure(helpers.Runtime, err)
   215  		}
   216  		go vm.PprofReport()
   217  
   218  	case helpers.K8s:
   219  		//FIXME: This should be:
   220  		// Start k8s1 and provision kubernetes.
   221  		// When finish, start to build cilium in background
   222  		// Start k8s2
   223  		// Wait until compilation finished, and pull cilium image on k8s2
   224  
   225  		// Name for K8s VMs depends on K8s version that is running.
   226  
   227  		// Boot / provision VMs if specified by configuration.
   228  		if config.CiliumTestConfig.Reprovision {
   229  			err = helpers.CreateVM(helpers.K8s1VMName())
   230  			if err != nil {
   231  				reportCreateVMFailure(helpers.K8s1VMName(), err)
   232  			}
   233  
   234  			err = helpers.CreateVM(helpers.K8s2VMName())
   235  			if err != nil {
   236  				reportCreateVMFailure(helpers.K8s2VMName(), err)
   237  			}
   238  
   239  			// For Nightly test we need to have more than two kubernetes nodes. If
   240  			// the env variable K8S_NODES is present, more nodes will be created.
   241  			if nodes := os.Getenv(k8sNodesEnv); nodes != "" {
   242  				nodesInt, err := strconv.Atoi(nodes)
   243  				if err != nil {
   244  					Fail(fmt.Sprintf("%s value is not a number %q", k8sNodesEnv, nodes))
   245  				}
   246  				for i := 3; i <= nodesInt; i++ {
   247  					vmName := fmt.Sprintf("%s%d-%s", helpers.K8s, i, helpers.GetCurrentK8SEnv())
   248  					err = helpers.CreateVM(vmName)
   249  					if err != nil {
   250  						reportCreateVMFailure(vmName, err)
   251  					}
   252  				}
   253  			}
   254  		}
   255  		kubectl := helpers.CreateKubectl(helpers.K8s1VMName(), logger)
   256  
   257  		kubectl.ApplyDefault(helpers.GetFilePath("../examples/kubernetes/addons/prometheus/prometheus.yaml"))
   258  
   259  		go kubectl.PprofReport()
   260  	}
   261  	return
   262  })
   263  
   264  var _ = AfterSuite(func() {
   265  	if !helpers.IsRunningOnJenkins() {
   266  		GinkgoPrint("AfterSuite: not running on Jenkins; leaving VMs running for debugging")
   267  		return
   268  	}
   269  	// Errors are not checked here because it should fail on BeforeAll
   270  	scope, _ := helpers.GetScope()
   271  	GinkgoPrint("cleaning up VMs started for %s tests", scope)
   272  	switch scope {
   273  	case helpers.Runtime:
   274  		helpers.DestroyVM(helpers.Runtime)
   275  	case helpers.K8s:
   276  		helpers.DestroyVM(helpers.K8s1VMName())
   277  		helpers.DestroyVM(helpers.K8s2VMName())
   278  	}
   279  	return
   280  })
   281  
   282  func getOrSetEnvVar(key, value string) {
   283  	if val := os.Getenv(key); val == "" {
   284  		log.Infof("environment variable %q was not set; setting to default value %q", key, value)
   285  		os.Setenv(key, value)
   286  	}
   287  }
   288  
   289  var _ = AfterEach(func() {
   290  
   291  	// Send the Checks output to Junit report to be render on Jenkins.
   292  	defer helpers.CheckLogs.Reset()
   293  	GinkgoPrint("<Checks>\n%s\n</Checks>\n", helpers.CheckLogs.Buffer.String())
   294  
   295  	defer config.TestLogWriterReset()
   296  	err := helpers.CreateLogFile(config.TestLogFileName, config.TestLogWriter.Bytes())
   297  	if err != nil {
   298  		log.WithError(err).Errorf("cannot create log file '%s'", config.TestLogFileName)
   299  		return
   300  	}
   301  
   302  	defer helpers.SSHMetaLogs.Reset()
   303  	err = helpers.CreateLogFile(commandsLogFileName, helpers.SSHMetaLogs.Bytes())
   304  	if err != nil {
   305  		log.WithError(err).Errorf("cannot create log file '%s'", commandsLogFileName)
   306  		return
   307  	}
   308  
   309  	// This piece of code is to enable zip attachments on Junit Output.
   310  	if ginkgo.CurrentGinkgoTestDescription().Failed && helpers.IsRunningOnJenkins() {
   311  		// ReportDirectory is already created. No check the error
   312  		path, _ := helpers.CreateReportDirectory()
   313  		zipFileName := fmt.Sprintf("%s_%s.zip", helpers.MakeUID(), ginkgoext.GetTestName())
   314  		zipFilePath := filepath.Join(helpers.TestResultsPath, zipFileName)
   315  
   316  		_, err := exec.Command(
   317  			"/bin/bash", "-c",
   318  			fmt.Sprintf("zip -qr %s %s", zipFilePath, path)).CombinedOutput()
   319  		if err != nil {
   320  			log.WithError(err).Errorf("cannot create zip file '%s'", zipFilePath)
   321  		}
   322  
   323  		ginkgoext.GinkgoPrint("[[ATTACHMENT|%s]]", zipFileName)
   324  	}
   325  
   326  	if !ginkgo.CurrentGinkgoTestDescription().Failed && helpers.IsRunningOnJenkins() {
   327  		// If the test success delete the monitor.log filename to not store all
   328  		// the data in Jenkins
   329  		testPath, err := helpers.CreateReportDirectory()
   330  		if err != nil {
   331  			log.WithError(err).Error("cannot retrieve test result path")
   332  			return
   333  		}
   334  		_ = os.Remove(filepath.Join(testPath, helpers.MonitorLogFileName))
   335  	}
   336  })