| 
 | 1 | +package main_test  | 
 | 2 | + | 
 | 3 | +import (  | 
 | 4 | +	"fmt"  | 
 | 5 | +	"net"  | 
 | 6 | +	"os"  | 
 | 7 | +	"os/exec"  | 
 | 8 | +	"strconv"  | 
 | 9 | + | 
 | 10 | +	. "github.com/onsi/ginkgo"  | 
 | 11 | +	. "github.com/onsi/gomega"  | 
 | 12 | +	"github.com/onsi/gomega/gbytes"  | 
 | 13 | +	"github.com/onsi/gomega/gexec"  | 
 | 14 | +	"github.com/onsi/gomega/ghttp"  | 
 | 15 | +)  | 
 | 16 | + | 
 | 17 | +var _ = Describe("HealthCheck", func() {  | 
 | 18 | +	var (  | 
 | 19 | +		server     *ghttp.Server  | 
 | 20 | +		serverAddr string  | 
 | 21 | +	)  | 
 | 22 | + | 
 | 23 | +	itExitsWithCode := func(healthCheck func() *gexec.Session, code int, reason string) {  | 
 | 24 | +		It("exits with code "+strconv.Itoa(code)+" and logs reason", func() {  | 
 | 25 | +			session := healthCheck()  | 
 | 26 | +			Eventually(session).Should(gexec.Exit(code))  | 
 | 27 | +			Expect(session.Out).To(gbytes.Say(reason))  | 
 | 28 | +		})  | 
 | 29 | +	}  | 
 | 30 | + | 
 | 31 | +	BeforeEach(func() {  | 
 | 32 | +		ip := getNonLoopbackIP()  | 
 | 33 | +		server = ghttp.NewUnstartedServer()  | 
 | 34 | +		listener, err := net.Listen("tcp", ip+":0")  | 
 | 35 | +		Expect(err).NotTo(HaveOccurred())  | 
 | 36 | + | 
 | 37 | +		server.HTTPTestServer.Listener = listener  | 
 | 38 | +		serverAddr = listener.Addr().String()  | 
 | 39 | +		server.Start()  | 
 | 40 | +	})  | 
 | 41 | + | 
 | 42 | +	Describe("fails when parsing flags", func() {  | 
 | 43 | +		It("exits with code 2", func() {  | 
 | 44 | +			session, _ := gexec.Start(exec.Command(healthCheck, "-invalid_flag"), GinkgoWriter, GinkgoWriter)  | 
 | 45 | +			Eventually(session).Should(gexec.Exit(2))  | 
 | 46 | +		})  | 
 | 47 | +	})  | 
 | 48 | + | 
 | 49 | +	Describe("port healthcheck", func() {  | 
 | 50 | +		var port string  | 
 | 51 | +		var err error  | 
 | 52 | + | 
 | 53 | +		portHealthCheck := func() *gexec.Session {  | 
 | 54 | +			command := exec.Command(healthCheck, "-port", "8080", "-timeout", "100ms")  | 
 | 55 | +			command.Env = append(  | 
 | 56 | +				os.Environ(),  | 
 | 57 | +				fmt.Sprintf(`CF_INSTANCE_PORTS=[{"external":%s,"internal":%s}]`, port, "8080"),  | 
 | 58 | +			)  | 
 | 59 | +			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)  | 
 | 60 | +			Expect(err).NotTo(HaveOccurred())  | 
 | 61 | +			return session  | 
 | 62 | +		}  | 
 | 63 | + | 
 | 64 | +		BeforeEach(func() {  | 
 | 65 | +			_, port, err = net.SplitHostPort(serverAddr)  | 
 | 66 | +			Expect(err).NotTo(HaveOccurred())  | 
 | 67 | +		})  | 
 | 68 | + | 
 | 69 | +		Context("when the address is listening", func() {  | 
 | 70 | +			itExitsWithCode(portHealthCheck, 0, "healthcheck passed")  | 
 | 71 | +		})  | 
 | 72 | + | 
 | 73 | +		Context("when the address is not listening", func() {  | 
 | 74 | +			BeforeEach(func() {  | 
 | 75 | +				port = "-1"  | 
 | 76 | +			})  | 
 | 77 | + | 
 | 78 | +			itExitsWithCode(portHealthCheck, 4, "failure to make TCP connection")  | 
 | 79 | +		})  | 
 | 80 | +	})  | 
 | 81 | + | 
 | 82 | +	Describe("http healthcheck", func() {  | 
 | 83 | +		var port string  | 
 | 84 | +		var err error  | 
 | 85 | + | 
 | 86 | +		httpHealthCheck := func() *gexec.Session {  | 
 | 87 | +			command := exec.Command(healthCheck, "-uri", "/api/_ping", "-port", "8080", "-timeout", "100ms")  | 
 | 88 | +			command.Env = append(  | 
 | 89 | +				os.Environ(),  | 
 | 90 | +				fmt.Sprintf(`CF_INSTANCE_PORTS=[{"external":%s,"internal":%s}]`, port, "8080"),  | 
 | 91 | +			)  | 
 | 92 | +			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)  | 
 | 93 | +			Expect(err).NotTo(HaveOccurred())  | 
 | 94 | +			return session  | 
 | 95 | +		}  | 
 | 96 | + | 
 | 97 | +		BeforeEach(func() {  | 
 | 98 | +			_, port, err = net.SplitHostPort(serverAddr)  | 
 | 99 | +			Expect(err).NotTo(HaveOccurred())  | 
 | 100 | +		})  | 
 | 101 | + | 
 | 102 | +		Context("when the healthcheck is properly invoked", func() {  | 
 | 103 | +			BeforeEach(func() {  | 
 | 104 | +				server.RouteToHandler("GET", "/api/_ping", ghttp.VerifyRequest("GET", "/api/_ping"))  | 
 | 105 | +			})  | 
 | 106 | + | 
 | 107 | +			Context("when the address is listening", func() {  | 
 | 108 | +				itExitsWithCode(httpHealthCheck, 0, "healthcheck passed")  | 
 | 109 | +			})  | 
 | 110 | + | 
 | 111 | +			Context("when the address returns error http code", func() {  | 
 | 112 | +				BeforeEach(func() {  | 
 | 113 | +					server.RouteToHandler("GET", "/api/_ping", ghttp.RespondWith(500, ""))  | 
 | 114 | +				})  | 
 | 115 | + | 
 | 116 | +				itExitsWithCode(httpHealthCheck, 6, "failure to get valid HTTP status code: 500")  | 
 | 117 | +			})  | 
 | 118 | +		})  | 
 | 119 | +	})  | 
 | 120 | +})  | 
 | 121 | + | 
 | 122 | +func getNonLoopbackIP() string {  | 
 | 123 | +	interfaces, err := net.Interfaces()  | 
 | 124 | +	Expect(err).NotTo(HaveOccurred())  | 
 | 125 | +	for _, intf := range interfaces {  | 
 | 126 | +		addrs, err := intf.Addrs()  | 
 | 127 | +		if err != nil {  | 
 | 128 | +			continue  | 
 | 129 | +		}  | 
 | 130 | + | 
 | 131 | +		for _, a := range addrs {  | 
 | 132 | +			if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {  | 
 | 133 | +				if ipnet.IP.To4() != nil {  | 
 | 134 | +					return ipnet.IP.String()  | 
 | 135 | +				}  | 
 | 136 | +			}  | 
 | 137 | +		}  | 
 | 138 | +	}  | 
 | 139 | +	Fail("no non-loopback address found")  | 
 | 140 | +	panic("non-reachable")  | 
 | 141 | +}  | 
0 commit comments