This is a design document describing how tests which use multiple versions of Ganeti can be introduced into the current build infrastructure.
The testing of Ganeti is currently done by using two different approaches - unit tests and QA. While the former are useful for ensuring that the individual parts of the system work as expected, most errors are discovered only when all the components of Ganeti interact during QA.
However useful otherwise, until now the QA has failed to provide support for testing upgrades and version compatibility as it was limited to using only one version of Ganeti. While these can be tested for every release manually, a systematic approach is preferred and none can exist with this restriction in place. To lift it, the buildbot scripts and QA utilities must be extended to allow a way of specifying and using diverse multi-version checks.
Required use cases¶
There are two classes of multi-version tests that are interesting in Ganeti, and this chapter provides an example from each to highlight what should be accounted for in the design.
One interface Ganeti exposes to clients interested in interacting with it is the RAPI. Its stability has always been a design principle followed during implementation, but whether it held true in practice was not asserted through tests.
An automatic test of RAPI compatibility would have to take a diverse set of RAPI requests and perform them on two clusters of different versions, one of which would be the reference version. If the clusters had been identically configured, all of the commands successfully executed on the reference version should succeed on the newer version as well.
To achieve this, two versions of Ganeti can be run separately on a cleanly setup cluster. With no guarantee that the versions can coexist, the deployment of these has to be separate. A proxy placed between the client and Ganeti records all the requests and responses. Using this data, a testing utility can decide if the newer version is compatible or not, and provide additional information to assist with debugging.
Upgrade / downgrade tests¶
An upgrade / downgrade test serves to examine whether the state of the cluster is unchanged after its configuration has been upgraded or downgraded to another version of Ganeti.
The test works with two consecutive versions of Ganeti, both installed on the same machine. It examines whether the configuration data and instances survive the downgrade and upgrade procedures. This is done by creating a cluster with the newer version, downgrading it to the older one, and upgrading it to the newer one again. After every step, the integrity of the cluster is checked by running various operations and ensuring everything still works.
Design and implementation¶
Although the previous examples have not been selected to show use cases as diverse as possible, they still show a number of dissimilarities:
Parallel installation vs sequential deployments
Comparing with reference version vs comparing consecutive versions
Examining result dumps vs trying a sequence of operations
With the first two real use cases demonstrating such diversity, it does not make sense to design multi-version test classes. Instead, the programmability of buildbot’s configuration files can be leveraged to implement each test as a separate builder with a custom sequence of steps. The individual steps such as checking out a given or previous version, or installing and removing Ganeti, will be provided as utility functions for any test writer to use.
An upgrade / downgrade test is a part of the QA suite as of commit aa104b5e. The test and the corresponding buildbot changes are a very good first step, both by showing that multi-version tests can be done, and by providing utilities needed for builds of multiple branches. Previously, the same folder was used as the base directory of any build, and now a directory structure more accommodating to multiple builds is in place.
The builder running the test has one flaw - regardless of the branch submitted, it compares versions 2.10 and 2.11 (current master). This behaviour is different from any of the other builders, which may restrict the branches a test can be performed on, but do not differentiate between them otherwise. While additional builders for different versions pairs may be added, this is not a good long-term solution.
The test can be improved by making it compare the current and the previous version. As the buildbot has no notion of what a previous version is, additional utilities to handle this logic will have to be introduced.
The upgrade / downgrade test should be generalized to work for any version which can be downgraded from and upgraded to automatically, meaning versions from 2.11 onwards. This will be made challenging by the fact that the previous version has to be checked out by reading the version of the currently checked out code, identifying the previous version, and then making yet another checkout.
The major and minor version can be read from a Ganeti repository in multiple ways. The two are present as constants defined in source files, but due to refactorings shifting constants from the Python to the Haskell side, their position varies across versions. A more reliable way of fetching them is by examining the news file, as it obeys strict formatting restrictions.
With the version found, a script that acts as a previous version lookup table can be invoked. This script can be constructed dynamically upon buildbot startup, and specified as a build step. The checkout following it proceeds as expected.
The RAPI compatibility test should be added as a separate builder afterwards. As the test requires additional comparison and proxy logic to be used, it will be enabled only on 2.11 onwards, comparing the versions to 2.6 - the reference version for the RAPI. Details on the design of this test will be added in a separate document.
While there are many advantages to having a single builder representing a multi-version test, working on every branch, there is at least one disadvantage: the need to define a base or reference version, which is the only version that can be used to trigger the test, and the only one on which code changes can be tried.
If an error is detected while running a test, and the issue lies with a version other than the one used to invoke the test, the fix would have to make it into the repository before the test could be tried again.
For simple tests, the issue might be mitigated by running them locally. However, the multi-version tests are more likely to be complicated than not, and it could be difficult to reproduce a test by hand.
The situation can be made simpler by requiring that any multi-version test can use only versions lower than the reference version. As errors are more likely to be found in new rather than old code, this would at least reduce the number of troublesome cases.