|
| 1 | +# Promoting an API |
| 2 | + |
| 3 | +This guide describes the process for promoting an API in Crossplane from one |
| 4 | +version to the next. This process should be handled with care over multiple |
| 5 | +releases, as it has proven fairly easy for Crossplane's API promotions of the |
| 6 | +past to be problematic for both upgrades and downgrades, as demonstrated by |
| 7 | +[#6148], [#5932], and [#4400]. |
| 8 | + |
| 9 | +An explanation of Crossplane's feature lifecycle can be found in the [Feature |
| 10 | +Lifecycle] docs page. |
| 11 | + |
| 12 | +## Core Issue |
| 13 | + |
| 14 | +The root cause we have encountered in past problematic API promotions is that we |
| 15 | +try to drop a particular version from a CRD while there may still be resources |
| 16 | +of that version stored in etcd. Kubernetes does not allow this action because it |
| 17 | +can result in possible data loss. |
| 18 | + |
| 19 | +Note that this can happen on both upgrades as well as downgrades. Let's briefly |
| 20 | +summarize one common downgrade path where we've encountered this issue: |
| 21 | + |
| 22 | +1. A Crossplane API is promoted to Beta and the storage version for the CRD is |
| 23 | + updated to Beta in the same release |
| 24 | +1. User installs this new Crossplane version and creates a Beta resource that is |
| 25 | + stored in etcd as Beta |
| 26 | +1. User downgrades to the previous Crossplane version where the CRD's Beta |
| 27 | + version did not exist |
| 28 | +1. The Crossplane init container tries to update the CRD back to the Alpha |
| 29 | + version, which drops the Beta version |
| 30 | +1. The Kubernetes API server rejects this action because there might still be |
| 31 | + Beta resources stored in etcd according to the CRD's |
| 32 | + [`status.storedVersions`] field |
| 33 | +1. Crossplane init container crashes and the Crossplane pod restarts in a loop |
| 34 | + |
| 35 | +## Guiding Principles |
| 36 | + |
| 37 | +There are a few simple principles we can follow in order to avoid this common |
| 38 | +scenario when promoting Crossplane APIs: |
| 39 | + |
| 40 | +1. Never introduce or drop a CRD version while also bumping the storage version |
| 41 | + in the same release |
| 42 | +1. Always migrate resources to the current storage version during Crossplane |
| 43 | + initialization |
| 44 | + |
| 45 | +Adherence to these two principles should result in never dropping a CRD version |
| 46 | +that still has resources of that version stored in etcd, for both the upgrade |
| 47 | +and downgrade directions. |
| 48 | + |
| 49 | +## Promotion Workflow |
| 50 | + |
| 51 | +The following table outlines the steps to introduce and then promote an API |
| 52 | +safely across multiple versions while maintaining safe upgrades and downgrades: |
| 53 | + |
| 54 | +| Version | Description | Alpha | Beta | Migration | |
| 55 | +|---------|----------------------------|-----------------|-----------------|------------------| |
| 56 | +| v0.1 | introduce new API as alpha | storage, served | not exist | none | |
| 57 | +| v0.2 | promote to beta | storage, served | served | migrate to alpha | |
| 58 | +| v0.3 | bump storage to beta | served | storage, served | migrate to beta | |
| 59 | +| v0.4 | drop alpha | not exist | storage, served | none | |
| 60 | + |
| 61 | +The same process could be applied again when promoting the API from Beta to v1 |
| 62 | +GA. |
| 63 | + |
| 64 | +## Practical Pointers |
| 65 | + |
| 66 | +This section contains some helpful pointers to areas of the codebase involved |
| 67 | +in promoting an API. These are just general direction and not entirely |
| 68 | +prescriptive, because you'll need to make specific decisions for your promotion |
| 69 | +based on the workflow defined above. |
| 70 | + |
| 71 | +1. Define the API types under the `apis` directory, for example |
| 72 | + [`apis/apiextensions/v1beta1`] |
| 73 | +1. Set the storage version `+kubebuilder:storageversion` marker on the correct |
| 74 | + version, e.g., as shown here for [Usage v1alpha1] |
| 75 | +1. Duplicate the API to other versions if needed using [`generate.go`], also |
| 76 | + instructing the [duplicate script] to set the storage version if needed |
| 77 | +1. Run `earthly +generate` to generate the CRDs and check them for sanity in the |
| 78 | + [`cluster/crds`] directory |
| 79 | +1. Include a [migrator] if needed to ensure resources are migrated to the |
| 80 | + current storage version and the CRD status is updated to declare that it only |
| 81 | + has resources stored in etcd of the current storage version |
| 82 | + * **Note** that the version passed into the migrator is the **"old"** |
| 83 | + version you want to migrate **from**, not the target version you want to |
| 84 | + migrate to |
| 85 | + |
| 86 | +<!-- Links --> |
| 87 | +[#6148]: https://github.com/crossplane/crossplane/issues/6148 |
| 88 | +[#5932]: https://github.com/crossplane/crossplane/issues/5932 |
| 89 | +[#4400]: https://github.com/crossplane/crossplane/issues/4400 |
| 90 | +[Feature Lifecycle]: https://docs.crossplane.io/latest/learn/feature-lifecycle/ |
| 91 | +[`status.storedVersions`]: https://github.com/kubernetes/apiextensions-apiserver/blob/v0.32.0/pkg/apis/apiextensions/v1/types.go#L368-L376 |
| 92 | +[`apis/apiextensions/v1beta1`]: https://github.com/crossplane/crossplane/tree/release-1.18/apis/apiextensions/v1beta1 |
| 93 | +[Usage v1alpha1]: https://github.com/crossplane/crossplane/blob/release-1.18/apis/apiextensions/v1alpha1/usage_types.go#L89 |
| 94 | +[`generate.go`]: https://github.com/crossplane/crossplane/blob/release-1.18/apis/generate.go |
| 95 | +[duplicate script]: https://github.com/crossplane/crossplane/blob/release-1.18/hack/duplicate_api_type.sh |
| 96 | +[`cluster/crds`]: https://github.com/crossplane/crossplane/tree/release-1.18/cluster/crds |
| 97 | +[migrator]: https://github.com/crossplane/crossplane/blob/release-1.18/cmd/crossplane/core/init.go#L75-L79 |
0 commit comments