152 lines
4.3 KiB
Go
152 lines
4.3 KiB
Go
/*
|
|
Copyright IBM Corp All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"syscall"
|
|
|
|
docker "github.com/fsouza/go-dockerclient"
|
|
"github.com/hyperledger/fabric-lib-go/healthz"
|
|
"github.com/hyperledger/fabric/integration/nwo"
|
|
"github.com/hyperledger/fabric/integration/nwo/runner"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
"github.com/tedsuo/ifrit"
|
|
ginkgomon "github.com/tedsuo/ifrit/ginkgomon_v2"
|
|
)
|
|
|
|
var _ = Describe("Health", func() {
|
|
var (
|
|
testDir string
|
|
client *docker.Client
|
|
network *nwo.Network
|
|
process ifrit.Process
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
var err error
|
|
testDir, err = ioutil.TempDir("", "e2e")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
client, err = docker.NewClientFromEnv()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
config := nwo.BasicEtcdRaft()
|
|
network = nwo.New(config, testDir, client, StartPort(), components)
|
|
network.GenerateConfigTree()
|
|
network.Bootstrap()
|
|
})
|
|
|
|
AfterEach(func() {
|
|
if process != nil {
|
|
process.Signal(syscall.SIGTERM)
|
|
Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
}
|
|
if network != nil {
|
|
network.Cleanup()
|
|
}
|
|
os.RemoveAll(testDir)
|
|
})
|
|
|
|
Describe("CouchDB health checks", func() {
|
|
var (
|
|
couchAddr string
|
|
authClient *http.Client
|
|
healthURL string
|
|
peer *nwo.Peer
|
|
couchDB *runner.CouchDB
|
|
couchProcess ifrit.Process
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
couchDB = &runner.CouchDB{}
|
|
couchProcess = ifrit.Invoke(couchDB)
|
|
Eventually(couchProcess.Ready(), runner.DefaultStartTimeout).Should(BeClosed())
|
|
Consistently(couchProcess.Wait()).ShouldNot(Receive())
|
|
couchAddr = couchDB.Address()
|
|
|
|
peer = network.Peer("Org1", "peer0")
|
|
core := network.ReadPeerConfig(peer)
|
|
core.Ledger.State.StateDatabase = "CouchDB"
|
|
core.Ledger.State.CouchDBConfig.CouchDBAddress = couchAddr
|
|
network.WritePeerConfig(peer, core)
|
|
|
|
peerRunner := network.PeerRunner(peer)
|
|
process = ginkgomon.Invoke(peerRunner)
|
|
Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed())
|
|
|
|
authClient, _ = nwo.PeerOperationalClients(network, peer)
|
|
healthURL = fmt.Sprintf("https://127.0.0.1:%d/healthz", network.PeerPort(peer, nwo.OperationsPort))
|
|
})
|
|
|
|
AfterEach(func() {
|
|
couchProcess.Signal(syscall.SIGTERM)
|
|
Eventually(couchProcess.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
})
|
|
|
|
When("running health checks on Couch DB", func() {
|
|
It("returns appropriate response codes", func() {
|
|
By("returning 200 when able to reach Couch DB")
|
|
statusCode, status := doHealthCheck(authClient, healthURL)
|
|
Expect(statusCode).To(Equal(http.StatusOK))
|
|
Expect(status.Status).To(Equal("OK"))
|
|
|
|
By("terminating CouchDB")
|
|
couchProcess.Signal(syscall.SIGTERM)
|
|
Eventually(couchProcess.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
|
|
By("waiting for termination to complete")
|
|
Eventually(func() bool {
|
|
if c, err := net.Dial("tcp", couchDB.Address()); err == nil {
|
|
c.Close()
|
|
return false
|
|
}
|
|
return true
|
|
}, network.EventuallyTimeout).Should(BeTrue())
|
|
|
|
By("returning 503 when unable to reach Couch DB")
|
|
Eventually(func() int {
|
|
statusCode, _ := doHealthCheck(authClient, healthURL)
|
|
return statusCode
|
|
}, network.EventuallyTimeout).Should(Equal(http.StatusServiceUnavailable))
|
|
statusCode, status = doHealthCheck(authClient, healthURL)
|
|
Expect(statusCode).To(Equal(http.StatusServiceUnavailable))
|
|
Expect(status.Status).To(Equal("Service Unavailable"))
|
|
Expect(status.FailedChecks[0].Component).To(Equal("couchdb"))
|
|
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)))
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
func doHealthCheck(client *http.Client, url string) (int, *healthz.HealthStatus) {
|
|
resp, err := client.Get(url)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
resp.Body.Close()
|
|
|
|
// This occurs when a request to the health check server times out, no body is
|
|
// returned when a timeout occurs
|
|
if len(bodyBytes) == 0 {
|
|
return resp.StatusCode, nil
|
|
}
|
|
|
|
healthStatus := &healthz.HealthStatus{}
|
|
err = json.Unmarshal(bodyBytes, &healthStatus)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
return resp.StatusCode, healthStatus
|
|
}
|