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