Skip to content

Instantly share code, notes, and snippets.

@jbw976
Last active January 11, 2025 02:47
Show Gist options
  • Save jbw976/276a088a2cd0f3c1e99b5f7cd074124a to your computer and use it in GitHub Desktop.
Save jbw976/276a088a2cd0f3c1e99b5f7cd074124a to your computer and use it in GitHub Desktop.
API promotion upgrade and downgrade testing

This document walks through a full upgrade and downgrade path acrosss multiple versions and API promotions using the usages.apiextensions.crossplane.io API.

The Crossplane and usages versions are summarized below:

Version Description Alpha Beta Migration
v1.18 most recent release, Usage is Alpha storage, served not exist none
v1.19 promote Usage to beta storage, served served migrate to alpha
v1.20 bump storage version to beta served storage, served migrate to beta
v1.21 drop alpha not exist storage, served none

Install v1.18 with alpha Usage and create an alpha Usage resource

kind create cluster
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm install crossplane --namespace crossplane-system --create-namespace crossplane-stable/crossplane --version 1.18.2
kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/refs/heads/release-1.18/test/e2e/manifests/apiextensions/usage/standalone/setup/provider.yaml
kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/refs/heads/release-1.18/test/e2e/manifests/apiextensions/usage/standalone/with-by/usage.yaml
kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/refs/heads/release-1.18/test/e2e/manifests/apiextensions/usage/standalone/with-by/used.yaml
kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/refs/heads/release-1.18/test/e2e/manifests/apiextensions/usage/standalone/with-by/using.yaml

Perform checks, expecting the following:

  • Crossplane pod is healthy/running on v1.18
  • init container log has no errors
  • Usage CRD has status.storedVersions of alpha
  • Usage CRD has alpha served/stored, beta not exist

Upgrade to upcoming v1.19

git checkout jbw976/usage-beta-migrator
export CROSSPLANE_VERSION=v1.19.0
export CROSSPLANE_REPO=jared-local/crossplane
earthly +generate
earthly +build --CROSSPLANE_VERSION=${CROSSPLANE_VERSION} --CROSSPLANE_REPO=${CROSSPLANE_REPO}
kind load docker-image ${CROSSPLANE_REPO}:${CROSSPLANE_VERSION}
helm upgrade crossplane --namespace crossplane-system \
    _output/charts/crossplane-$(echo ${CROSSPLANE_VERSION}|sed -e 's/^v//').tgz \
    --set "image.pullPolicy=Never,image.repository=${CROSSPLANE_REPO},image.tag=${CROSSPLANE_VERSION}" \
    --reset-values

Perform checks, expecting the following:

  • Crossplane pod is healthy/running on v1.19
  • init container log has no errors
  • Usage CRD has status.storedVersions of alpha
  • Usage CRD has alpha served/stored, beta served

Upgrade to v1.20

git checkout jbw976/crossplane-usages-1.20
export CROSSPLANE_VERSION=v1.20.0
export CROSSPLANE_REPO=jared-local/crossplane
earthly +generate
earthly +build --CROSSPLANE_VERSION=${CROSSPLANE_VERSION} --CROSSPLANE_REPO=${CROSSPLANE_REPO}
kind load docker-image ${CROSSPLANE_REPO}:${CROSSPLANE_VERSION}
helm upgrade crossplane --namespace crossplane-system \
    _output/charts/crossplane-$(echo ${CROSSPLANE_VERSION}|sed -e 's/^v//').tgz \
    --set "image.pullPolicy=Never,image.repository=${CROSSPLANE_REPO},image.tag=${CROSSPLANE_VERSION}" \
    --reset-values

Perform checks, expecting the following:

  • Crossplane pod is healthy/running on v1.20
  • init container log has no errors
  • Usage CRD has status.storedVersions of beta
  • Usage CRD has alpha served, beta served/stored

Upgrade to v1.21

git checkout jbw976/crossplane-usages-1.21
export CROSSPLANE_VERSION=v1.21.0
export CROSSPLANE_REPO=jared-local/crossplane
earthly +generate
earthly +build --CROSSPLANE_VERSION=${CROSSPLANE_VERSION} --CROSSPLANE_REPO=${CROSSPLANE_REPO}
kind load docker-image ${CROSSPLANE_REPO}:${CROSSPLANE_VERSION}
helm upgrade crossplane --namespace crossplane-system \
    _output/charts/crossplane-$(echo ${CROSSPLANE_VERSION}|sed -e 's/^v//').tgz \
    --set "image.pullPolicy=Never,image.repository=${CROSSPLANE_REPO},image.tag=${CROSSPLANE_VERSION}" \
    --reset-values

Perform checks, expecting the following:

  • Crossplane pod is healthy/running on v1.21
  • init container log has no errors
  • Usage CRD has status.storedVersions of beta
  • Usage CRD has alpha not exist, beta served/stored

Downgrade to v1.20

Perform the same build, installation, and verification checks as in the Install v1.20 section.

Downgrade to v1.19

Perform the same build, installation, and verification checks as in the Install v1.19 section.

Downgrade v1.18

