github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/common/server/etcdraft_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package server_test
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"sync/atomic"
    16  	"testing"
    17  	"time"
    18  
    19  	. "github.com/onsi/gomega"
    20  	"github.com/onsi/gomega/gbytes"
    21  	"github.com/onsi/gomega/gexec"
    22  )
    23  
    24  var basePort = int32(8000)
    25  
    26  func nextPort() int32 {
    27  	return atomic.AddInt32(&basePort, 1)
    28  }
    29  
    30  func TestSpawnEtcdRaft(t *testing.T) {
    31  	gt := NewGomegaWithT(t)
    32  
    33  	// Build the configtxgen binary
    34  	configtxgen, err := gexec.Build("github.com/hechain20/hechain/cmd/configtxgen")
    35  	gt.Expect(err).NotTo(HaveOccurred())
    36  
    37  	cryptogen, err := gexec.Build("github.com/hechain20/hechain/cmd/cryptogen")
    38  	gt.Expect(err).NotTo(HaveOccurred())
    39  
    40  	// Build the orderer binary
    41  	orderer, err := gexec.Build("github.com/hechain20/hechain/cmd/orderer")
    42  	gt.Expect(err).NotTo(HaveOccurred())
    43  
    44  	defer gexec.CleanupBuildArtifacts()
    45  
    46  	tempSharedDir, err := ioutil.TempDir("", "etcdraft-test")
    47  	gt.Expect(err).NotTo(HaveOccurred())
    48  	defer os.RemoveAll(tempSharedDir)
    49  
    50  	copyYamlFiles(gt, "testdata", tempSharedDir)
    51  
    52  	cryptoPath := generateCryptoMaterials(gt, cryptogen, tempSharedDir)
    53  
    54  	t.Run("Bad", func(t *testing.T) {
    55  		t.Run("Invalid bootstrap block", func(t *testing.T) {
    56  			testEtcdRaftOSNFailureInvalidBootstrapBlock(NewGomegaWithT(t), tempSharedDir, orderer, configtxgen, cryptoPath)
    57  		})
    58  
    59  		t.Run("TLS disabled single listener", func(t *testing.T) {
    60  			testEtcdRaftOSNNoTLSSingleListener(NewGomegaWithT(t), tempSharedDir, orderer, configtxgen, cryptoPath)
    61  		})
    62  	})
    63  
    64  	t.Run("Good", func(t *testing.T) {
    65  		// tests in this suite actually launch process with success, hence we need to avoid
    66  		// conflicts in listening port, opening files.
    67  		t.Run("TLS disabled dual listener", func(t *testing.T) {
    68  			testEtcdRaftOSNNoTLSDualListener(NewGomegaWithT(t), tempSharedDir, orderer, configtxgen, cryptoPath)
    69  		})
    70  
    71  		t.Run("TLS enabled single listener", func(t *testing.T) {
    72  			testEtcdRaftOSNSuccess(NewGomegaWithT(t), tempSharedDir, configtxgen, orderer, cryptoPath)
    73  		})
    74  
    75  		t.Run("Restart orderer without Genesis Block", func(t *testing.T) {
    76  			testEtcdRaftOSNRestart(NewGomegaWithT(t), tempSharedDir, configtxgen, orderer, cryptoPath)
    77  		})
    78  
    79  		t.Run("Restart orderer after joining system channel", func(t *testing.T) {
    80  			testEtcdRaftOSNJoinSysChan(NewGomegaWithT(t), tempSharedDir, configtxgen, orderer, cryptoPath)
    81  		})
    82  	})
    83  }
    84  
    85  func copyYamlFiles(gt *GomegaWithT, src, dst string) {
    86  	for _, file := range []string{"configtx.yaml", "examplecom-config.yaml", "orderer.yaml"} {
    87  		fileBytes, err := ioutil.ReadFile(filepath.Join(src, file))
    88  		gt.Expect(err).NotTo(HaveOccurred())
    89  		err = ioutil.WriteFile(filepath.Join(dst, file), fileBytes, 0o644)
    90  		gt.Expect(err).NotTo(HaveOccurred())
    91  	}
    92  }
    93  
    94  func generateBootstrapBlock(gt *GomegaWithT, tempDir, configtxgen, channel, profile string) string {
    95  	// create a genesis block for the specified channel and profile
    96  	genesisBlockPath := filepath.Join(tempDir, "genesis.block")
    97  	cmd := exec.Command(
    98  		configtxgen,
    99  		"-channelID", channel,
   100  		"-profile", profile,
   101  		"-outputBlock", genesisBlockPath,
   102  		"--configPath", tempDir,
   103  	)
   104  	configtxgenProcess, err := gexec.Start(cmd, nil, nil)
   105  	gt.Expect(err).NotTo(HaveOccurred())
   106  	gt.Eventually(configtxgenProcess, time.Minute).Should(gexec.Exit(0))
   107  	gt.Expect(configtxgenProcess.Err).To(gbytes.Say("Writing genesis block"))
   108  
   109  	return genesisBlockPath
   110  }
   111  
   112  func generateCryptoMaterials(gt *GomegaWithT, cryptogen, path string) string {
   113  	cryptoPath := filepath.Join(path, "crypto")
   114  
   115  	cmd := exec.Command(
   116  		cryptogen,
   117  		"generate",
   118  		"--config", filepath.Join(path, "examplecom-config.yaml"),
   119  		"--output", cryptoPath,
   120  	)
   121  	cryptogenProcess, err := gexec.Start(cmd, nil, nil)
   122  	gt.Expect(err).NotTo(HaveOccurred())
   123  	gt.Eventually(cryptogenProcess, time.Minute).Should(gexec.Exit(0))
   124  
   125  	return cryptoPath
   126  }
   127  
   128  func testEtcdRaftOSNRestart(gt *GomegaWithT, tempDir, configtxgen, orderer, cryptoPath string) {
   129  	genesisBlockPath := generateBootstrapBlock(gt, tempDir, configtxgen, "system", "SampleEtcdRaftSystemChannel")
   130  
   131  	// Launch the OSN
   132  	ordererProcess := launchOrderer(gt, orderer, tempDir, tempDir, genesisBlockPath, cryptoPath, "file", "false", "info")
   133  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   134  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   135  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
   136  
   137  	// Restart orderer with ORDERER_GENERAL_BOOTSTRAPMETHOD = none
   138  	gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit())
   139  	ordererProcess = launchOrderer(gt, orderer, tempDir, tempDir, "", cryptoPath, "none", "true", "info")
   140  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   141  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
   142  	gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit())
   143  }
   144  
   145  func testEtcdRaftOSNJoinSysChan(gt *GomegaWithT, configPath, configtxgen, orderer, cryptoPath string) {
   146  	tempDir, err := ioutil.TempDir("", "etcdraft-test")
   147  	gt.Expect(err).NotTo(HaveOccurred())
   148  	defer os.RemoveAll(tempDir)
   149  
   150  	// Launch the OSN without channels
   151  	ordererProcess := launchOrderer(gt, orderer, tempDir, configPath, "", cryptoPath, "none", "true", "info:orderer.common.server=debug")
   152  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   153  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Channel Participation API enabled, registrar initializing with file repo"))
   154  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("No join-block was found for the system channel"))
   155  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   156  
   157  	// emulate a join-block for the system channel written to the join-block filerepo location
   158  	genesisBlockPath := generateBootstrapBlock(gt, configPath, configtxgen, "system", "SampleEtcdRaftSystemChannel")
   159  	genesisBlockBytes, err := ioutil.ReadFile(genesisBlockPath)
   160  	gt.Expect(err).NotTo(HaveOccurred())
   161  	fileRepoDir := filepath.Join(tempDir, "ledger", "pendingops", "join")
   162  	joinBlockPath := filepath.Join(fileRepoDir, "system.join")
   163  	err = ioutil.WriteFile(joinBlockPath, genesisBlockBytes, 0o644)
   164  	gt.Expect(err).NotTo(HaveOccurred())
   165  
   166  	gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit())
   167  
   168  	// Restart, should pick up the join-block and bootstrap with it
   169  	ordererProcess = launchOrderer(gt, orderer, tempDir, configPath, "", cryptoPath, "none", "true", "info")
   170  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Channel Participation API enabled, registrar initializing with file repo"))
   171  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Join-block was found for the system channel: system, number: 0"))
   172  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   173  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
   174  	// File was removed after on-boarding
   175  	_, err = os.Stat(joinBlockPath)
   176  	gt.Expect(err).To(HaveOccurred())
   177  	pathErr := err.(*os.PathError)
   178  	gt.Expect(pathErr.Err.Error()).To(Equal("no such file or directory"))
   179  	gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit())
   180  }
   181  
   182  func testEtcdRaftOSNSuccess(gt *GomegaWithT, configPath, configtxgen, orderer, cryptoPath string) {
   183  	tempDir, err := ioutil.TempDir("", "etcdraft-test")
   184  	gt.Expect(err).NotTo(HaveOccurred())
   185  	defer os.RemoveAll(tempDir)
   186  
   187  	genesisBlockPath := generateBootstrapBlock(gt, configPath, configtxgen, "system", "SampleEtcdRaftSystemChannel")
   188  
   189  	// Launch the OSN
   190  	ordererProcess := launchOrderer(gt, orderer, tempDir, configPath, genesisBlockPath, cryptoPath, "file", "false", "info")
   191  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   192  	// The following configuration parameters are not specified in the orderer.yaml, so let's ensure
   193  	// they are really configured autonomously via the localconfig code.
   194  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.DialTimeout = 5s"))
   195  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.RPCTimeout = 7s"))
   196  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationBufferSize = 20971520"))
   197  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationPullTimeout = 5s"))
   198  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationRetryTimeout = 5s"))
   199  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationBackgroundRefreshInterval = 5m0s"))
   200  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationMaxRetries = 12"))
   201  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.SendBufferSize = 10"))
   202  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.CertExpirationWarningThreshold = 168h0m0s"))
   203  
   204  	// Consensus.EvictionSuspicion is not specified in orderer.yaml, so let's ensure
   205  	// it is really configured autonomously via the etcdraft chain itself.
   206  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("EvictionSuspicion not set, defaulting to 10m"))
   207  	// Wait until the the node starts up and elects itself as a single leader in a single node cluster.
   208  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   209  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
   210  }
   211  
   212  func testEtcdRaftOSNFailureInvalidBootstrapBlock(gt *GomegaWithT, configPath, orderer, configtxgen, cryptoPath string) {
   213  	tempDir, err := ioutil.TempDir("", "etcdraft-test")
   214  	gt.Expect(err).NotTo(HaveOccurred())
   215  	defer os.RemoveAll(tempDir)
   216  
   217  	// create an application channel genesis block
   218  	genesisBlockPath := generateBootstrapBlock(gt, configPath, configtxgen, "mychannel", "SampleOrgChannel")
   219  	genesisBlockBytes, err := ioutil.ReadFile(genesisBlockPath)
   220  	gt.Expect(err).NotTo(HaveOccurred())
   221  
   222  	// Copy it to the designated location in the temporary folder
   223  	genesisBlockPath = filepath.Join(tempDir, "genesis.block")
   224  	err = ioutil.WriteFile(genesisBlockPath, genesisBlockBytes, 0o644)
   225  	gt.Expect(err).NotTo(HaveOccurred())
   226  
   227  	// Launch the OSN
   228  	ordererProcess := launchOrderer(gt, orderer, tempDir, configPath, genesisBlockPath, cryptoPath, "", "false", "info")
   229  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   230  
   231  	expectedErr := "Failed validating bootstrap block: the block isn't a system channel block because it lacks ConsortiumsConfig"
   232  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say(expectedErr))
   233  }
   234  
   235  func testEtcdRaftOSNNoTLSSingleListener(gt *GomegaWithT, configPath, orderer string, configtxgen, cryptoPath string) {
   236  	tempDir, err := ioutil.TempDir("", "etcdraft-test")
   237  	gt.Expect(err).NotTo(HaveOccurred())
   238  	defer os.RemoveAll(tempDir)
   239  
   240  	genesisBlockPath := generateBootstrapBlock(gt, configPath, configtxgen, "system", "SampleEtcdRaftSystemChannel")
   241  
   242  	cmd := exec.Command(orderer)
   243  	cmd.Env = []string{
   244  		fmt.Sprintf("ORDERER_GENERAL_LISTENPORT=%d", nextPort()),
   245  		"ORDERER_GENERAL_BOOTSTRAPMETHOD=file",
   246  		"ORDERER_GENERAL_SYSTEMCHANNEL=system",
   247  		fmt.Sprintf("ORDERER_FILELEDGER_LOCATION=%s", filepath.Join(tempDir, "ledger")),
   248  		fmt.Sprintf("ORDERER_GENERAL_BOOTSTRAPFILE=%s", genesisBlockPath),
   249  		fmt.Sprintf("FABRIC_CFG_PATH=%s", configPath),
   250  	}
   251  	ordererProcess, err := gexec.Start(cmd, nil, nil)
   252  	gt.Expect(err).NotTo(HaveOccurred())
   253  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   254  
   255  	expectedErr := "TLS is required for running ordering nodes of cluster type."
   256  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say(expectedErr))
   257  }
   258  
   259  func testEtcdRaftOSNNoTLSDualListener(gt *GomegaWithT, configPath, orderer string, configtxgen, cryptoPath string) {
   260  	tempDir, err := ioutil.TempDir("", "etcdraft-test")
   261  	gt.Expect(err).NotTo(HaveOccurred())
   262  	defer os.RemoveAll(tempDir)
   263  
   264  	ordererTLSPath := filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls")
   265  	genesisBlockPath := generateBootstrapBlock(gt, configPath, configtxgen, "system", "SampleEtcdRaftSystemChannel")
   266  
   267  	cmd := exec.Command(orderer)
   268  	cmd.Env = []string{
   269  		fmt.Sprintf("ORDERER_GENERAL_LISTENPORT=%d", nextPort()),
   270  		"ORDERER_GENERAL_BOOTSTRAPMETHOD=file",
   271  		"ORDERER_GENERAL_SYSTEMCHANNEL=system",
   272  		"ORDERER_GENERAL_TLS_ENABLED=false",
   273  		"ORDERER_OPERATIONS_TLS_ENABLED=false",
   274  		fmt.Sprintf("ORDERER_FILELEDGER_LOCATION=%s", filepath.Join(tempDir, "ledger")),
   275  		fmt.Sprintf("ORDERER_GENERAL_BOOTSTRAPFILE=%s", genesisBlockPath),
   276  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_LISTENPORT=%d", nextPort()),
   277  		"ORDERER_GENERAL_CLUSTER_LISTENADDRESS=127.0.0.1",
   278  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_SERVERCERTIFICATE=%s", filepath.Join(ordererTLSPath, "server.crt")),
   279  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_SERVERPRIVATEKEY=%s", filepath.Join(ordererTLSPath, "server.key")),
   280  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=%s", filepath.Join(ordererTLSPath, "server.crt")),
   281  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=%s", filepath.Join(ordererTLSPath, "server.key")),
   282  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_ROOTCAS=[%s]", filepath.Join(ordererTLSPath, "ca.crt")),
   283  		fmt.Sprintf("ORDERER_CONSENSUS_WALDIR=%s", filepath.Join(tempDir, "wal")),
   284  		fmt.Sprintf("ORDERER_CONSENSUS_SNAPDIR=%s", filepath.Join(tempDir, "snapshot")),
   285  		fmt.Sprintf("FABRIC_CFG_PATH=%s", configPath),
   286  		"ORDERER_OPERATIONS_LISTENADDRESS=127.0.0.1:0",
   287  	}
   288  	ordererProcess, err := gexec.Start(cmd, nil, nil)
   289  	gt.Expect(err).NotTo(HaveOccurred())
   290  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   291  
   292  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   293  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
   294  }
   295  
   296  func launchOrderer(gt *GomegaWithT, orderer, tempDir, configPath, genesisBlockPath, cryptoPath, bootstrapMethod, channelParticipationEnabled, logSpec string) *gexec.Session {
   297  	ordererTLSPath := filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls")
   298  	// Launch the orderer process
   299  	cmd := exec.Command(orderer)
   300  	cmd.Env = []string{
   301  		fmt.Sprintf("ORDERER_GENERAL_LISTENPORT=%d", nextPort()),
   302  		"ORDERER_GENERAL_BOOTSTRAPMETHOD=" + bootstrapMethod,
   303  		"ORDERER_GENERAL_SYSTEMCHANNEL=system",
   304  		"ORDERER_GENERAL_TLS_CLIENTAUTHREQUIRED=true",
   305  		"ORDERER_GENERAL_TLS_ENABLED=true",
   306  		"ORDERER_OPERATIONS_TLS_ENABLED=false",
   307  		"ORDERER_FILELEDGER_LOCATION=" + filepath.Join(tempDir, "ledger"),
   308  		"ORDERER_GENERAL_BOOTSTRAPFILE=" + genesisBlockPath,
   309  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_LISTENPORT=%d", nextPort()),
   310  		"ORDERER_GENERAL_CLUSTER_LISTENADDRESS=127.0.0.1",
   311  		"ORDERER_GENERAL_CLUSTER_SERVERCERTIFICATE=" + filepath.Join(ordererTLSPath, "server.crt"),
   312  		"ORDERER_GENERAL_CLUSTER_SERVERPRIVATEKEY=" + filepath.Join(ordererTLSPath, "server.key"),
   313  		"ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=" + filepath.Join(ordererTLSPath, "server.crt"),
   314  		"ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=" + filepath.Join(ordererTLSPath, "server.key"),
   315  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_ROOTCAS=[%s]", filepath.Join(ordererTLSPath, "ca.crt")),
   316  		fmt.Sprintf("ORDERER_GENERAL_TLS_ROOTCAS=[%s]", filepath.Join(ordererTLSPath, "ca.crt")),
   317  		"ORDERER_GENERAL_TLS_CERTIFICATE=" + filepath.Join(ordererTLSPath, "server.crt"),
   318  		"ORDERER_GENERAL_TLS_PRIVATEKEY=" + filepath.Join(ordererTLSPath, "server.key"),
   319  		"ORDERER_CONSENSUS_WALDIR=" + filepath.Join(tempDir, "wal"),
   320  		"ORDERER_CONSENSUS_SNAPDIR=" + filepath.Join(tempDir, "snapshot"),
   321  		"ORDERER_CHANNELPARTICIPATION_ENABLED=" + channelParticipationEnabled,
   322  		"FABRIC_CFG_PATH=" + configPath,
   323  		"FABRIC_LOGGING_SPEC=" + logSpec,
   324  	}
   325  	sess, err := gexec.Start(cmd, nil, nil)
   326  	gt.Expect(err).NotTo(HaveOccurred())
   327  	return sess
   328  }