github.com/ewagmig/fabric@v2.1.1+incompatible/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/hyperledger/fabric/integration/nwo" 21 "github.com/hyperledger/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(status.Status).To(Equal("Service Unavailable")) 126 Expect(status.FailedChecks[0].Component).To(Equal("couchdb")) 127 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))) 128 }) 129 }) 130 }) 131 132 Describe("Kafka health checks", func() { 133 var ( 134 oProcess ifrit.Process 135 zProcess ifrit.Process 136 kProcess []ifrit.Process 137 138 authClient *http.Client 139 healthURL string 140 ) 141 142 BeforeEach(func() { 143 // Start Zookeeper 144 zookeepers := []string{} 145 zk := network.ZooKeeperRunner(0) 146 zookeepers = append(zookeepers, fmt.Sprintf("%s:2181", zk.Name)) 147 zProcess = ifrit.Invoke(zk) 148 Eventually(zProcess.Ready(), network.EventuallyTimeout).Should(BeClosed()) 149 150 // Start Kafka Brokers 151 for i := 0; i < network.Consensus.Brokers; i++ { 152 kafkaRunner := network.BrokerRunner(i, zookeepers) 153 kp := ifrit.Invoke(kafkaRunner) 154 Eventually(kp.Ready(), network.EventuallyTimeout).Should(BeClosed()) 155 kProcess = append(kProcess, kp) 156 } 157 158 // Start Orderer 159 ordererRunner := network.OrdererGroupRunner() 160 oProcess = ifrit.Invoke(ordererRunner) 161 Eventually(oProcess.Ready(), network.EventuallyTimeout).Should(BeClosed()) 162 163 orderer := network.Orderers[0] 164 authClient, _ = OrdererOperationalClients(network, orderer) 165 healthURL = fmt.Sprintf("https://127.0.0.1:%d/healthz", network.OrdererPort(orderer, nwo.OperationsPort)) 166 }) 167 168 AfterEach(func() { 169 if zProcess != nil { 170 zProcess.Signal(syscall.SIGTERM) 171 Eventually(zProcess.Wait, network.EventuallyTimeout).Should(Receive()) 172 } 173 if oProcess != nil { 174 oProcess.Signal(syscall.SIGTERM) 175 Eventually(oProcess.Wait, network.EventuallyTimeout).Should(Receive()) 176 } 177 for _, k := range kProcess { 178 if k != nil { 179 k.Signal(syscall.SIGTERM) 180 Eventually(k.Wait, network.EventuallyTimeout).Should(Receive()) 181 } 182 } 183 }) 184 185 Context("when running health checks on orderer the kafka health check", func() { 186 It("returns appropriate response code", func() { 187 By("returning 200 when all brokers are online", func() { 188 statusCode, status := DoHealthCheck(authClient, healthURL) 189 Expect(statusCode).To(Equal(http.StatusOK)) 190 Expect(status.Status).To(Equal("OK")) 191 }) 192 193 By("returning a 200 when one of the three brokers goes offline", func() { 194 k := kProcess[1] 195 k.Signal(syscall.SIGTERM) 196 Eventually(k.Wait, network.EventuallyTimeout).Should(Receive()) 197 198 var statusCode int 199 var status *healthz.HealthStatus 200 Consistently(func() int { 201 statusCode, status = DoHealthCheck(authClient, healthURL) 202 return statusCode 203 }).Should(Equal(http.StatusOK)) 204 Expect(status.Status).To(Equal("OK")) 205 }) 206 207 By("returning a 503 when two of the three brokers go offline", func() { 208 k := kProcess[0] 209 k.Signal(syscall.SIGTERM) 210 Eventually(k.Wait, network.EventuallyTimeout).Should(Receive()) 211 212 var statusCode int 213 var status *healthz.HealthStatus 214 Eventually(func() int { 215 statusCode, status = DoHealthCheck(authClient, healthURL) 216 return statusCode 217 }, network.EventuallyTimeout).Should(Equal(http.StatusServiceUnavailable)) 218 Expect(status.Status).To(Equal("Service Unavailable")) 219 Expect(status.FailedChecks[0].Component).To(Equal("systemchannel/0")) 220 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\.`)) 221 }) 222 }) 223 }) 224 }) 225 }) 226 227 func DoHealthCheck(client *http.Client, url string) (int, *healthz.HealthStatus) { 228 resp, err := client.Get(url) 229 Expect(err).NotTo(HaveOccurred()) 230 231 bodyBytes, err := ioutil.ReadAll(resp.Body) 232 Expect(err).NotTo(HaveOccurred()) 233 resp.Body.Close() 234 235 // This occurs when a request to the health check server times out, no body is 236 // returned when a timeout occurs 237 if len(bodyBytes) == 0 { 238 return resp.StatusCode, nil 239 } 240 241 healthStatus := &healthz.HealthStatus{} 242 err = json.Unmarshal(bodyBytes, &healthStatus) 243 Expect(err).NotTo(HaveOccurred()) 244 245 return resp.StatusCode, healthStatus 246 }