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 }