github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/orderer/common/server/etcdraft_test.go (about)

     1  /*
     2  Copyright IBM Corp. 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/osdi23p228/fabric/cmd/configtxgen")
    35  	gt.Expect(err).NotTo(HaveOccurred())
    36  
    37  	cryptogen, err := gexec.Build("github.com/osdi23p228/fabric/cmd/cryptogen")
    38  	gt.Expect(err).NotTo(HaveOccurred())
    39  
    40  	// Build the orderer binary
    41  	orderer, err := gexec.Build("github.com/osdi23p228/fabric/cmd/orderer")
    42  	gt.Expect(err).NotTo(HaveOccurred())
    43  
    44  	defer gexec.CleanupBuildArtifacts()
    45  
    46  	tempDir, err := ioutil.TempDir("", "etcdraft-test")
    47  	gt.Expect(err).NotTo(HaveOccurred())
    48  	defer os.RemoveAll(tempDir)
    49  
    50  	copyYamlFiles(gt, "testdata", tempDir)
    51  
    52  	cryptoPath := generateCryptoMaterials(gt, cryptogen, tempDir)
    53  
    54  	t.Run("Bad", func(t *testing.T) {
    55  		t.Run("Invalid bootstrap block", func(t *testing.T) {
    56  			testEtcdRaftOSNFailureInvalidBootstrapBlock(NewGomegaWithT(t), tempDir, orderer, configtxgen, cryptoPath)
    57  		})
    58  
    59  		t.Run("TLS disabled single listener", func(t *testing.T) {
    60  			testEtcdRaftOSNNoTLSSingleListener(NewGomegaWithT(t), tempDir, 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), tempDir, orderer, configtxgen, cryptoPath)
    69  		})
    70  
    71  		t.Run("TLS enabled single listener", func(t *testing.T) {
    72  			testEtcdRaftOSNSuccess(NewGomegaWithT(t), tempDir, configtxgen, orderer, cryptoPath)
    73  		})
    74  
    75  		t.Run("Restart orderer without Genesis Block", func(t *testing.T) {
    76  			testEtcdRaftOSNRestart(NewGomegaWithT(t), tempDir, configtxgen, orderer, cryptoPath)
    77  		})
    78  	})
    79  }
    80  
    81  func copyYamlFiles(gt *GomegaWithT, src, dst string) {
    82  	for _, file := range []string{"configtx.yaml", "examplecom-config.yaml", "orderer.yaml"} {
    83  		fileBytes, err := ioutil.ReadFile(filepath.Join(src, file))
    84  		gt.Expect(err).NotTo(HaveOccurred())
    85  		err = ioutil.WriteFile(filepath.Join(dst, file), fileBytes, 0644)
    86  		gt.Expect(err).NotTo(HaveOccurred())
    87  	}
    88  }
    89  
    90  func generateBootstrapBlock(gt *GomegaWithT, tempDir, configtxgen, channel, profile string) string {
    91  	// create a genesis block for the specified channel and profile
    92  	genesisBlockPath := filepath.Join(tempDir, "genesis.block")
    93  	cmd := exec.Command(
    94  		configtxgen,
    95  		"-channelID", channel,
    96  		"-profile", profile,
    97  		"-outputBlock", genesisBlockPath,
    98  		"--configPath", tempDir,
    99  	)
   100  	configtxgenProcess, err := gexec.Start(cmd, nil, nil)
   101  	gt.Expect(err).NotTo(HaveOccurred())
   102  	gt.Eventually(configtxgenProcess, time.Minute).Should(gexec.Exit(0))
   103  	gt.Expect(configtxgenProcess.Err).To(gbytes.Say("Writing genesis block"))
   104  
   105  	return genesisBlockPath
   106  }
   107  
   108  func generateCryptoMaterials(gt *GomegaWithT, cryptogen, path string) string {
   109  	cryptoPath := filepath.Join(path, "crypto")
   110  
   111  	cmd := exec.Command(
   112  		cryptogen,
   113  		"generate",
   114  		"--config", filepath.Join(path, "examplecom-config.yaml"),
   115  		"--output", cryptoPath,
   116  	)
   117  	cryptogenProcess, err := gexec.Start(cmd, nil, nil)
   118  	gt.Expect(err).NotTo(HaveOccurred())
   119  	gt.Eventually(cryptogenProcess, time.Minute).Should(gexec.Exit(0))
   120  
   121  	return cryptoPath
   122  }
   123  
   124  func testEtcdRaftOSNRestart(gt *GomegaWithT, tempDir, configtxgen, orderer, cryptoPath string) {
   125  	genesisBlockPath := generateBootstrapBlock(gt, tempDir, configtxgen, "system", "SampleEtcdRaftSystemChannel")
   126  
   127  	// Launch the OSN
   128  	ordererProcess := launchOrderer(gt, orderer, tempDir, genesisBlockPath, cryptoPath, "file")
   129  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   130  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   131  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
   132  
   133  	// Restart orderer with ORDERER_GENERAL_BOOTSTRAPMETHOD = none
   134  	gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit())
   135  	ordererProcess = launchOrderer(gt, orderer, tempDir, "", cryptoPath, "none")
   136  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   137  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
   138  }
   139  
   140  func testEtcdRaftOSNSuccess(gt *GomegaWithT, tempDir, configtxgen, orderer, cryptoPath string) {
   141  	genesisBlockPath := generateBootstrapBlock(gt, tempDir, configtxgen, "system", "SampleEtcdRaftSystemChannel")
   142  
   143  	// Launch the OSN
   144  	ordererProcess := launchOrderer(gt, orderer, tempDir, genesisBlockPath, cryptoPath, "file")
   145  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   146  	// The following configuration parameters are not specified in the orderer.yaml, so let's ensure
   147  	// they are really configured autonomously via the localconfig code.
   148  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.DialTimeout = 5s"))
   149  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.RPCTimeout = 7s"))
   150  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationBufferSize = 20971520"))
   151  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationPullTimeout = 5s"))
   152  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationRetryTimeout = 5s"))
   153  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationBackgroundRefreshInterval = 5m0s"))
   154  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.ReplicationMaxRetries = 12"))
   155  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.SendBufferSize = 10"))
   156  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("General.Cluster.CertExpirationWarningThreshold = 168h0m0s"))
   157  
   158  	// Consensus.EvictionSuspicion is not specified in orderer.yaml, so let's ensure
   159  	// it is really configured autonomously via the etcdraft chain itself.
   160  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("EvictionSuspicion not set, defaulting to 10m"))
   161  	// Wait until the the node starts up and elects itself as a single leader in a single node cluster.
   162  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   163  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
   164  }
   165  
   166  func testEtcdRaftOSNFailureInvalidBootstrapBlock(gt *GomegaWithT, tempDir, orderer, configtxgen, cryptoPath string) {
   167  	// create an application channel genesis block
   168  	genesisBlockPath := generateBootstrapBlock(gt, tempDir, configtxgen, "mychannel", "SampleOrgChannel")
   169  	genesisBlockBytes, err := ioutil.ReadFile(genesisBlockPath)
   170  	gt.Expect(err).NotTo(HaveOccurred())
   171  
   172  	// Copy it to the designated location in the temporary folder
   173  	genesisBlockPath = filepath.Join(tempDir, "genesis.block")
   174  	err = ioutil.WriteFile(genesisBlockPath, genesisBlockBytes, 0644)
   175  	gt.Expect(err).NotTo(HaveOccurred())
   176  
   177  	// Launch the OSN
   178  	ordererProcess := launchOrderer(gt, orderer, tempDir, genesisBlockPath, cryptoPath, "")
   179  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   180  
   181  	expectedErr := "Failed validating bootstrap block: the block isn't a system channel block because it lacks ConsortiumsConfig"
   182  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say(expectedErr))
   183  }
   184  
   185  func testEtcdRaftOSNNoTLSSingleListener(gt *GomegaWithT, tempDir, orderer string, configtxgen, cryptoPath string) {
   186  	genesisBlockPath := generateBootstrapBlock(gt, tempDir, configtxgen, "system", "SampleEtcdRaftSystemChannel")
   187  
   188  	cmd := exec.Command(orderer)
   189  	cmd.Env = []string{
   190  		fmt.Sprintf("ORDERER_GENERAL_LISTENPORT=%d", nextPort()),
   191  		"ORDERER_GENERAL_BOOTSTRAPMETHOD=file",
   192  		"ORDERER_GENERAL_SYSTEMCHANNEL=system",
   193  		fmt.Sprintf("ORDERER_FILELEDGER_LOCATION=%s", filepath.Join(tempDir, "ledger")),
   194  		fmt.Sprintf("ORDERER_GENERAL_BOOTSTRAPFILE=%s", genesisBlockPath),
   195  		fmt.Sprintf("FABRIC_CFG_PATH=%s", tempDir),
   196  	}
   197  	ordererProcess, err := gexec.Start(cmd, nil, nil)
   198  	gt.Expect(err).NotTo(HaveOccurred())
   199  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   200  
   201  	expectedErr := "TLS is required for running ordering nodes of cluster type."
   202  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say(expectedErr))
   203  }
   204  
   205  func testEtcdRaftOSNNoTLSDualListener(gt *GomegaWithT, tempDir, orderer string, configtxgen, cryptoPath string) {
   206  	ordererTLSPath := filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls")
   207  	genesisBlockPath := generateBootstrapBlock(gt, tempDir, configtxgen, "system", "SampleEtcdRaftSystemChannel")
   208  
   209  	cmd := exec.Command(orderer)
   210  	cmd.Env = []string{
   211  		fmt.Sprintf("ORDERER_GENERAL_LISTENPORT=%d", nextPort()),
   212  		"ORDERER_GENERAL_BOOTSTRAPMETHOD=file",
   213  		"ORDERER_GENERAL_SYSTEMCHANNEL=system",
   214  		"ORDERER_GENERAL_TLS_ENABLED=false",
   215  		"ORDERER_OPERATIONS_TLS_ENABLED=false",
   216  		fmt.Sprintf("ORDERER_FILELEDGER_LOCATION=%s", filepath.Join(tempDir, "ledger")),
   217  		fmt.Sprintf("ORDERER_GENERAL_BOOTSTRAPFILE=%s", genesisBlockPath),
   218  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_LISTENPORT=%d", nextPort()),
   219  		"ORDERER_GENERAL_CLUSTER_LISTENADDRESS=127.0.0.1",
   220  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_SERVERCERTIFICATE=%s", filepath.Join(ordererTLSPath, "server.crt")),
   221  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_SERVERPRIVATEKEY=%s", filepath.Join(ordererTLSPath, "server.key")),
   222  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=%s", filepath.Join(ordererTLSPath, "server.crt")),
   223  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=%s", filepath.Join(ordererTLSPath, "server.key")),
   224  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_ROOTCAS=[%s]", filepath.Join(ordererTLSPath, "ca.crt")),
   225  		fmt.Sprintf("ORDERER_CONSENSUS_WALDIR=%s", filepath.Join(tempDir, "wal")),
   226  		fmt.Sprintf("ORDERER_CONSENSUS_SNAPDIR=%s", filepath.Join(tempDir, "snapshot")),
   227  		fmt.Sprintf("FABRIC_CFG_PATH=%s", tempDir),
   228  		"ORDERER_OPERATIONS_LISTENADDRESS=127.0.0.1:0",
   229  	}
   230  	ordererProcess, err := gexec.Start(cmd, nil, nil)
   231  	gt.Expect(err).NotTo(HaveOccurred())
   232  	defer func() { gt.Eventually(ordererProcess.Kill(), time.Minute).Should(gexec.Exit()) }()
   233  
   234  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("Beginning to serve requests"))
   235  	gt.Eventually(ordererProcess.Err, time.Minute).Should(gbytes.Say("becomeLeader"))
   236  }
   237  
   238  func launchOrderer(gt *GomegaWithT, orderer, tempDir, genesisBlockPath, cryptoPath, bootstrapMethod string) *gexec.Session {
   239  	ordererTLSPath := filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls")
   240  	// Launch the orderer process
   241  	cmd := exec.Command(orderer)
   242  	cmd.Env = []string{
   243  		fmt.Sprintf("ORDERER_GENERAL_LISTENPORT=%d", nextPort()),
   244  		fmt.Sprintf("ORDERER_GENERAL_BOOTSTRAPMETHOD=%s", bootstrapMethod),
   245  		"ORDERER_GENERAL_SYSTEMCHANNEL=system",
   246  		"ORDERER_GENERAL_TLS_CLIENTAUTHREQUIRED=true",
   247  		"ORDERER_GENERAL_TLS_ENABLED=true",
   248  		"ORDERER_OPERATIONS_TLS_ENABLED=false",
   249  		fmt.Sprintf("ORDERER_FILELEDGER_LOCATION=%s", filepath.Join(tempDir, "ledger")),
   250  		fmt.Sprintf("ORDERER_GENERAL_BOOTSTRAPFILE=%s", genesisBlockPath),
   251  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_LISTENPORT=%d", nextPort()),
   252  		"ORDERER_GENERAL_CLUSTER_LISTENADDRESS=127.0.0.1",
   253  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_SERVERCERTIFICATE=%s", filepath.Join(ordererTLSPath, "server.crt")),
   254  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_SERVERPRIVATEKEY=%s", filepath.Join(ordererTLSPath, "server.key")),
   255  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=%s", filepath.Join(ordererTLSPath, "server.crt")),
   256  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=%s", filepath.Join(ordererTLSPath, "server.key")),
   257  		fmt.Sprintf("ORDERER_GENERAL_CLUSTER_ROOTCAS=[%s]", filepath.Join(ordererTLSPath, "ca.crt")),
   258  		fmt.Sprintf("ORDERER_GENERAL_TLS_ROOTCAS=[%s]", filepath.Join(ordererTLSPath, "ca.crt")),
   259  		fmt.Sprintf("ORDERER_GENERAL_TLS_CERTIFICATE=%s", filepath.Join(ordererTLSPath, "server.crt")),
   260  		fmt.Sprintf("ORDERER_GENERAL_TLS_PRIVATEKEY=%s", filepath.Join(ordererTLSPath, "server.key")),
   261  		fmt.Sprintf("ORDERER_CONSENSUS_WALDIR=%s", filepath.Join(tempDir, "wal")),
   262  		fmt.Sprintf("ORDERER_CONSENSUS_SNAPDIR=%s", filepath.Join(tempDir, "snapshot")),
   263  		fmt.Sprintf("FABRIC_CFG_PATH=%s", tempDir),
   264  	}
   265  	sess, err := gexec.Start(cmd, nil, nil)
   266  	gt.Expect(err).NotTo(HaveOccurred())
   267  	return sess
   268  }