|
| 1 | +# Developing E2E tests |
| 2 | + |
| 3 | +E2E tests are meant to verify the proper functioning of a Cluster API management |
| 4 | +cluster in an environment that resemble a real production environment. |
| 5 | + |
| 6 | +Following guidelines should be followed when developing E2E tests: |
| 7 | + |
| 8 | +- Use the [Cluster API test framework]. |
| 9 | +- Define test spec reflecting real user workflow, e.g. [Cluster API quick start]. |
| 10 | +- Unless you are testing provider specific features, ensure your test can run with |
| 11 | + different infrastructure providers (see [Writing Portable Tests](#writing-portable-e2e-tests)). |
| 12 | + |
| 13 | +The [Cluster API test framework] provides you a set of helpers method for getting your test in place |
| 14 | +quickly; the [test E2E package] provide examples of how this can be achieved and reusable |
| 15 | +test specs for the most common Cluster API use cases. |
| 16 | + |
| 17 | +## Prerequisites |
| 18 | + |
| 19 | +Each E2E test requires a set of artifacts to be available: |
| 20 | + |
| 21 | +- Binaries & docker images for Kubernetes, CNI, CRI & CSI |
| 22 | +- Manifests & docker images for the Cluster API core components |
| 23 | +- Manifests & docker images for the Cluster API infrastructure provider; in most cases |
| 24 | + also machine images are required (AMI, OVA etc.) |
| 25 | +- Credentials for the target infrastructure provider |
| 26 | +- Other support tools (e.g. kustomize, gsutil etc.) |
| 27 | + |
| 28 | +The Cluster API test framework provides support for building and retrieving the manifest |
| 29 | +files for Cluster API core components and for the Cluster API infrastructure provider |
| 30 | +(see [Setup](#setup)) |
| 31 | + |
| 32 | +For the remaining tasks you can find examples of |
| 33 | +how this can be implemented e.g. in [CAPA E2E tests] and [CAPG E2E tests]. |
| 34 | + |
| 35 | +## Setup |
| 36 | + |
| 37 | +In order to run E2E tests it is required to create a Kubernetes cluster with a |
| 38 | +complete set of Cluster API providers installed. Setting up those elements is |
| 39 | +usually implemented in a `BeforeSuite` function, and it consists of two steps: |
| 40 | + |
| 41 | +- Defining an E2E config file |
| 42 | +- Creating the management cluster and installing providers |
| 43 | + |
| 44 | +### Defining an E2E config file |
| 45 | + |
| 46 | +The [E2E config file] provides a convenient and flexible way to define common tasks for |
| 47 | +setting up a management cluster. |
| 48 | + |
| 49 | +Using the config file it is possible to: |
| 50 | + |
| 51 | +- Define the list of providers to be installed in the management cluster. Most notably, |
| 52 | + for each provider it is possible to define: |
| 53 | + - One or more versions of the providers manifest (built from the sources, or pulled from a |
| 54 | + remote location). |
| 55 | + - A list of additional files to be added to the provider repository, to be used e.g. |
| 56 | + to provide `cluster-templates.yaml` files. |
| 57 | +- Define the list of variables to be used when doing `clusterctl init` or |
| 58 | + `clusterctl config cluster`. |
| 59 | +- Define a list of intervals to be used in the test specs for defining timeouts for the |
| 60 | + wait and `Eventually` methods. |
| 61 | +- Define the list of images to be loaded in the management cluster (this is specif of |
| 62 | + management cluster based on kind). |
| 63 | + |
| 64 | +An [example E2E config file] can be found here. |
| 65 | + |
| 66 | +<aside class="note"> |
| 67 | + |
| 68 | +<h1>Deprecated E2E config file format</h1> |
| 69 | + |
| 70 | +The [Cluster API test framework] includes also a [deprecated E2E config file] implementation, |
| 71 | +that was used before the introduction of clusterctl. This might be removed in future releases |
| 72 | +of the test framework. |
| 73 | + |
| 74 | +</aside> |
| 75 | + |
| 76 | +### Creating the management cluster and installing providers |
| 77 | + |
| 78 | +In order to run Cluster API E2E tests, you need a Kubernetes cluster; the [NewKindClusterProvider] gives you a |
| 79 | +type that can be used to create a local kind cluster and pre-load images into it, but also existing clusters can |
| 80 | +be used if available. |
| 81 | + |
| 82 | +Once you have a Kubernetes cluster, the [InitManagementClusterAndWatchControllerLogs method] provides a convenient |
| 83 | +way for installing providers. |
| 84 | + |
| 85 | +This method: |
| 86 | +- Runs `clusterctl init` using the above local repository. |
| 87 | +- Waits for the providers controllers to be running. |
| 88 | +- Creates log watchers for all the providers |
| 89 | + |
| 90 | +<aside class="note"> |
| 91 | + |
| 92 | +<h1>Deprecated InitManagementCluster method</h1> |
| 93 | + |
| 94 | +The [Cluster API test framework] includes also a [deprecated InitManagementCluster method] implementation, |
| 95 | +that was used before the introduction of clusterctl. This might be removed in future releases |
| 96 | +of the test framework. |
| 97 | + |
| 98 | +</aside> |
| 99 | + |
| 100 | +## Writing test specs |
| 101 | + |
| 102 | +A typical test spec is a sequence of: |
| 103 | + |
| 104 | +- Creating a namespace to host in isolation all the test objects |
| 105 | +- Creating objects in the management cluster, wait for the corresponding infrastructure to be provisioned. |
| 106 | +- Exec operations like e.g. changing the Kubernetes version or `clusterctl move`, wait for the action to complete. |
| 107 | +- Delete objects in the management cluster, wait for the corresponding infrastructure to be terminated. |
| 108 | + |
| 109 | +### Creating Namespaces |
| 110 | + |
| 111 | +The [CreateNamespaceAndWatchEvents method] provides a convenient way to create a namespace and setup |
| 112 | +watches for capturing namespaces events |
| 113 | + |
| 114 | +### Creating objects |
| 115 | + |
| 116 | +There are two possible approaches for creating objects in the management cluster: |
| 117 | + |
| 118 | +- Create object by object: create the `Cluster` object, then `AwsCluster`, `Machines`, `AwsMachines` etc. |
| 119 | +- Apply a `cluster-templates.yaml` file thus creating all the objects this file contains. |
| 120 | + |
| 121 | +The first approaches leverage on the [controller-runtime Client] and gives you full control, but it comes with |
| 122 | +some drawbacks as well, because this method does not reflect directly real user workflows, and most importantly, |
| 123 | +the resulting tests are not as reusable with other infrastructure providers. (See [writing portable tests](#writing-portable-e2e-tests)). |
| 124 | + |
| 125 | +We recommend using the [ClusterTemplate method] and the [Apply method] for creating objects in the cluster. |
| 126 | +This methods mimics the recommended user workflows, and it is based on `cluster-templates.yaml` files that can be |
| 127 | +provided via the [E2E config file], and thus easily swappable when changing the target infrastructure provider. |
| 128 | + |
| 129 | +<aside class="note"> |
| 130 | + |
| 131 | +<h1>Tips</h1> |
| 132 | + |
| 133 | +If you need control over object creation but you want to preserve portability, you can create many templates |
| 134 | +files each one creating only a small set of objects (instead of using a single template creating a full cluster). |
| 135 | + |
| 136 | +</aside> |
| 137 | + |
| 138 | +After creating objects in the cluster, use the existing methods in the [Cluster API test framework] to discover |
| 139 | +which object was created in the cluster so your code can adapt to different `cluster-templates.yaml` files. |
| 140 | + |
| 141 | +Once you have objects references, the framework includes methods for waiting for the corresponding |
| 142 | +infrastructure to be provisioned, e.g. [WaitForClusterToProvision], [WaitForKubeadmControlPlaneMachinesToExist]. |
| 143 | + |
| 144 | +### Exec operations |
| 145 | + |
| 146 | +You can use [Cluster API test framework] methods to modify Cluster API objects, as a last option, use |
| 147 | +the [controller-runtime Client]. |
| 148 | + |
| 149 | +The [Cluster API test framework] includes also methods for executing clusterctl operations, like e.g. |
| 150 | +the [ClusterTemplate method], the [ClusterctlMove method] etc.; in order to improve observability, |
| 151 | +each clusterctl operation creates a detailed log. |
| 152 | + |
| 153 | +After using clusterctl operations, you can rely on the `Get` and on the `Wait` methods |
| 154 | +defined in the [Cluster API test framework] to check if the operation completed successfully. |
| 155 | + |
| 156 | +## Tear down |
| 157 | + |
| 158 | +After a test completes/fails, it is required to: |
| 159 | + |
| 160 | +- Collect all the logs for the Cluster API controllers |
| 161 | +- Dump all the relevant Cluster API/Kubernetes objects |
| 162 | +- Cleanup all the infrastructure resources created during the test |
| 163 | + |
| 164 | +Those task are usually implemented in the `AfterSuite`, and again the [Cluster API test framework] provides |
| 165 | +you useful methods for those tasks. |
| 166 | + |
| 167 | +Please note that despite the fact that test specs are expected to delete objects in the management cluster and |
| 168 | +wait for the corresponding infrastructure to be terminated, it can happen that the test spec |
| 169 | +fails before starting object deletion or that objects deletion itself fails. |
| 170 | + |
| 171 | +As a consequence, when scheduling/running a test suite, it is required to ensure all the generated |
| 172 | +resources are cleaned up. In Kubernetes, this is implemented by the [boskos] project. |
| 173 | + |
| 174 | +## Writing portable E2E tests |
| 175 | + |
| 176 | +A portable E2E test is a test can run with different infrastructure providers by simply |
| 177 | +changing the test configuration file. |
| 178 | + |
| 179 | +Following recommendations should be followed to write portable E2E tests: |
| 180 | + |
| 181 | +- Create different [E2E config file], one for each target infrastructure provider, |
| 182 | + providing different sets of env variables and timeout intervals. |
| 183 | +- Use the [InitManagementCluster method] for setting up the management cluster. |
| 184 | +- Use the [ClusterTemplate method] and the [Apply method] |
| 185 | + for creating objects in the cluster using `cluster-templates.yaml` files instead |
| 186 | + of hard coding object creation. |
| 187 | +- Use the `Get` methods defined in the [Cluster API test framework] to checks object |
| 188 | + being created, so your code can adapt to different `cluster-templates.yaml` files. |
| 189 | +- Never hard code the infrastructure provider name in your test spec. |
| 190 | + Instead, use the [InfrastructureProvider method] to get access to the |
| 191 | + name of the infrastructure provider defined in the [E2E config file]. |
| 192 | +- Never hard code wait intervals in your test spec. |
| 193 | + Instead use the [GetIntervals method] to get access to the |
| 194 | + intervals defined in the [E2E config file]. |
| 195 | + |
| 196 | +## Cluster API conformance tests |
| 197 | + |
| 198 | +As of today there is no a well-defined suites of E2E tests that can be used as a |
| 199 | +baseline for Cluster API conformance. |
| 200 | + |
| 201 | +However, creating such suite is something that can provide a huge value for the |
| 202 | +long term success of the project. |
| 203 | + |
| 204 | +The [test E2E package] provide examples of how this can be achieved implemeting a set of and reusable |
| 205 | +test specs for the most common Cluster API use cases. |
| 206 | + |
| 207 | +<!-- links --> |
| 208 | +[Cluster API quick start]: https://cluster-api.sigs.k8s.io/user/quick-start.html |
| 209 | +[Cluster API test framework]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc |
| 210 | +[deprecated E2E config file]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#Config |
| 211 | +[deprecated InitManagementCluster method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#InitManagementCluster |
| 212 | +[Apply method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#Applier |
| 213 | +[CAPA E2E tests]: https://github.com/kubernetes-sigs/cluster-api-provider-aws/blob/master/scripts/ci-e2e.sh |
| 214 | +[CAPG E2E tests]: https://github.com/kubernetes-sigs/cluster-api-provider-gcp/blob/master/scripts/ci-e2e.sh |
| 215 | +[WaitForClusterToProvision]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#WaitForClusterToProvision |
| 216 | +[WaitForKubeadmControlPlaneMachinesToExist]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#WaitForKubeadmControlPlaneMachinesToExist |
| 217 | +[controller-runtime Client]: https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.5.2/pkg/client?tab=doc#Client |
| 218 | +[boskos]: https://github.com/kubernetes/test-infra/tree/master/boskos |
| 219 | +[E2E config file]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#E2EConfig |
| 220 | +[example E2E config file]: https://github.com/kubernetes-sigs/cluster-api/blob/master/test/e2e/config/docker-dev.yaml |
| 221 | +[NewKindClusterProvider]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/bootstrap?tab=doc#NewKindClusterProvider |
| 222 | +[InitManagementClusterAndWatchControllerLogs method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#InitManagementClusterAndWatchControllerLogs |
| 223 | +[ClusterTemplate method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#ConfigCluster |
| 224 | +[ClusterctlMove method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#Move |
| 225 | +[InfrastructureProvider method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#E2EConfig.InfrastructureProviders |
| 226 | +[GetIntervals method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#E2EConfig.GetIntervals |
| 227 | +[test E2E package]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/e2e?tab=doc |
| 228 | +[CreateNamespaceAndWatchEvents method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#CreateNamespaceAndWatchEvents |
0 commit comments