@@ -16,17 +16,21 @@ package e2e
1616
1717import (
1818 "fmt"
19+ "strings"
1920 "sync"
2021 "testing"
2122 "time"
2223
24+ "github.com/coreos/go-semver/semver"
2325 "github.com/stretchr/testify/assert"
2426 "github.com/stretchr/testify/require"
2527
2628 "go.etcd.io/etcd/api/v3/version"
2729 "go.etcd.io/etcd/client/pkg/v3/fileutil"
2830 "go.etcd.io/etcd/pkg/v3/expect"
31+ "go.etcd.io/etcd/tests/v3/framework/config"
2932 "go.etcd.io/etcd/tests/v3/framework/e2e"
33+ "go.etcd.io/etcd/tests/v3/framework/testutils"
3034)
3135
3236// TestReleaseUpgrade ensures that changes to master branch does not affect
@@ -165,3 +169,102 @@ func TestReleaseUpgradeWithRestart(t *testing.T) {
165169
166170 require .NoError (t , ctlV3Get (cx , []string {kvs [0 ].key }, []kv {kvs [0 ]}... ))
167171}
172+
173+ func TestClusterUpgradeAfterPromotingMembers (t * testing.T ) {
174+ if ! fileutil .Exist (e2e .BinPath .EtcdLastRelease ) {
175+ t .Skipf ("%q does not exist" , e2e .BinPath .EtcdLastRelease )
176+ }
177+
178+ e2e .BeforeTest (t )
179+
180+ currentVersion , err := e2e .GetVersionFromBinary (e2e .BinPath .Etcd )
181+ require .NoError (t , err , "failed to get version from binary" )
182+
183+ lastClusterVersion , err := e2e .GetVersionFromBinary (e2e .BinPath .EtcdLastRelease )
184+ require .NoError (t , err , "failed to get version from last release binary" )
185+
186+ epc := createNewClusterByPromotingMembers (t , e2e .LastVersion )
187+ defer func () {
188+ require .NoError (t , epc .Close ())
189+ }()
190+
191+ err = e2e .DowngradeUpgradeMembers (t , nil , epc , 3 , false , lastClusterVersion , currentVersion )
192+ require .NoError (t , err )
193+ }
194+
195+ func createNewClusterByPromotingMembers (t * testing.T , clusterVersion e2e.ClusterVersion ) * e2e.EtcdProcessCluster {
196+ var version * semver.Version
197+ var err error
198+
199+ switch clusterVersion {
200+ case e2e .CurrentVersion :
201+ version , err = e2e .GetVersionFromBinary (e2e .BinPath .Etcd )
202+ require .NoError (t , err , "failed to get version from binary" )
203+ case e2e .LastVersion :
204+ if ! fileutil .Exist (e2e .BinPath .EtcdLastRelease ) {
205+ t .Skipf ("%q does not exist" , e2e .BinPath .EtcdLastRelease )
206+ }
207+
208+ version , err = e2e .GetVersionFromBinary (e2e .BinPath .EtcdLastRelease )
209+ require .NoError (t , err , "failed to get version from last release binary" )
210+ default :
211+ t .Fatalf ("unexpected cluster version: %v" , clusterVersion )
212+
213+ }
214+
215+ t .Logf ("Creating new etcd cluster - %v" , version )
216+
217+ t .Log ("Creating first node" )
218+ epc , err := e2e .NewEtcdProcessCluster (t .Context (), t ,
219+ e2e .WithVersion (clusterVersion ),
220+ e2e .WithClusterSize (1 ),
221+ e2e .WithSnapshotCount (10 ),
222+ )
223+ require .NoError (t , err , "failed to start first etcd process" )
224+ defer func () {
225+ if t .Failed () {
226+ epc .Close ()
227+ }
228+ }()
229+
230+ for _ , idx := range []string {"second" , "third" } {
231+ var nodeID uint64
232+ var aerr error
233+
234+ // NOTE: New promoted member needs time to get connected.
235+ t .Logf ("Adding %s node as learner" , idx )
236+ testutils .ExecuteWithTimeout (t , 1 * time .Minute , func () {
237+ for {
238+ nodeID , aerr = epc .StartNewProc (t .Context (), nil , t , true )
239+ if aerr != nil {
240+ if strings .Contains (aerr .Error (), "etcdserver: unhealthy cluster" ) {
241+ time .Sleep (1 * time .Second )
242+ continue
243+ }
244+ }
245+ break
246+ }
247+ })
248+ require .NoError (t , aerr )
249+
250+ t .Logf ("Promoting %s node" , idx )
251+ etcdctl := epc .Procs [0 ].Etcdctl ()
252+ _ , err = etcdctl .MemberPromote (t .Context (), nodeID )
253+ require .NoError (t , err )
254+ }
255+
256+ t .Log ("Checking all member status" )
257+ mresp , merr := epc .Etcdctl ().MemberList (t .Context (), true )
258+ require .NoError (t , merr )
259+ require .Len (t , mresp .Members , 3 )
260+ for _ , m := range mresp .Members {
261+ require .Falsef (t , m .IsLearner , "%s should not be learner" , m .Name )
262+ }
263+
264+ t .Logf ("Adding 10 key/value to trigger snapshot" )
265+ for i := 0 ; i < 10 ; i ++ {
266+ err = epc .Etcdctl ().Put (t .Context (), "foo" , "bar" , config.PutOptions {})
267+ require .NoError (t , err )
268+ }
269+ return epc
270+ }
0 commit comments