github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/integration/e2e/health_test.go (about) 1 /* 2 Copyright IBM Corp All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package e2e 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "io/ioutil" 13 "net" 14 "net/http" 15 "os" 16 "syscall" 17 18 docker "github.com/fsouza/go-dockerclient" 19 "github.com/hyperledger/fabric-lib-go/healthz" 20 "github.com/osdi23p228/fabric/integration/nwo" 21 "github.com/osdi23p228/fabric/integration/runner" 22 . "github.com/onsi/ginkgo" 23 . "github.com/onsi/gomega" 24 "github.com/tedsuo/ifrit" 25 "github.com/tedsuo/ifrit/ginkgomon" 26 ) 27 28 var _ = Describe("Health", func() { 29 var ( 30 testDir string 31 client *docker.Client 32 network *nwo.Network 33 process ifrit.Process 34 ) 35 36 BeforeEach(func() { 37 var err error 38 testDir, err = ioutil.TempDir("", "e2e") 39 Expect(err).NotTo(HaveOccurred()) 40 41 client, err = docker.NewClientFromEnv() 42 Expect(err).NotTo(HaveOccurred()) 43 44 config := nwo.BasicKafka() 45 config.Consensus.Brokers = 3 46 47 network = nwo.New(config, testDir, client, StartPort(), components) 48 network.GenerateConfigTree() 49 network.Bootstrap() 50 }) 51 52 AfterEach(func() { 53 if process != nil { 54 process.Signal(syscall.SIGTERM) 55 Eventually(process.Wait, network.EventuallyTimeout).Should(Receive()) 56 } 57 if network != nil { 58 network.Cleanup() 59 } 60 os.RemoveAll(testDir) 61 }) 62 63 Describe("CouchDB health checks", func() { 64 var ( 65 couchAddr string 66 authClient *http.Client 67 healthURL string 68 peer *nwo.Peer 69 couchDB *runner.CouchDB 70 couchProcess ifrit.Process 71 ) 72 73 BeforeEach(func() { 74 couchDB = &runner.CouchDB{} 75 couchProcess = ifrit.Invoke(couchDB) 76 Eventually(couchProcess.Ready(), runner.DefaultStartTimeout).Should(BeClosed()) 77 Consistently(couchProcess.Wait()).ShouldNot(Receive()) 78 couchAddr = couchDB.Address() 79 80 peer = network.Peer("Org1", "peer0") 81 core := network.ReadPeerConfig(peer) 82 core.Ledger.State.StateDatabase = "CouchDB" 83 core.Ledger.State.CouchDBConfig.CouchDBAddress = couchAddr 84 network.WritePeerConfig(peer, core) 85 86 peerRunner := network.PeerRunner(peer) 87 process = ginkgomon.Invoke(peerRunner) 88 Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed()) 89 90 authClient, _ = PeerOperationalClients(network, peer) 91 healthURL = fmt.Sprintf("https://127.0.0.1:%d/healthz", network.PeerPort(peer, nwo.OperationsPort)) 92 }) 93 94 AfterEach(func() { 95 couchProcess.Signal(syscall.SIGTERM) 96 Eventually(couchProcess.Wait(), network.EventuallyTimeout).Should(Receive()) 97 }) 98 99 When("running health checks on Couch DB", func() { 100 It("returns appropriate response codes", func() { 101 By("returning 200 when able to reach Couch DB") 102 statusCode, status := DoHealthCheck(authClient, healthURL) 103 Expect(statusCode).To(Equal(http.StatusOK)) 104 Expect(status.Status).To(Equal("OK")) 105 106 By("terminating CouchDB") 107 couchProcess.Signal(syscall.SIGTERM) 108 Eventually(couchProcess.Wait(), network.EventuallyTimeout).Should(Receive()) 109 110 By("waiting for termination to complete") 111 Eventually(func() bool { 112 if c, err := net.Dial("tcp", couchDB.Address()); err == nil { 113 c.Close() 114 return false 115 } 116 return true 117 }, network.EventuallyTimeout).Should(BeTrue()) 118 119 By("returning 503 when unable to reach Couch DB") 120 Eventually(func() int { 121 statusCode, _ := DoHealthCheck(authClient, healthURL) 122 return statusCode 123 }, network.EventuallyTimeout).Should(Equal(http.StatusServiceUnavailable)) 124 statusCode, status = DoHealthCheck(authClient, healthURL) 125 Expect(statusCode).To(Equal(http.StatusServiceUnavailable)) 126 Expect(status.Status).To(Equal("Service Unavailable")) 127 Expect(status.FailedChecks[0].Component).To(Equal("couchdb")) 128 Expect(status.FailedChecks[0].Reason).To(MatchRegexp(fmt.Sprintf(`failed to connect to couch db \[http error calling couchdb: Head "?http://%s"?: dial tcp %s: .*\]`, couchAddr, couchAddr))) 129 }) 130 }) 131 }) 132 133 Describe("Kafka health checks", func() { 134 var ( 135 oProcess ifrit.Process 136 zProcess ifrit.Process 137 kProcess []ifrit.Process 138 139 authClient *http.Client 140 healthURL string 141 ) 142 143 BeforeEach(func() { 144 // Start Zookeeper 145 zookeepers := []string{} 146 zk := network.ZooKeeperRunner(0) 147 zookeepers = append(zookeepers, fmt.Sprintf("%s:2181", zk.Name)) 148 zProcess = ifrit.Invoke(zk) 149 Eventually(zProcess.Ready(), network.EventuallyTimeout).Should(BeClosed()) 150 151 // Start Kafka Brokers 152 for i := 0; i < network.Consensus.Brokers; i++ { 153 kafkaRunner := network.BrokerRunner(i, zookeepers) 154 kp := ifrit.Invoke(kafkaRunner) 155 Eventually(kp.Ready(), network.EventuallyTimeout).Should(BeClosed()) 156 kProcess = append(kProcess, kp) 157 } 158 159 // Start Orderer 160 ordererRunner := network.OrdererGroupRunner() 161 oProcess = ifrit.Invoke(ordererRunner) 162 Eventually(oProcess.Ready(), network.EventuallyTimeout).Should(BeClosed()) 163 164 orderer := network.Orderers[0] 165 authClient, _ = OrdererOperationalClients(network, orderer) 166 healthURL = fmt.Sprintf("https://127.0.0.1:%d/healthz", network.OrdererPort(orderer, nwo.OperationsPort)) 167 }) 168 169 AfterEach(func() { 170 if zProcess != nil { 171 zProcess.Signal(syscall.SIGTERM) 172 Eventually(zProcess.Wait, network.EventuallyTimeout).Should(Receive()) 173 } 174 if oProcess != nil { 175 oProcess.Signal(syscall.SIGTERM) 176 Eventually(oProcess.Wait, network.EventuallyTimeout).Should(Receive()) 177 } 178 for _, k := range kProcess { 179 if k != nil { 180 k.Signal(syscall.SIGTERM) 181 Eventually(k.Wait, network.EventuallyTimeout).Should(Receive()) 182 } 183 } 184 }) 185 186 Context("when running health checks on orderer the kafka health check", func() { 187 It("returns appropriate response code", func() { 188 By("returning 200 when all brokers are online", func() { 189 statusCode, status := DoHealthCheck(authClient, healthURL) 190 Expect(statusCode).To(Equal(http.StatusOK)) 191 Expect(status.Status).To(Equal("OK")) 192 }) 193 194 By("returning a 200 when one of the three brokers goes offline", func() { 195 k := kProcess[1] 196 k.Signal(syscall.SIGTERM) 197 Eventually(k.Wait, network.EventuallyTimeout).Should(Receive()) 198 199 var statusCode int 200 var status *healthz.HealthStatus 201 Consistently(func() int { 202 statusCode, status = DoHealthCheck(authClient, healthURL) 203 return statusCode 204 }).Should(Equal(http.StatusOK)) 205 Expect(status.Status).To(Equal("OK")) 206 }) 207 208 By("returning a 503 when two of the three brokers go offline", func() { 209 k := kProcess[0] 210 k.Signal(syscall.SIGTERM) 211 Eventually(k.Wait, network.EventuallyTimeout).Should(Receive()) 212 213 var statusCode int 214 var status *healthz.HealthStatus 215 Eventually(func() int { 216 statusCode, status = DoHealthCheck(authClient, healthURL) 217 return statusCode 218 }, network.EventuallyTimeout).Should(Equal(http.StatusServiceUnavailable)) 219 Expect(status.Status).To(Equal("Service Unavailable")) 220 Expect(status.FailedChecks[0].Component).To(Equal("systemchannel/0")) 221 Expect(status.FailedChecks[0].Reason).To(MatchRegexp(`\[replica ids: \[\d \d \d\]\]: kafka server: Messages are rejected since there are fewer in-sync replicas than required\.`)) 222 }) 223 }) 224 }) 225 }) 226 }) 227 228 func DoHealthCheck(client *http.Client, url string) (int, *healthz.HealthStatus) { 229 resp, err := client.Get(url) 230 Expect(err).NotTo(HaveOccurred()) 231 232 bodyBytes, err := ioutil.ReadAll(resp.Body) 233 Expect(err).NotTo(HaveOccurred()) 234 resp.Body.Close() 235 236 // This occurs when a request to the health check server times out, no body is 237 // returned when a timeout occurs 238 if len(bodyBytes) == 0 { 239 return resp.StatusCode, nil 240 } 241 242 healthStatus := &healthz.HealthStatus{} 243 err = json.Unmarshal(bodyBytes, &healthStatus) 244 Expect(err).NotTo(HaveOccurred()) 245 246 return resp.StatusCode, healthStatus 247 }