From f1ff53ede9a1942a5cd53c6802b7f58f9dd00ea4 Mon Sep 17 00:00:00 2001 From: Chun-Hung Tseng Date: Tue, 21 Jan 2025 15:09:45 +0000 Subject: [PATCH] Add downgrade cancellation e2e tests Signed-off-by: Chun-Hung Tseng --- etcdctl/README.md | 2 +- tests/e2e/cluster_downgrade_test.go | 60 ++++++++++++++++++++++++++--- tests/framework/e2e/downgrade.go | 20 ++++++++++ tests/framework/e2e/etcdctl.go | 5 +++ 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/etcdctl/README.md b/etcdctl/README.md index 40e93c4d1c0..3589f4d699d 100644 --- a/etcdctl/README.md +++ b/etcdctl/README.md @@ -1119,7 +1119,7 @@ DOWNGRADE ENABLE starts a downgrade action to cluster. Downgrade enable success, cluster version 3.6 ``` -### DOWNGRADE CANCEL \ +### DOWNGRADE CANCEL DOWNGRADE CANCEL cancels the ongoing downgrade action to cluster. diff --git a/tests/e2e/cluster_downgrade_test.go b/tests/e2e/cluster_downgrade_test.go index 086b80894d5..c6edb8aac1f 100644 --- a/tests/e2e/cluster_downgrade_test.go +++ b/tests/e2e/cluster_downgrade_test.go @@ -37,23 +37,47 @@ import ( "go.etcd.io/etcd/tests/v3/framework/e2e" ) +type CancellationState int + +const ( + NoCancellation CancellationState = iota + CancelRightBeforeEnable + CancelRightAfterEnable +) + func TestDowngradeUpgradeClusterOf1(t *testing.T) { - testDowngradeUpgrade(t, 1, false) + testDowngradeUpgrade(t, 1, false, NoCancellation) } func TestDowngradeUpgradeClusterOf3(t *testing.T) { - testDowngradeUpgrade(t, 3, false) + testDowngradeUpgrade(t, 3, false, NoCancellation) } func TestDowngradeUpgradeClusterOf1WithSnapshot(t *testing.T) { - testDowngradeUpgrade(t, 1, true) + testDowngradeUpgrade(t, 1, true, NoCancellation) } func TestDowngradeUpgradeClusterOf3WithSnapshot(t *testing.T) { - testDowngradeUpgrade(t, 3, true) + testDowngradeUpgrade(t, 3, true, NoCancellation) +} + +func TestDowngradeCancellationWithoutEnablingClusterOf1(t *testing.T) { + testDowngradeUpgrade(t, 1, false, CancelRightBeforeEnable) +} + +func TestDowngradeCancellationRightAfterEnablingClusterOf1(t *testing.T) { + testDowngradeUpgrade(t, 1, false, CancelRightAfterEnable) +} + +func TestDowngradeCancellationWithoutEnablingClusterOf3(t *testing.T) { + testDowngradeUpgrade(t, 3, false, CancelRightBeforeEnable) +} + +func TestDowngradeCancellationRightAfterEnablingClusterOf3(t *testing.T) { + testDowngradeUpgrade(t, 3, false, CancelRightAfterEnable) } -func testDowngradeUpgrade(t *testing.T, clusterSize int, triggerSnapshot bool) { +func testDowngradeUpgrade(t *testing.T, clusterSize int, triggerSnapshot bool, triggerCancellation CancellationState) { currentEtcdBinary := e2e.BinPath.Etcd lastReleaseBinary := e2e.BinPath.EtcdLastRelease if !fileutil.Exist(lastReleaseBinary) { @@ -107,7 +131,18 @@ func testDowngradeUpgrade(t *testing.T, clusterSize int, triggerSnapshot bool) { require.NoError(t, err) beforeMembers, beforeKV := getMembersAndKeys(t, cc) + if triggerCancellation == CancelRightBeforeEnable { + t.Logf("Cancelling downgrade before enabling") + e2e.DowngradeCancel(t, epc, generateIdenticalVersions(clusterSize, currentVersionStr)) + return // No need to perform downgrading, end the test here + } e2e.DowngradeEnable(t, epc, lastVersion) + if triggerCancellation == CancelRightAfterEnable { + t.Logf("Cancelling downgrade right after enabling (no node is downgraded yet)") + e2e.DowngradeCancel(t, epc, generateIdenticalVersions(clusterSize, currentVersionStr)) + return // No need to perform downgrading, end the test here + } + t.Logf("Starting downgrade process to %q", lastVersionStr) e2e.DowngradeUpgradeMembers(t, nil, epc, len(epc.Procs), currentVersion, lastClusterVersion) e2e.AssertProcessLogs(t, leader(t, epc), "the cluster has been downgraded") @@ -233,3 +268,18 @@ func getMembersAndKeys(t *testing.T, cc *e2e.EtcdctlV3) (*clientv3.MemberListRes return members, kvs } + +func generateIdenticalVersions(clusterSize int, currentVersion string) []*version.Versions { + ret := make([]*version.Versions, clusterSize) + + for i := range clusterSize { + ret[i] = &version.Versions{ + Cluster: currentVersion, + Server: currentVersion, + Storage: currentVersion, + } + + } + + return ret +} diff --git a/tests/framework/e2e/downgrade.go b/tests/framework/e2e/downgrade.go index 60a45adedae..3f771f1bcc1 100644 --- a/tests/framework/e2e/downgrade.go +++ b/tests/framework/e2e/downgrade.go @@ -53,6 +53,26 @@ func DowngradeEnable(t *testing.T, epc *EtcdProcessCluster, ver *semver.Version) t.Log("Cluster is ready for downgrade") } +func DowngradeCancel(t *testing.T, epc *EtcdProcessCluster, versions []*version.Versions) { + t.Logf("etcdctl downgrade cancel") + c := epc.Etcdctl() + testutils.ExecuteWithTimeout(t, 20*time.Second, func() { + err := c.DowngradeCancel(context.TODO()) + require.NoError(t, err) + }) + + t.Log("Downgrade cancelled, validating if cluster is in the right state") + for i := 0; i < len(epc.Procs); i++ { + ValidateVersion(t, epc.Cfg, epc.Procs[i], version.Versions{ + Cluster: versions[i].Cluster, + Server: versions[i].Server, + Storage: versions[i].Storage, + }) + } + + t.Log("Cluster downgrade cancellation is completed") +} + func DowngradeUpgradeMembers(t *testing.T, lg *zap.Logger, clus *EtcdProcessCluster, numberOfMembersToChange int, currentVersion, targetVersion *semver.Version) error { if lg == nil { lg = clus.lg diff --git a/tests/framework/e2e/etcdctl.go b/tests/framework/e2e/etcdctl.go index d0c8dc14c72..9235ab28d85 100644 --- a/tests/framework/e2e/etcdctl.go +++ b/tests/framework/e2e/etcdctl.go @@ -86,6 +86,11 @@ func (ctl *EtcdctlV3) DowngradeEnable(ctx context.Context, version string) error return err } +func (ctl *EtcdctlV3) DowngradeCancel(ctx context.Context) error { + _, err := SpawnWithExpectLines(ctx, ctl.cmdArgs("downgrade", "cancel"), nil, expect.ExpectedResponse{Value: "Downgrade cancel success"}) + return err +} + func (ctl *EtcdctlV3) Get(ctx context.Context, key string, o config.GetOptions) (*clientv3.GetResponse, error) { resp := clientv3.GetResponse{} var args []string