istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/README.md (about) 1 # Istio Integration Tests 2 3 This folder contains Istio integration tests that use the test framework checked in at 4 [istio.io/istio/pkg/test/framework](https://github.com/istio/istio/tree/master/pkg/test/framework). 5 6 ## Table of Contents 7 8 1. [Overview](#overview) 9 1. [Writing Tests](#writing-tests) 10 1. [Adding a Test Suite](#adding-a-test-suite) 11 1. [Sub-Tests](#sub-tests) 12 1. [Parallel Tests](#parallel-tests) 13 1. [Using Components](#using-components) 14 1. [Writing Components](#writing-components) 15 1. [Running Tests](#running-tests) 16 1. [Test Parallelism and Kubernetes](#test-parellelism-and-kubernetes) 17 1. [Test Selection](#test-selection) 18 1. [Running Tests on CI](#running-tests-on-ci) 19 1. [Step 1: Add a Test Script](#step-1-add-a-test-script) 20 1. [Step 2: Add a Prow Job](#step-2-add-a-prow-job) 21 1. [Step 3: Update TestGrid](#step-3-update-testgrid) 22 1. [Environments](#environments) 23 1. [Diagnosing Failures](#diagnosing-failures) 24 1. [Working Directory](#working-directory) 25 1. [Enabling CI Mode](#enabling-ci-mode) 26 1. [Preserving State (No Cleanup)](#preserving-state-no-cleanup) 27 1. [Additional Logging](#additional-logging) 28 1. [Running Tests Under Debugger](#running-tests-under-debugger-goland) 29 1. [Reference](#reference) 30 1. [Helm Values Overrides](#helm-values-overrides) 31 1. [Commandline Flags](#command-line-flags) 32 1. [Notes](#notes) 33 1. [Running on a Mac](#running-on-a-mac) 34 35 ## Overview 36 37 The goal of the framework is to make it as easy as possible to author and run tests. In its simplest 38 case, just typing ```go test ./...``` should be sufficient to run tests. 39 40 This guide walks through the basics of writing tests with the Istio test framework. For best 41 practices, see [Writing Good Integration Tests](https://github.com/istio/istio/wiki/Writing-Good-Integration-Tests). 42 43 ## Writing Tests 44 45 The test framework is designed to work with standard go tooling and allows developers 46 to write environment-agnostics tests in a high-level fashion. 47 48 ### Adding a Test Suite 49 50 All tests that use the framework, must run as part of a *suite*. Only a single suite can be defined per package, since 51 it is bootstrapped by a Go `TestMain`, which has the same restriction. 52 53 To begin, create a new folder for your suite under 54 [tests/integration](https://github.com/istio/istio/tree/master/tests/integration). 55 56 ```console 57 $ cd ${ISTIO}/tests/integration 58 $ mkdir mysuite 59 ``` 60 61 Within that package, create a `TestMain` to bootstrap the test suite: 62 63 ```go 64 func TestMain(m *testing.M) { 65 framework. 66 NewSuite("mysuite", m). 67 Run() 68 } 69 ``` 70 71 Next, define your tests in the same package: 72 73 ```go 74 func TestMyLogic(t *testing.T) { 75 framework. 76 NewTest(t). 77 Run(func(ctx framework.TestContext) { 78 // Create a component 79 p := pilot.NewOrFail(ctx, ctx, cfg) 80 81 // Use the component. 82 // Apply Kubernetes Config 83 ctx.ApplyConfigOrFail(ctx, nil, mycfg) 84 85 // Do more stuff here. 86 } 87 } 88 ``` 89 90 The `framework.TestContext` is a wrapper around the underlying `testing.T` and implements the same interface. Test code 91 should generally not interact with the `testing.T` directly. 92 93 In the `TestMain`, you can also restrict the test to particular environment, apply labels, or do test-wide setup, such as 94 deploying Istio. 95 96 ```go 97 func TestMain(m *testing.M) { 98 framework. 99 NewSuite("mysuite", m). 100 // Deploy Istio on the cluster 101 Setup(istio.Setup(nil, nil)). 102 // Run your own custom setup 103 Setup(mySetup). 104 Run() 105 } 106 107 func mySetup(ctx resource.Context) error { 108 // Your own setup code 109 return nil 110 } 111 ``` 112 113 ### Sub-Tests 114 115 Go allows you to run sub-tests with `t.Run()`. Similarly, this framework supports nesting tests with `ctx.NewSubTest()`: 116 117 ```go 118 func TestMyLogic(t *testing.T) { 119 framework. 120 NewTest(t). 121 Run(func(ctx framework.TestContext) { 122 123 // Create a component 124 g := galley.NewOrFail(ctx, ctx, cfg) 125 126 configs := []struct{ 127 name: string 128 yaml: string 129 } { 130 // Some array of YAML 131 } 132 133 for _, cfg := range configs { 134 ctx.NewSubTest(cfg.name). 135 Run(func(ctx framework.TestContext) { 136 ctx.ApplyConfigOrFail(ctx, nil, mycfg) 137 // Do more stuff here. 138 }) 139 } 140 }) 141 } 142 ``` 143 144 Under the hood, calling `subtest.Run()` delegates to `t.Run()` in order to create a child `testing.T`. 145 146 ### Parallel Tests 147 148 Many tests can take a while to start up for a variety of reasons, such as waiting for pods to start or waiting 149 for a particular piece of configuration to propagate throughout the system. Where possible, it may be desirable 150 to run these sorts of tests in parallel: 151 152 ```go 153 func TestMyLogic(t *testing.T) { 154 framework. 155 NewTest(t). 156 RunParallel(func(ctx framework.TestContext) { 157 // ... 158 } 159 } 160 ``` 161 162 Under the hood, this relies on Go's `t.Parallel()` and will, therefore, have the same behavior. 163 164 A parallel test will run in parallel with siblings that share the same parent test. The parent test function 165 will exit before the parallel children are executed. It should be noted that if the parent test is prevented 166 from exiting (e.g. parent test is waiting for something to occur within the child test), the test will 167 deadlock. 168 169 Consider the following example: 170 171 ```go 172 func TestMyLogic(t *testing.T) { 173 framework.NewTest(t). 174 Run(func(ctx framework.TestContext) { 175 ctx.NewSubTest("T1"). 176 Run(func(ctx framework.TestContext) { 177 ctx.NewSubTest("T1a"). 178 RunParallel(func(ctx framework.TestContext) { 179 // Run in parallel with T1b 180 }) 181 ctx.NewSubTest("T1b"). 182 RunParallel(func(ctx framework.TestContext) { 183 // Run in parallel with T1a 184 }) 185 // Exits before T1a and T1b are run. 186 }) 187 188 ctx.NewSubTest("T2"). 189 Run(func(ctx framework.TestContext) { 190 ctx.NewSubTest("T2a"). 191 RunParallel(func(ctx framework.TestContext) { 192 // Run in parallel with T2b 193 }) 194 ctx.NewSubTest("T2b"). 195 RunParallel(func(ctx framework.TestContext) { 196 // Run in parallel with T2a 197 }) 198 // Exits before T2a and T2b are run. 199 }) 200 }) 201 } 202 ``` 203 204 In the example above, non-parallel parents T1 and T2 contain parallel children T1a, T1b, T2a, T2b. 205 206 Since both T1 and T2 are non-parallel, they are run synchronously: T1 followed by T2. After T1 exits, 207 T1a and T1b are run asynchronously with each other. After T1a and T1b complete, T2 is then run in the 208 same way: T2 exits, then T2a and T2b are run asynchronously to completion. 209 210 ### Using Components 211 212 The framework itself is just a platform for running tests and tracking resources. Without these `resources`, there 213 isn't much added value. Enter: components. 214 215 Components are utilities that provide abstractions for Istio resources. They are maintained in the 216 [components package](https://github.com/istio/istio/tree/master/pkg/test/framework/components), which defines 217 various Istio components such as galley, pilot, and namespaces. 218 219 Each component defines their own API which simplifies their use from test code, abstracting away the 220 environment-specific details. This means that the test code can (and should, where possible) be written in an 221 environment-agnostic manner, so that they can be run against any Istio implementation. 222 223 For example, the following code creates and then interacts with a Galley and Pilot component: 224 225 ```go 226 func TestMyLogic(t *testing.T) { 227 framework. 228 NewTest(t). 229 Run(func(ctx framework.TestContext) { 230 // Create the components. 231 g := galley.NewOrFail(ctx, ctx, galley.Config{}) 232 p := pilot.NewOrFail(ctx, ctx, pilot.Config {}) 233 234 // Apply configuration via Galley. 235 ctx.ApplyConfigOrFail(ctx, nil, mycfg) 236 237 // Wait until Pilot has received the configuration update. 238 p.StartDiscoveryOrFail(t, discoveryRequest) 239 p.WatchDiscoveryOrFail(t, timeout, 240 func(response *xdsapi.DiscoveryResponse) (b bool, e error) { 241 // Validate that the discovery response has the configuration applied. 242 }) 243 // Do more stuff... 244 } 245 } 246 ``` 247 248 When a component is created, the framework tracks its lifecycle. When the test exits, any components that were 249 created during the test are automatically closed. 250 251 ### Writing Components 252 253 To add a new component, you'll first need to create a top-level folder for your component under the 254 [components folder](https://github.com/istio/istio/tree/master/pkg/test/framework/components). 255 256 ```console 257 $ cd ${ISTIO}/pkg/test/framework/components 258 $ mkdir mycomponent 259 ``` 260 261 You'll then need to define your component's API. 262 263 ```go 264 package mycomponent 265 266 type Instance interface { 267 resource.Resource 268 269 DoStuff() error 270 DoStuffOrFail(t test.Failer) 271 } 272 ``` 273 274 | NOTE: A common pattern is to provide two versions of many methods: one that returns an error as well as an `OrFail` version that fails the test upon encountering an error. This provides options to the calling test and helps to simplify the calling logic. | 275 | --- | 276 277 Next you need to implement your component for one or more environments. If possible, create both a native and Kubernetes version. 278 279 ```go 280 package mycomponent 281 282 type nativeComponent struct { 283 id resource.ID 284 // ... 285 } 286 287 func newNative(ctx resource.Context) (Instance, error) { 288 if config.Galley == nil { 289 return nil, errors.New("galley must be provided") 290 } 291 292 instance := &nativeComponent{} 293 instance.id = ctx.TrackResource(instance) 294 295 //... 296 return instance, nil 297 } 298 299 func (c *nativeComponent) ID() resource.ID { 300 return c.id 301 } 302 ``` 303 304 Each implementation of the component must implement `resource.Resource`, which just exposes a unique identifier for your 305 component instances used for resource tracking by the framework. To get the ID, the component must call `ctx.TrackResource` 306 during construction. 307 308 Finally, you'll need to provide an environment-agnostic constructor for your component: 309 310 ```go 311 package mycomponent 312 313 func New(ctx resource.Context) (i Instance, err error){ 314 err = resource.UnsupportedEnvironment(ctx.Environment()) 315 ctx.Environment().Case(environment.Native, func() { 316 i, err = newNative(ctx) 317 }) 318 ctx.Environment().Case(environment.Kube, func() { 319 i, err = newKube(ctx) 320 }) 321 return 322 } 323 324 func NewOrFail(t test.Failer, ctx resource.Context) Instance { 325 i, err := New(ctx) 326 if err != nil { 327 t.Fatal(err) 328 } 329 return i 330 } 331 ``` 332 333 Now that everything is in place, you can begin using your component: 334 335 ```go 336 func TestMyLogic(t *testing.T) { 337 framework. 338 NewTest(t). 339 Run(func(ctx framework.TestContext) { 340 // Create the components. 341 g := myComponent.NewOrFail(ctx, ctx) 342 343 // Do more stuff... 344 } 345 } 346 ``` 347 348 ## Running Tests 349 350 The test framework builds on top of the Go testing infrastructure, and is therefore compatible with 351 the standard `go test` command-line. For example, to run the tests under the `/tests/integration/mycomponent` 352 using the default (native) environment, you can simply type: 353 354 ```console 355 $ go test -tags=integ ./tests/integration/mycomponent/... 356 ``` 357 358 Note that samples below invoking variations of ```go test ./...``` are intended to be run from the ```tests/integration``` directory. 359 360 Tests are tagged with the `integ` build target to avoid accidental invocation. If this is not set, no tests will be run. 361 362 ### Test Parellelism and Kubernetes 363 364 By default, Go will run tests within the same package (i.e. suite) synchronously. However, tests in other packages 365 may be run concurrently. 366 367 When running in the Kubernetes environment this can be problematic for suites that deploy Istio. The Istio deployment, 368 as it stands is a singleton per cluster. If multiple suites attempt to deploy/configure Istio, 369 they can corrupt each other and/or simply fail. To avoid this issue, you have a couple of options: 370 371 1. Run one suite per command (e.g. `go test ./tests/integration/mysuite/...`) 372 1. Disable parallelism with `-p 1` (e.g. `go test -p 1 ./...`). A major disadvantage to doing this is that it will also disable 373 parallelism within the suite, even when explicitly specified via [RunParallel](#parallel-tests). 374 375 ### Test Selection 376 377 When no flags are specified, the test framework will run all applicable tests. It is possible to filter in/out specific 378 tests using 2 mechanisms: 379 380 1. The standard ```-run <regexp>``` flag, as exposed by Go's own test framework. 381 1. ```--istio.test.select <filter-expr>``` flag to select/skip framework-aware tests that use labels. 382 383 For example, if a test, or test suite uses labels in this fashion: 384 385 ```go 386 func TestMain(m *testing.M) { 387 framework. 388 NewSuite("galley_conversion", m). 389 // Test is tagged with "CustomSetup" label 390 Label(label.CustomSetup). 391 Run() 392 ``` 393 394 Then you can explicitly select execution of such tests using label based selection. For example, the following expression 395 will select only the tests that have the ```label.CustomSetup``` label. 396 397 ```console 398 $ go test ./... --istio.test.select +customsetup 399 ``` 400 401 Similarly, you can exclude tests that use ```label.CustomSetup``` label by: 402 403 ```console 404 $ go test ./... --istio.test.select -customsetup 405 ``` 406 407 You can "and" the predicates by separating with commas: 408 409 ```console 410 $ go test ./... --istio.test.select +customsetup,-postsubmit 411 ``` 412 413 This will select tests that have ```label.CustomSetup``` only. It will **not** select tests that have both ```label.CustomSetup``` 414 and ```label.Postsubmit```. 415 416 ### Running Tests on CI 417 418 Istio's CI/CD system is composed of 2 parts: 419 420 Tool | Description | 421 ---|--- 422 [Prow](https://github.com/kubernetes/test-infra/tree/master/prow) | Kubernetes-based CI/CD system developed by the Kubernetes community and is deployed in Google Kubernetes Engine (GKE). 423 [TestGrid](https://k8s-testgrid.appspot.com/istio-release) | A Kubernetes dashboard used for visualizing the status of the Prow jobs. 424 425 Test suites are defined for each toplevel directory (such as `pilot` and `telemetry`), so any tests added to these directories will automatically be run in CI. 426 427 If you need to add a new test suite, it can be added to the [job configuration](https://github.com/istio/test-infra/blob/master/prow/config/jobs/istio.yaml). 428 429 ## Environments 430 431 The test binaries run in a Kubernetes cluster, but the test logic runs in the test binary. 432 433 ```console 434 $ go test ./... -p 1 435 ``` 436 437 | WARNING: ```-p 1``` is required when running directly in the ```tests/integration/``` folder. | 438 | --- | 439 440 You will need to provide a K8s cluster to run the tests against. 441 (See [here](https://github.com/istio/istio/blob/master/tests/integration/GKE.md) 442 for info about how to set up a suitable GKE cluster.) 443 You can specify the kube config file that should be used to use for connecting to the cluster, through 444 command-line: 445 446 ```console 447 $ go test ./... -p 1 --istio.test.kube.config ~/.kube/config 448 ``` 449 450 If not specified, `~/.kube/config` will be used by default. 451 452 **Be aware that any existing content will be altered and/or removed from the cluster**. 453 454 Note that the HUB and TAG environment variables **must** be set when running tests in the Kubernetes environment. 455 456 ## Diagnosing Failures 457 458 ### Working Directory 459 460 The test framework will generate additional diagnostic output in its work directory. Typically, this is 461 created under the host operating system's temporary folder (which can be overridden using 462 the `--istio.test.work_dir` flag). The name of the work dir will be based on the test id that is supplied in 463 a tests TestMain method. These files typically contain some of the logging & diagnostic output that components 464 spew out as part of test execution 465 466 ```console 467 $ go test galley/... --istio.test.work_dir /foo 468 ... 469 470 $ ls /foo 471 galley-test-4ef25d910d2746f9b38/ 472 473 $ ls /foo/galley-test-4ef25d910d2746f9b38/ 474 istio-system-1537332205890088657.yaml 475 ... 476 ``` 477 478 ### Enabling CI Mode 479 480 When executing in the CI systems, the makefiles use the ```--istio.test.ci``` flag. This flag causes a few changes in 481 behavior. Specifically, more verbose logging output will be displayed, some of the timeout values will be more relaxed, and 482 additional diagnostic data will be dumped into the working directory at the end of the test execution. 483 484 The flag is not enabled by default to provide a better U/X when running tests locally (i.e. additional logging can clutter 485 test output and error dumping can take quite a while). However, if you see a behavior difference between local and CI runs, 486 you can enable the flag to make the tests work in a similar fashion. 487 488 ### Preserving State (No Cleanup) 489 490 By default, the test framework will cleanup all deployed artifacts after the test run, especially on the Kubernetes 491 environment. You can specify the ```--istio.test.nocleanup``` flag to stop the framework from cleaning up the state 492 for investigation. 493 494 ### Additional Logging 495 496 The framework accepts standard istio logging flags. You can use these flags to enable additional logging for both the 497 framework, as well as some of the components that are used in-line in the native environment: 498 499 ```console 500 $ go test ./... --log_output_level=tf:debug 501 ``` 502 503 The above example will enable debugging logging for the test framework (```tf```) and the MCP protocol stack (```mcp```). 504 505 ### Running Tests Under Debugger (GoLand) 506 507 The tests authored in the new test framework can be debugged directly under GoLand using the debugger. If you want to 508 pass command-line flags to the test while running under the debugger, you can use the 509 [Run/Debug configurations dialog](https://i.stack.imgur.com/C6y0L.png) to specify these flags as program arguments. 510 511 ## Reference 512 513 ### Command-Line Flags 514 515 The test framework supports the following command-line flags: 516 517 | Name | Type | Description | 518 |------|------|-------------| 519 | -istio.test.work_dir | string | Local working directory for creating logs/temp files. If left empty, os.TempDir() is used. | 520 | -istio.test.ci | bool | Enable CI Mode. Additional logging and state dumping will be enabled. | 521 | -istio.test.nocleanup | bool | Do not cleanup resources after test completion. | 522 | -istio.test.select | string | Comma separated list of labels for selecting tests to run (e.g. 'foo,+bar-baz'). | 523 | -istio.test.hub | string | Container registry hub to use (default HUB environment variable). | 524 | -istio.test.tag | string | Common Container tag to use when deploying container images (default TAG environment variable). | 525 | -istio.test.pullpolicy | string | Common image pull policy to use when deploying container images. | 526 | -istio.test.kube.config | string | A comma-separated list of paths to kube config files for cluster environments. (default ~/.kube/config). | 527 | -istio.test.kube.deploy | bool | Deploy Istio into the target Kubernetes environment. (default true). | 528 | -istio.test.kube.deployEastWestGW | bool | Deploy Istio east west gateway into the target Kubernetes environment. (default true). | 529 | -istio.test.kube.systemNamespace | string | The namespace where the Istio components reside in a typical deployment. (default "istio-system"). | 530 | -istio.test.kube.helm.values | string | Manual overrides for Helm values file. Only valid when deploying Istio. | 531 | -istio.test.kube.helm.iopFile | string | IstioOperator spec file. This can be an absolute path or relative to the repository root. Defaults to "tests/integration/iop-integration-test-defaults.yaml". | 532 | -istio.test.kube.loadbalancer | bool | Used to obtain the right IP address for ingress gateway. This should be false for any environment that doesn't support a LoadBalancer type. | 533 | -istio.test.revision | string | Overwrite the default namespace label (istio-enabled=true) with revision lable (istio.io/rev=XXX). (default is no overwrite). | 534 | -istio.test.skip | []string | Skip tests matching the regular expression. This follows the semantics of -test.run. | 535 | -istio.test.skipVM | bool | Skip all the VM related parts in all the tests. (default is "false"). | 536 | -istio.test.helmRepo | string | Overwrite the default helm Repo used for the tests. | 537 | -istio.test.ambient | bool | Indicate the use of ambient mesh. | 538 | -istio.test.openshift | bool | Set to `true` when running the tests in an OpenShift cluster, rather than in KinD. | 539 540 ## Notes 541 542 ### Running on a Mac 543 544 * Currently some _native_ tests fail when being run on a Mac with an error like: 545 546 ```plain 547 unable to locate an Envoy binary 548 ``` 549 550 This is documented in this [PR](https://github.com/istio/istio/issues/13677). Once the Envoy binary is available for the Mac, 551 these tests will hopefully succeed. 552 553 * If one uses Docker for Mac for the kubernetes environment be sure to specify the `-istio.test.kube.loadbalancer=false` parameter. This solves an error like: 554 555 ```plain 556 service ingress is not available yet 557 ```