helm upgrade crossplane --namespace crossplane-system crossplane-stable/crossplane --version 1.18.2 --reset-values

Then perform the verification checks as in the Install v1.18 section.

The following checks can be run for any version to ensure that the Crossplane pod and initilization is OK, and that Usage resources and CRD are at their expected versions.

kubectl -n crossplane-system get pod -l app=crossplane
kubectl -n crossplane-system get pod -o json | jq '.items[].spec.containers[0].image'
kubectl -n crossplane-system logs -l app=crossplane -c crossplane-init
kubectl get crd usages.apiextensions.crossplane.io -o json | jq .status.storedVersions
kubectl get crd usages.apiextensions.crossplane.io -o json | jq '.spec.versions[] | {name: .name, served: .served, storage: .storage}'

v1.18

❯ kubectl -n crossplane-system get pod -l app=crossplane
NAME                          READY   STATUS    RESTARTS   AGE
crossplane-5cdc8c8bc7-s92jm   1/1     Running   0          33s

❯ kubectl -n crossplane-system get pod -o json | jq '.items[].spec.containers[0].image'
"xpkg.upbound.io/crossplane/crossplane:v1.18.2"
"xpkg.upbound.io/crossplane/crossplane:v1.18.2"
"xpkg.upbound.io/crossplane-contrib/provider-nop:v0.3.0"

❯ kubectl -n crossplane-system logs -l app=crossplane -c crossplane-init
{"level":"info","ts":"2025-01-10T16:48:35Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-10T16:48:35Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-10T16:48:35Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-10T16:48:36Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDs"}
{"level":"info","ts":"2025-01-10T16:48:36Z","logger":"crossplane","msg":"Step has been completed","Name":"WebhookConfigurations"}
{"level":"info","ts":"2025-01-10T16:48:38Z","logger":"crossplane","msg":"Step has been completed","Name":"LockObject"}
{"level":"info","ts":"2025-01-10T16:48:38Z","logger":"crossplane","msg":"Step has been completed","Name":"PackageInstaller"}
{"level":"info","ts":"2025-01-10T16:48:38Z","logger":"crossplane","msg":"Step has been completed","Name":"StoreConfigObject"}
{"level":"info","ts":"2025-01-10T16:48:38Z","logger":"crossplane","msg":"Step has been completed","Name":"StepFunc"}
{"level":"info","ts":"2025-01-10T16:48:38Z","logger":"crossplane","msg":"Initialization has been completed"}

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq .status.storedVersions
[
  "v1alpha1"
]

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq '.spec.versions[] | {name: .name, served: .served, storage: .storage}'
{
  "name": "v1alpha1",
  "served": true,
  "storage": true
}

v1.19

❯ kubectl -n crossplane-system get pod -l app=crossplane
NAME                          READY   STATUS    RESTARTS   AGE
crossplane-5669f6458f-vvlhk   1/1     Running   0          68s

❯ kubectl -n crossplane-system get pod -o json | jq '.items[].spec.containers[0].image'
"jared-local/crossplane:v1.19.0"
"jared-local/crossplane:v1.19.0"
"xpkg.upbound.io/crossplane-contrib/provider-nop:v0.3.0"

❯ kubectl -n crossplane-system logs -l app=crossplane -c crossplane-init
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Step has been completed","Name":"LockObject"}
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Step has been completed","Name":"PackageInstaller"}
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Step has been completed","Name":"StoreConfigObject"}
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Step has been completed","Name":"StepFunc"}
{"level":"info","ts":"2025-01-11T01:42:14Z","logger":"crossplane","msg":"Initialization has been completed"}

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq .status.storedVersions
[
  "v1alpha1"
]

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq '.spec.versions[] | {name: .name, served: .served, storage: .storage}'
{
  "name": "v1alpha1",
  "served": true,
  "storage": true
}
{
  "name": "v1beta1",
  "served": true,
  "storage": false
}

v1.20

❯ kubectl -n crossplane-system get pod -l app=crossplane
NAME                          READY   STATUS    RESTARTS   AGE
crossplane-6658bb4dfc-rxq4c   1/1     Running   0          103s

❯ kubectl -n crossplane-system get pod -o json | jq '.items[].spec.containers[0].image'
"jared-local/crossplane:v1.20.0"
"jared-local/crossplane:v1.20.0"
"xpkg.upbound.io/crossplane-contrib/provider-nop:v0.3.0"

❯ kubectl -n crossplane-system logs -l app=crossplane -c crossplane-init
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Step has been completed","Name":"LockObject"}
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Step has been completed","Name":"PackageInstaller"}
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Step has been completed","Name":"StoreConfigObject"}
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Step has been completed","Name":"StepFunc"}
{"level":"info","ts":"2025-01-11T02:02:11Z","logger":"crossplane","msg":"Initialization has been completed"}

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq .status.storedVersions
[
  "v1beta1"
]

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq '.spec.versions[] | {name: .name, served: .served, storage: .storage}'
{
  "name": "v1alpha1",
  "served": true,
  "storage": false
}
{
  "name": "v1beta1",
  "served": true,
  "storage": true
}

