Skip to content

Commit

Permalink
Merge pull request #2839 from fabriziopandini/book-developing-e2e-test
Browse files Browse the repository at this point in the history
📖 developing e2e tests
  • Loading branch information
k8s-ci-robot authored May 15, 2020
2 parents 15300a4 + 8476213 commit beff1e1
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 37 deletions.
1 change: 1 addition & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [Repository Layout](./developer/repository-layout.md)
- [Rapid iterative development with Tilt](./developer/tilt.md)
- [Testing](./developer/testing.md)
- [Developing E2E tests](./developer/e2e.md)
- [Controllers](./developer/architecture/controllers.md)
- [Bootstrap](./developer/architecture/controllers/bootstrap.md)
- [Cluster](./developer/architecture/controllers/cluster.md)
Expand Down
228 changes: 228 additions & 0 deletions docs/book/src/developer/e2e.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
# Developing E2E tests

E2E tests are meant to verify the proper functioning of a Cluster API management
cluster in an environment that resemble a real production environment.

Following guidelines should be followed when developing E2E tests:

- Use the [Cluster API test framework].
- Define test spec reflecting real user workflow, e.g. [Cluster API quick start].
- Unless you are testing provider specific features, ensure your test can run with
different infrastructure providers (see [Writing Portable Tests](#writing-portable-e2e-tests)).

The [Cluster API test framework] provides you a set of helpers method for getting your test in place
quickly; the [test E2E package] provide examples of how this can be achieved and reusable
test specs for the most common Cluster API use cases.

## Prerequisites

Each E2E test requires a set of artifacts to be available:

- Binaries & docker images for Kubernetes, CNI, CRI & CSI
- Manifests & docker images for the Cluster API core components
- Manifests & docker images for the Cluster API infrastructure provider; in most cases
also machine images are required (AMI, OVA etc.)
- Credentials for the target infrastructure provider
- Other support tools (e.g. kustomize, gsutil etc.)

The Cluster API test framework provides support for building and retrieving the manifest
files for Cluster API core components and for the Cluster API infrastructure provider
(see [Setup](#setup))

For the remaining tasks you can find examples of
how this can be implemented e.g. in [CAPA E2E tests] and [CAPG E2E tests].

## Setup

In order to run E2E tests it is required to create a Kubernetes cluster with a
complete set of Cluster API providers installed. Setting up those elements is
usually implemented in a `BeforeSuite` function, and it consists of two steps:

- Defining an E2E config file
- Creating the management cluster and installing providers

### Defining an E2E config file

The [E2E config file] provides a convenient and flexible way to define common tasks for
setting up a management cluster.

Using the config file it is possible to:

- Define the list of providers to be installed in the management cluster. Most notably,
for each provider it is possible to define:
- One or more versions of the providers manifest (built from the sources, or pulled from a
remote location).
- A list of additional files to be added to the provider repository, to be used e.g.
to provide `cluster-templates.yaml` files.
- Define the list of variables to be used when doing `clusterctl init` or
`clusterctl config cluster`.
- Define a list of intervals to be used in the test specs for defining timeouts for the
wait and `Eventually` methods.
- Define the list of images to be loaded in the management cluster (this is specif of
management cluster based on kind).

An [example E2E config file] can be found here.

<aside class="note">

<h1>Deprecated E2E config file format</h1>

The [Cluster API test framework] includes also a [deprecated E2E config file] implementation,
that was used before the introduction of clusterctl. This might be removed in future releases
of the test framework.

</aside>

### Creating the management cluster and installing providers

In order to run Cluster API E2E tests, you need a Kubernetes cluster; the [NewKindClusterProvider] gives you a
type that can be used to create a local kind cluster and pre-load images into it, but also existing clusters can
be used if available.

Once you have a Kubernetes cluster, the [InitManagementClusterAndWatchControllerLogs method] provides a convenient
way for installing providers.

This method:
- Runs `clusterctl init` using the above local repository.
- Waits for the providers controllers to be running.
- Creates log watchers for all the providers

<aside class="note">

<h1>Deprecated InitManagementCluster method</h1>

The [Cluster API test framework] includes also a [deprecated InitManagementCluster method] implementation,
that was used before the introduction of clusterctl. This might be removed in future releases
of the test framework.

</aside>

## Writing test specs

A typical test spec is a sequence of:

- Creating a namespace to host in isolation all the test objects
- Creating objects in the management cluster, wait for the corresponding infrastructure to be provisioned.
- Exec operations like e.g. changing the Kubernetes version or `clusterctl move`, wait for the action to complete.
- Delete objects in the management cluster, wait for the corresponding infrastructure to be terminated.

### Creating Namespaces

The [CreateNamespaceAndWatchEvents method] provides a convenient way to create a namespace and setup
watches for capturing namespaces events

### Creating objects

There are two possible approaches for creating objects in the management cluster:

- Create object by object: create the `Cluster` object, then `AwsCluster`, `Machines`, `AwsMachines` etc.
- Apply a `cluster-templates.yaml` file thus creating all the objects this file contains.

The first approaches leverage on the [controller-runtime Client] and gives you full control, but it comes with
some drawbacks as well, because this method does not reflect directly real user workflows, and most importantly,
the resulting tests are not as reusable with other infrastructure providers. (See [writing portable tests](#writing-portable-e2e-tests)).

We recommend using the [ClusterTemplate method] and the [Apply method] for creating objects in the cluster.
This methods mimics the recommended user workflows, and it is based on `cluster-templates.yaml` files that can be
provided via the [E2E config file], and thus easily swappable when changing the target infrastructure provider.

<aside class="note">

<h1>Tips</h1>

If you need control over object creation but you want to preserve portability, you can create many templates
files each one creating only a small set of objects (instead of using a single template creating a full cluster).

</aside>

After creating objects in the cluster, use the existing methods in the [Cluster API test framework] to discover
which object was created in the cluster so your code can adapt to different `cluster-templates.yaml` files.

Once you have objects references, the framework includes methods for waiting for the corresponding
infrastructure to be provisioned, e.g. [WaitForClusterToProvision], [WaitForKubeadmControlPlaneMachinesToExist].

### Exec operations

You can use [Cluster API test framework] methods to modify Cluster API objects, as a last option, use
the [controller-runtime Client].

The [Cluster API test framework] includes also methods for executing clusterctl operations, like e.g.
the [ClusterTemplate method], the [ClusterctlMove method] etc.; in order to improve observability,
each clusterctl operation creates a detailed log.

After using clusterctl operations, you can rely on the `Get` and on the `Wait` methods
defined in the [Cluster API test framework] to check if the operation completed successfully.

## Tear down

After a test completes/fails, it is required to:

- Collect all the logs for the Cluster API controllers
- Dump all the relevant Cluster API/Kubernetes objects
- Cleanup all the infrastructure resources created during the test

Those task are usually implemented in the `AfterSuite`, and again the [Cluster API test framework] provides
you useful methods for those tasks.

Please note that despite the fact that test specs are expected to delete objects in the management cluster and
wait for the corresponding infrastructure to be terminated, it can happen that the test spec
fails before starting object deletion or that objects deletion itself fails.

As a consequence, when scheduling/running a test suite, it is required to ensure all the generated
resources are cleaned up. In Kubernetes, this is implemented by the [boskos] project.

## Writing portable E2E tests

A portable E2E test is a test can run with different infrastructure providers by simply
changing the test configuration file.

Following recommendations should be followed to write portable E2E tests:

- Create different [E2E config file], one for each target infrastructure provider,
providing different sets of env variables and timeout intervals.
- Use the [InitManagementCluster method] for setting up the management cluster.
- Use the [ClusterTemplate method] and the [Apply method]
for creating objects in the cluster using `cluster-templates.yaml` files instead
of hard coding object creation.
- Use the `Get` methods defined in the [Cluster API test framework] to checks object
being created, so your code can adapt to different `cluster-templates.yaml` files.
- Never hard code the infrastructure provider name in your test spec.
Instead, use the [InfrastructureProvider method] to get access to the
name of the infrastructure provider defined in the [E2E config file].
- Never hard code wait intervals in your test spec.
Instead use the [GetIntervals method] to get access to the
intervals defined in the [E2E config file].

## Cluster API conformance tests

As of today there is no a well-defined suites of E2E tests that can be used as a
baseline for Cluster API conformance.

However, creating such suite is something that can provide a huge value for the
long term success of the project.

The [test E2E package] provide examples of how this can be achieved implemeting a set of and reusable
test specs for the most common Cluster API use cases.

<!-- links -->
[Cluster API quick start]: https://cluster-api.sigs.k8s.io/user/quick-start.html
[Cluster API test framework]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc
[deprecated E2E config file]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#Config
[deprecated InitManagementCluster method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#InitManagementCluster
[Apply method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#Applier
[CAPA E2E tests]: https://github.com/kubernetes-sigs/cluster-api-provider-aws/blob/master/scripts/ci-e2e.sh
[CAPG E2E tests]: https://github.com/kubernetes-sigs/cluster-api-provider-gcp/blob/master/scripts/ci-e2e.sh
[WaitForClusterToProvision]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#WaitForClusterToProvision
[WaitForKubeadmControlPlaneMachinesToExist]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#WaitForKubeadmControlPlaneMachinesToExist
[controller-runtime Client]: https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/client?tab=doc#Client
[boskos]: https://github.com/kubernetes/test-infra/tree/master/boskos
[E2E config file]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#E2EConfig
[example E2E config file]: https://github.com/kubernetes-sigs/cluster-api/blob/master/test/e2e/config/docker-dev.yaml
[NewKindClusterProvider]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/bootstrap?tab=doc#NewKindClusterProvider
[InitManagementClusterAndWatchControllerLogs method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#InitManagementClusterAndWatchControllerLogs
[ClusterTemplate method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#ConfigCluster
[ClusterctlMove method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#Move
[InfrastructureProvider method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#E2EConfig.InfrastructureProviders
[GetIntervals method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework/clusterctl?tab=doc#E2EConfig.GetIntervals
[test E2E package]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/e2e?tab=doc
[CreateNamespaceAndWatchEvents method]: https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework?tab=doc#CreateNamespaceAndWatchEvents
38 changes: 1 addition & 37 deletions test/framework/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,4 @@

This framework aims to define common end-to-end patterns that can be reused across Cluster API providers.

## Usage

Use this framework as you see fit. If there are pieces that don't work for you, do not use them. If there are pieces
that almost work for you, customize them. If this does not fit your e2e testing please file an issue and we can discuss
your use case and find a nice solution for everyone.

### Features

#### Optionally override the images that get loaded onto the management cluster.

This feature allows you to obtain a CAPI management image locally and use that specific image in your kind cluster.
If you do not have one locally then the latest :master image will be used on the management cluster.

### Contents

This framework contains

* A [Go interface][mgmt] to a management cluster
* A [struct that implements][impl] the management cluster interface using `kind` as the backend.
* A series of behavioral tests

[mgmt]: ./interfaces.go
[impl]: ./management/kind/mgmt.go

## Requirements

### Code

* You must use [ginkgo][ginkgo] for your testing framework.

[ginkgo]: https://onsi.github.io/ginkgo/

## Examples

To see this framework in use please take a look at the [docker provider found in the cluster-api repository][capd].

[capd]: https://github.com/kubernetes-sigs/cluster-api/tree/master/test/infrastructure/docker
See https://cluster-api.sigs.k8s.io/developer/e2e.html for more information.

0 comments on commit beff1e1

Please sign in to comment.