Upgrade tests verify that the provider can be successfully upgraded from one version to another while maintaining resource health. The test framework works as follows:
- Creates a Kind cluster with Crossplane installed
- Installs the provider at the "from" version
- Applies test resources
- Verifies resources are healthy before upgrade (can be disabled)
- (optionally) Runs any pre-upgrade assessments
- Upgrades the provider to the "to" version
- Verifies resources remain healthy after upgrade (can be disabled)
- (optionally) Runs any post-upgrade assessments
- Cleans up resources and the provider
While the base upgrade test (base_upgrade_test.go) only verifies resource health by checking whether the resources are synced and ready, custom upgrade tests may be added, e.g. to verify external name upgradability.
The main target for running upgrade tests.
# Required environment variables
export UPGRADE_TEST_FROM_TAG=v1.4.0
export UPGRADE_TEST_TO_TAG=v1.5.0
# Run all upgrade tests
make upgrade-test
# Run a specific test by name (e.g. only baseline tests)
make upgrade-test testFilter=Test_BaselineUpgradeProviderRun upgrade tests with Delve debugger attached for debugging.
export UPGRADE_TEST_FROM_TAG=v1.4.0
export UPGRADE_TEST_TO_TAG=v1.5.0
make upgrade-test-debug testFilter=Test_BaselineUpgradeProviderThe debugger listens on port :2345.
| Variable | Required | Default | Description |
|---|---|---|---|
UPGRADE_TEST_FROM_TAG |
Yes | - | Source provider version (e.g., v1.4.0 or local) |
UPGRADE_TEST_TO_TAG |
Yes | - | Target provider version (e.g., v1.5.0 or local) |
UPGRADE_TEST_CRS_TAG |
No | $UPGRADE_TEST_FROM_TAG |
Git tag to pull baseline CRs from |
UPGRADE_TEST_VERIFY_TIMEOUT |
No | 30 |
Timeout in minutes for resource verification |
UPGRADE_TEST_WAIT_FOR_PAUSE |
No | 1 |
Minutes to wait for resources to pause during upgrade |
UPGRADE_TEST_FROM_PROVIDER_REPOSITORY |
No | ghcr.io/sap/crossplane-provider-btp/crossplane/provider-btp |
Source provider image repository |
UPGRADE_TEST_TO_PROVIDER_REPOSITORY |
No | ghcr.io/sap/crossplane-provider-btp/crossplane/provider-btp |
Target provider image repository |
UPGRADE_TEST_FROM_CONTROLLER_REPOSITORY |
No | ghcr.io/sap/crossplane-provider-btp/crossplane/provider-btp-controller |
Source controller image repository |
UPGRADE_TEST_TO_CONTROLLER_REPOSITORY |
No | ghcr.io/sap/crossplane-provider-btp/crossplane/provider-btp-controller |
Target controller image repository |
Set UPGRADE_TEST_FROM_TAG=local or UPGRADE_TEST_TO_TAG=local to use locally built provider images:
# Test upgrading FROM local build TO a released version
export UPGRADE_TEST_FROM_TAG=local
export UPGRADE_TEST_TO_TAG=v1.5.0
make upgrade-test
# Test upgrading FROM a released version TO local build
export UPGRADE_TEST_FROM_TAG=v1.4.0
export UPGRADE_TEST_TO_TAG=local
make upgrade-testWhen using local, the Makefile automatically builds the provider images before running tests.
Cleanup that:
- Restores test CRs to their original state
- Removes upgrade test log files
- Deletes Kind clusters created by upgrade tests
- Cleans up BTP artifacts
make upgrade-test-cleanRestores only the test/upgrade/testdata/baseCRs directory to the git state:
make upgrade-test-restore-crsPulls baseline CRs from a specific git tag:
export UPGRADE_TEST_CRS_TAG=v1.4.0
make pull-upgrade-test-version-crsGenerates test CRs by substituting environment variables in YAML files:
make generate-upgrade-test-crsBuilds local provider images when local tag is used:
make build-upgrade-test-imagestest/upgrade/testdata/
├── baseCRs/ # Baseline resources for standard upgrade tests
│ └── subaccount/
│ ├── directory.yaml
│ └── subaccount.yaml
└── customCRs/ # Resources for custom upgrade tests
└── subaccountExternalName/
└── subaccount.yaml
Resources in baseCRs/ are used by the baseline upgrade test (Test_BaselineUpgradeProvider). These resources are:
- Automatically discovered from subdirectories
- Applied before upgrade
- Verified before and after upgrade
- Cleaned up after the test
Resources in customCRs/ are used by custom upgrade tests that need specific resources or configurations.
The CustomUpgradeTestBuilder framework provides an API for creating custom upgrade tests with minimal boilerplate.
//go:build upgrade
package upgrade
import (
"testing"
)
func Test_MyCustomUpgrade(t *testing.T) {
upgradeTest := NewCustomUpgradeTest("my-custom-test").
FromVersion("v1.4.0").
ToVersion("v1.5.0").
WithResourceDirectories([]string{
"./testdata/customCRs/myResources",
})
testenv.Test(t, upgradeTest.Feature())
}// Set verification timeout (default: 30 minutes from env or global default)
builder.WithVerifyTimeout(45 * time.Minute)
// Set pause wait duration (default: 1 minute from env or global default)
builder.WithWaitForPause(2 * time.Minute)Add custom verification logic before or after the upgrade:
builder.WithCustomPreUpgradeAssessment(
"Verify state before upgrade",
func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
// Custom verification logic
return ctx
},
)
builder.WithCustomPostUpgradeAssessment(
"Verify state after upgrade",
func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
// Custom verification logic
return ctx
},
)If you want complete control over verification:
builder.SkipDefaultResourceVerification()When upgradeTest.Feature() is called, the test executes in this order:
-
Setup Phase
- Install provider at
fromVersion - Apply ProviderConfig
- Import resources from specified directories
- Install provider at
-
Pre-Upgrade Assessment
- Verify resources are synced and ready (unless skipped)
- Execute custom pre-upgrade assessments
-
Upgrade Phase
- Pause all managed resources
- Upgrade provider to
toVersion - Resume all managed resources
-
Post-Upgrade Assessment
- Verify resources are synced and ready (unless skipped)
- Execute custom post-upgrade assessments
-
Teardown Phase
- Delete test resources gracefully
- Delete ProviderConfig
- Delete provider
NewCustomUpgradeTest("subaccount-external-name-migration-test")When comparing pre/post upgrade states, use the context to pass data:
// Pre-upgrade: store value
return context.WithValue(ctx, "myKey", value)
// Post-upgrade: retrieve value
storedValue := ctx.Value("myKey").(string)if err != nil {
t.Fatalf("Failed to get resource: %v", err)
}import "k8s.io/klog/v2"
klog.V(4).Infof("Pre-upgrade value: %s", value)Place custom test resources in testdata/customCRs/<test-name>/ to avoid conflicts with other tests.
The framework handles cleanup automatically, but ensure your custom assessments don't leave orphaned resources.
Increase the verification timeout:
export UPGRADE_TEST_VERIFY_TIMEOUT=60 # 60 minutes
make upgrade-testOr in code:
builder.WithVerifyTimeout(60 * time.Minute)Ensure the resource directories exist and contain valid YAML files:
ls -la test/upgrade/testdata/customCRs/myResources/Clean up orphaned clusters:
make upgrade-test-cleanTest output is written to upgrade-test-output.log. Kind cluster logs are exported to test/upgrade/logs/post-tests/ after test completion.
# View test output
cat upgrade-test-output.log
# View Kind cluster logs
ls test/upgrade/logs/post-tests/