func (c *Cluster) Restart() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if err := c.shutdownWithoutLock(); err != nil {
		return err
	}
	c.createCmdToStartServers()
	return c.startWithoutLock()
}
Restart restarts the cluster by shutting down and starting all servers in the cluster
package setup
import (
	"crypto/tls"
	"os"
	"path"
	"strconv"
	"sync"
	"time"
	"github.com/hyperledger-labs/orion-server/internal/fileops"
	"github.com/hyperledger-labs/orion-server/pkg/crypto"
	"github.com/hyperledger-labs/orion-server/pkg/logger"
	"github.com/hyperledger-labs/orion-server/pkg/server/testutils"
	"github.com/pkg/errors"
)
type Cluster struct {
	Servers        []*Server
	Users          []string
	testDirAbsPath string
	bdbBinaryPath  string
	cmdTimeout     time.Duration
	logger         *logger.SugarLogger
	rootCAPath     string
	rootCAPemCert  []byte
	caPrivKey      []byte
	mu             sync.Mutex
}
type Config struct {
	NumberOfServers     int
	TestDirAbsolutePath string
	BDBBinaryPath       string
	CmdTimeout          time.Duration
}
func NewCluster(conf *Config) (*Cluster, error) {
	if conf.CmdTimeout < 1*time.Second {
		return nil, errors.New("cmd timeout must be at least 1 second")
	}
	exist, err := fileops.Exists(conf.BDBBinaryPath)
	if err != nil {
		return nil, err
	}
	if !exist {
		return nil, errors.New(conf.BDBBinaryPath + " executable does not exist")
	}
	l, err := logger.New(&logger.Config{
		Level:         "debug",
		OutputPath:    []string{"stdout"},
		ErrOutputPath: []string{"stderr"},
		Encoding:      "console",
	})
	if err != nil {
		return nil, err
	}
	cluster := &Cluster{
		Servers:        make([]*Server, conf.NumberOfServers),
		Users:          []string{"admin"},
		logger:         l,
		testDirAbsPath: conf.TestDirAbsolutePath,
		bdbBinaryPath:  conf.BDBBinaryPath,
		cmdTimeout:     conf.CmdTimeout,
		rootCAPath:     path.Join(conf.TestDirAbsolutePath, "ca"),
	}
	if err := cluster.createRootCA(); err != nil {
		return nil, err
	}
	for i := 0; i < conf.NumberOfServers; i++ {
		cluster.Servers[i], err = NewServer(uint64(i), conf.TestDirAbsolutePath, l)
		if err != nil {
			return nil, err
		}
	}
	if err := cluster.createCryptoMaterials(); err != nil {
		return nil, err
	}
	if err := cluster.createConfigFile(); err != nil {
		return nil, err
	}
	if err := cluster.createBootstrapFile(); err != nil {
		return nil, err
	}
	cluster.createCmdToStartServers()
	return cluster, nil
}
func (c *Cluster) createRootCA() (err error) {
	c.rootCAPemCert, c.caPrivKey, err = testutils.GenerateRootCA("BCDB RootCA", "127.0.0.1")
	if err != nil {
		return err
	}
	if err = fileops.CreateDir(c.rootCAPath); err != nil {
		return err
	}
	serverRootCACertPath := path.Join(c.rootCAPath, "rootCA.pem")
	serverRootCACertFile, err := os.Create(serverRootCACertPath)
	if err != nil {
		return err
	}
	if _, err = serverRootCACertFile.Write(c.rootCAPemCert); err != nil {
		return err
	}
	if err = serverRootCACertFile.Close(); err != nil {
		return err
	}
	serverRootCAKeyPath := path.Join(c.rootCAPath, "rootCA.key")
	serverRootCAKeyFile, err := os.Create(serverRootCAKeyPath)
	if err != nil {
		return err
	}
	if _, err = serverRootCAKeyFile.Write(c.caPrivKey); err != nil {
		return err
	}
	if err = serverRootCAKeyFile.Close(); err != nil {
		return err
	}
	return err
}
func (c *Cluster) Start() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.startWithoutLock()
}
func (c *Cluster) startWithoutLock() error {
	err := make(chan error, len(c.Servers))
	var wg sync.WaitGroup
	wg.Add(len(c.Servers))
	for _, s := range c.Servers {
		go func(s *Server, err chan error) {
			c.logger.Debug("Check whether the server " + s.serverID + " has started")
			defer wg.Done()
			if errR := s.start(c.cmdTimeout); err != nil {
				err <- errR
				return
			}
			c.logger.Debug("Successfully started server " + s.serverID)
		}(s, err)
	}
	wg.Wait()
	select {
	case e := <-err:
		return e
	default:
		return nil
	}
}
func (c *Cluster) Shutdown() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.shutdownWithoutLock()
}
func (c *Cluster) shutdownWithoutLock() error {
	err := make(chan error, len(c.Servers))
	var wg sync.WaitGroup
	wg.Add(len(c.Servers))
	for _, s := range c.Servers {
		go func(s *Server, err chan error) {
			defer wg.Done()
			if errR := s.shutdown(); err != nil {
				err <- errR
				return
			}
		}(s, err)
	}
	wg.Wait()
	select {
	case e := <-err:
		return e
	default:
		return nil
	}
}
func (c *Cluster) ShutdownAndCleanup() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if err := c.shutdownWithoutLock(); err != nil {
		return err
	}
	for _, s := range c.Servers {
		if err := os.RemoveAll(s.configDir); err != nil {
			return err
		}
	}
	return nil
}