Repository: etcd-io/etcd
File: tests/robustness/failpoint/cluster.go (Go)

Code before:
		DialKeepAliveTime:    10 * time.Second,
		DialKeepAliveTimeout: 100 * time.Millisecond,
	})
	if err != nil {
		return nil, err
	}
	defer cc.Close()

	// Need to wait health interval for cluster to accept changes
	time.Sleep(etcdserver.HealthInterval)
	lg.Info("Enable downgrade")
	err = enableDowngrade(ctx, cc, &targetVersion)
	if err != nil {
		return nil, err
	}
	// Need to wait health interval for cluster to prepare for downgrade
	time.Sleep(etcdserver.HealthInterval)

	for _, memberID := range membersToDowngrade {
		member = clus.Procs[memberID]
		lg.Info("Downgrading member", zap.String("member", member.Config().Name))
		if err = member.Stop(); err != nil {
			return nil, err
		}
		member.Config().ExecPath = e2e.BinPath.EtcdLastRelease
		err = patchArgs(member.Config().Args, "initial-cluster-state", "existing")
		if err != nil {
			return nil, err
		}
		lg.Info("Restarting member", zap.String("member", member.Config().Name))
		err = member.Start(ctx)
		if err != nil {
			return nil, err
		}
		err = verifyVersion(t, clus, member, targetVersion)
	}
	time.Sleep(etcdserver.HealthInterval)
	return nil, err
}

func (f memberDowngrade) Name() string {
	return "MemberDowngrade"
}

func (f memberDowngrade) Available(config e2e.EtcdProcessClusterConfig, member e2e.EtcdProcess, profile traffic.Profile) bool {
	if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
		return false
	}
	v, err := e2e.GetVersionFromBinary(e2e.BinPath.Etcd)
	if err != nil {
		panic("Failed checking etcd version binary")

Review comment (suggestion):
It isn't necessary to patch the flag `--initial-cluster-state`, because the data-dir and wal-dir have already been created & initialised. It won't do any harm to do it, but suggest to remove it for simplicity. 

Code after:
		DialKeepAliveTime:    10 * time.Second,
		DialKeepAliveTimeout: 100 * time.Millisecond,
	})
	if err != nil {
		return nil, err
	}
	defer cc.Close()

	// Need to wait health interval for cluster to accept changes
	time.Sleep(etcdserver.HealthInterval)
	lg.Info("Enable downgrade")
	err = enableDowngrade(ctx, cc, &targetVersion)
	if err != nil {
		return nil, err
	}
	// Need to wait health interval for cluster to prepare for downgrade
	time.Sleep(etcdserver.HealthInterval)

	for _, memberID := range membersToDowngrade {
		member = clus.Procs[memberID]
		lg.Info("Downgrading member", zap.String("member", member.Config().Name))
		if err = member.Stop(); err != nil {
			return nil, err
		}
		member.Config().ExecPath = e2e.BinPath.EtcdLastRelease
		lg.Info("Restarting member", zap.String("member", member.Config().Name))
		err = member.Start(ctx)
		if err != nil {
			return nil, err
		}
		err = verifyVersion(t, clus, member, targetVersion)
	}
	time.Sleep(etcdserver.HealthInterval)
	return nil, err
}

func (f memberDowngrade) Name() string {
	return "MemberDowngrade"
}

func (f memberDowngrade) Available(config e2e.EtcdProcessClusterConfig, member e2e.EtcdProcess, profile traffic.Profile) bool {
	if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
		return false
	}
	v, err := e2e.GetVersionFromBinary(e2e.BinPath.Etcd)
	if err != nil {
		panic("Failed checking etcd version binary")
	}
	v3_6 := semver.Version{Major: 3, Minor: 6}
	// only current version cluster can be downgraded.
	return v.Compare(v3_6) >= 0 && (config.Version == e2e.CurrentVersion && member.Config().ExecPath == e2e.BinPath.Etcd)