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 }