v1.21

❯ kubectl -n crossplane-system get pod -l app=crossplane
NAME                          READY   STATUS    RESTARTS   AGE
crossplane-5d8fd97797-bz7zm   1/1     Running   0          59s

❯ kubectl -n crossplane-system get pod -o json | jq '.items[].spec.containers[0].image'
"jared-local/crossplane:v1.21.0"
"jared-local/crossplane:v1.21.0"
"xpkg.upbound.io/crossplane-contrib/provider-nop:v0.3.0"

❯ kubectl -n crossplane-system logs -l app=crossplane -c crossplane-init
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Step has been completed","Name":"LockObject"}
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Step has been completed","Name":"PackageInstaller"}
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Step has been completed","Name":"StoreConfigObject"}
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Step has been completed","Name":"StepFunc"}
{"level":"info","ts":"2025-01-11T02:07:58Z","logger":"crossplane","msg":"Initialization has been completed"}

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq .status.storedVersions
[
  "v1beta1"
]

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq '.spec.versions[] | {name: .name, served: .served, storage: .storage}'
{
  "name": "v1beta1",
  "served": true,
  "storage": true
}

v1.20 (downgrade)

❯ kubectl -n crossplane-system get pod -l app=crossplane
NAME                          READY   STATUS    RESTARTS   AGE
crossplane-6658bb4dfc-42d9l   1/1     Running   0          34s

❯ kubectl -n crossplane-system get pod -o json | jq '.items[].spec.containers[0].image'
"jared-local/crossplane:v1.20.0"
"jared-local/crossplane:v1.20.0"
"xpkg.upbound.io/crossplane-contrib/provider-nop:v0.3.0"

❯ kubectl -n crossplane-system logs -l app=crossplane -c crossplane-init
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Step has been completed","Name":"LockObject"}
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Step has been completed","Name":"PackageInstaller"}
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Step has been completed","Name":"StoreConfigObject"}
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Step has been completed","Name":"StepFunc"}
{"level":"info","ts":"2025-01-11T02:39:30Z","logger":"crossplane","msg":"Initialization has been completed"}

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq .status.storedVersions
[
  "v1beta1"
]

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq '.spec.versions[] | {name: .name, served: .served, storage: .storage}'
{
  "name": "v1alpha1",
  "served": true,
  "storage": false
}
{
  "name": "v1beta1",
  "served": true,
  "storage": true
}

v1.19 (downgrade)

❯ kubectl -n crossplane-system get pod -l app=crossplane
NAME                          READY   STATUS    RESTARTS   AGE
crossplane-5669f6458f-bt6gn   1/1     Running   0          38s

❯ kubectl -n crossplane-system get pod -o json | jq '.items[].spec.containers[0].image'
"jared-local/crossplane:v1.19.0"
"jared-local/crossplane:v1.19.0"
"xpkg.upbound.io/crossplane-contrib/provider-nop:v0.3.0"

❯ kubectl -n crossplane-system logs -l app=crossplane -c crossplane-init
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Step has been completed","Name":"LockObject"}
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Step has been completed","Name":"PackageInstaller"}
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Step has been completed","Name":"StoreConfigObject"}
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Step has been completed","Name":"StepFunc"}
{"level":"info","ts":"2025-01-11T02:42:42Z","logger":"crossplane","msg":"Initialization has been completed"}

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq .status.storedVersions
[
  "v1alpha1"
]

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq '.spec.versions[] | {name: .name, served: .served, storage: .storage}'
{
  "name": "v1alpha1",
  "served": true,
  "storage": true
}
{
  "name": "v1beta1",
  "served": true,
  "storage": false
}

v1.18 (downgrade)

❯ kubectl -n crossplane-system get pod -l app=crossplane
NAME                          READY   STATUS    RESTARTS   AGE
crossplane-5cdc8c8bc7-pljkc   1/1     Running   0          43s

❯ kubectl -n crossplane-system get pod -o json | jq '.items[].spec.containers[0].image'
"xpkg.upbound.io/crossplane/crossplane:v1.18.2"
"xpkg.upbound.io/crossplane/crossplane:v1.18.2"
"xpkg.upbound.io/crossplane-contrib/provider-nop:v0.3.0"

❯ kubectl -n crossplane-system logs -l app=crossplane -c crossplane-init
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDsMigrator"}
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Step has been completed","Name":"CoreCRDs"}
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Step has been completed","Name":"WebhookConfigurations"}
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Step has been completed","Name":"LockObject"}
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Step has been completed","Name":"PackageInstaller"}
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Step has been completed","Name":"StoreConfigObject"}
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Step has been completed","Name":"StepFunc"}
{"level":"info","ts":"2025-01-11T02:45:08Z","logger":"crossplane","msg":"Initialization has been completed"}

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq .status.storedVersions
[
  "v1alpha1"
]

❯ kubectl get crd usages.apiextensions.crossplane.io -o json | jq '.spec.versions[] | {name: .name, served: .served, storage: .storage}'
{
  "name": "v1alpha1",
  "served": true,
  "storage": true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment