Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load backends from env vars #493

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 49 additions & 17 deletions integration_tests/backend_helpers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package integration

import (
"net"
"net/http"
"net/http/httptest"
"strconv"
Expand All @@ -12,14 +13,39 @@ import (
"github.com/onsi/gomega/ghttp"
)

func startSimpleBackend(identifier string) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var backends = map[string]string{
"backend-1": "127.0.0.1:6789",
"backend-2": "127.0.0.1:6790",
"outer": "127.0.0.1:6792",
"inner": "127.0.0.1:6793",
"innerer": "127.0.0.1:6794",
"root": "127.0.0.1:6795",
"other": "127.0.0.1:6796",
"fallthrough": "127.0.0.1:6797",
"down": "127.0.0.1:6798",
"slow-1": "127.0.0.1:6799",
"slow-2": "127.0.0.1:6800",
"backend": "127.0.0.1:6801",
"be": "127.0.0.1:6802",
"not-running": "127.0.0.1:6803",
"with-path": "127.0.0.1:6804",
}

func startSimpleBackend(identifier, host string) *httptest.Server {
l, err := net.Listen("tcp", host)
Expect(err).NotTo(HaveOccurred())

ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(identifier))
Expect(err).NotTo(HaveOccurred())
}))
ts.Listener.Close()
ts.Listener = l
ts.Start()
return ts
}

func startTarpitBackend(delays ...time.Duration) *httptest.Server {
func startTarpitBackend(host string, delays ...time.Duration) *httptest.Server {
responseDelay := 2 * time.Second
if len(delays) > 0 {
responseDelay = delays[0]
Expand All @@ -28,7 +54,11 @@ func startTarpitBackend(delays ...time.Duration) *httptest.Server {
if len(delays) > 1 {
bodyDelay = delays[1]
}
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

l, err := net.Listen("tcp", host)
Expect(err).NotTo(HaveOccurred())

ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body := "Tarpit\n"

if responseDelay > 0 {
Expand All @@ -44,24 +74,26 @@ func startTarpitBackend(delays ...time.Duration) *httptest.Server {
_, err := w.Write([]byte(body))
Expect(err).NotTo(HaveOccurred())
}))
ts.Listener.Close()
ts.Listener = l
ts.Start()
return ts
}

func startRecordingBackend() *ghttp.Server {
return startRecordingServer(false)
}

func startRecordingTLSBackend() *ghttp.Server {
return startRecordingServer(true)
}
func startRecordingBackend(tls bool, host string) *ghttp.Server {
l, err := net.Listen("tcp", host)
Expect(err).NotTo(HaveOccurred())

func startRecordingServer(tls bool) (server *ghttp.Server) {
ts := ghttp.NewUnstartedServer()
ts.HTTPTestServer.Listener.Close()
ts.HTTPTestServer.Listener = l
if tls {
server = ghttp.NewTLSServer()
ts.HTTPTestServer.StartTLS()
} else {
server = ghttp.NewServer()
ts.Start()
}

server.AllowUnhandledRequests = true
server.UnhandledRequestStatusCode = http.StatusOK
return server
ts.AllowUnhandledRequests = true
ts.UnhandledRequestStatusCode = http.StatusOK
return ts
}
9 changes: 8 additions & 1 deletion integration_tests/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ var _ = BeforeSuite(func() {
if err != nil {
Fail(err.Error())
}
err = startRouter(routerPort, apiPort, nil)

backendEnvVars := []string{}
for id, host := range backends {
envVar := "BACKEND_URL_" + id + "=http://" + host
backendEnvVars = append(backendEnvVars, envVar)
}

err = startRouter(routerPort, apiPort, backendEnvVars)
if err != nil {
Fail(err.Error())
}
Expand Down
24 changes: 9 additions & 15 deletions integration_tests/performance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ var _ = Describe("Performance", func() {
)

BeforeEach(func() {
backend1 = startSimpleBackend("backend 1")
backend2 = startSimpleBackend("backend 2")
addBackend("backend-1", backend1.URL)
addBackend("backend-2", backend2.URL)
backend1 = startSimpleBackend("backend 1", backends["backend-1"])
backend2 = startSimpleBackend("backend 2", backends["backend-2"])
addRoute("/one", NewBackendRoute("backend-1"))
addRoute("/two", NewBackendRoute("backend-2"))
reloadRoutes(apiPort)
Expand Down Expand Up @@ -58,10 +56,9 @@ var _ = Describe("Performance", func() {

Describe("with one slow backend hit separately", func() {
It("Router should not cause errors or much latency", func() {
slowBackend := startTarpitBackend(time.Second)
slowBackend := startTarpitBackend(backends["slow-1"], time.Second)
defer slowBackend.Close()
addBackend("backend-slow", slowBackend.URL)
addRoute("/slow", NewBackendRoute("backend-slow"))
addRoute("/slow", NewBackendRoute("slow-1"))
reloadRoutes(apiPort)

_, gen := generateLoad([]string{routerURL(routerPort, "/slow")}, 50)
Expand All @@ -73,8 +70,7 @@ var _ = Describe("Performance", func() {

Describe("with one downed backend hit separately", func() {
It("Router should not cause errors or much latency", func() {
addBackend("backend-down", "http://127.0.0.1:3162/")
addRoute("/down", NewBackendRoute("backend-down"))
addRoute("/down", NewBackendRoute("down"))
reloadRoutes(apiPort)

_, gen := generateLoad([]string{routerURL(routerPort, "/down")}, 50)
Expand All @@ -96,12 +92,10 @@ var _ = Describe("Performance", func() {
var backend2 *httptest.Server

BeforeEach(func() {
backend1 = startTarpitBackend(time.Second)
backend2 = startTarpitBackend(time.Second)
addBackend("backend-1", backend1.URL)
addBackend("backend-2", backend2.URL)
addRoute("/one", NewBackendRoute("backend-1"))
addRoute("/two", NewBackendRoute("backend-2"))
backend1 = startTarpitBackend(backends["slow-1"], time.Second)
backend2 = startTarpitBackend(backends["slow-2"], time.Second)
addRoute("/one", NewBackendRoute("slow-1"))
addRoute("/two", NewBackendRoute("slow-2"))
reloadRoutes(apiPort)
})
AfterEach(func() {
Expand Down
48 changes: 21 additions & 27 deletions integration_tests/proxy_function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ var _ = Describe("Functioning as a reverse proxy", func() {

Describe("connecting to the backend", func() {
It("should return a 502 if the connection to the backend is refused", func() {
addBackend("not-running", "http://127.0.0.1:3164/")
addRoute("/not-running", NewBackendRoute("not-running"))
reloadRoutes(apiPort)

Expand All @@ -31,21 +30,20 @@ var _ = Describe("Functioning as a reverse proxy", func() {

logDetails := lastRouterErrorLogEntry()
Expect(logDetails.Fields).To(Equal(map[string]interface{}{
"error": "dial tcp 127.0.0.1:3164: connect: connection refused",
"error": "dial tcp 127.0.0.1:6803: connect: connection refused",
"request": "GET /not-running HTTP/1.1",
"request_method": "GET",
"status": float64(502), // All numbers in JSON are floating point
"upstream_addr": "127.0.0.1:3164",
"upstream_addr": "127.0.0.1:6803",
}))
Expect(logDetails.Timestamp).To(BeTemporally("~", time.Now(), time.Second))
})

It("should log and return a 504 if the connection times out in the configured time", func() {
err := startRouter(3167, 3166, []string{"ROUTER_BACKEND_CONNECT_TIMEOUT=0.3s"})
err := startRouter(3167, 3166, []string{"ROUTER_BACKEND_CONNECT_TIMEOUT=0.3s", "BACKEND_URL_black-hole=http://240.0.0.0:1234/"})
Expect(err).NotTo(HaveOccurred())
defer stopRouter(3167)

addBackend("black-hole", "http://240.0.0.0:1234/")
addRoute("/should-time-out", NewBackendRoute("black-hole"))
reloadRoutes(3166)

Expand Down Expand Up @@ -74,14 +72,12 @@ var _ = Describe("Functioning as a reverse proxy", func() {
var tarpit1, tarpit2 *httptest.Server

BeforeEach(func() {
err := startRouter(3167, 3166, []string{"ROUTER_BACKEND_HEADER_TIMEOUT=0.3s"})
err := startRouter(3167, 3166, []string{"ROUTER_BACKEND_HEADER_TIMEOUT=0.3s", "BACKEND_URL_slow-1=http://127.0.0.1:6256/", "BACKEND_URL_slow-2=http://127.0.0.1:6253/"})
Expect(err).NotTo(HaveOccurred())
tarpit1 = startTarpitBackend(time.Second)
tarpit2 = startTarpitBackend(100*time.Millisecond, 500*time.Millisecond)
addBackend("tarpit1", tarpit1.URL)
addBackend("tarpit2", tarpit2.URL)
addRoute("/tarpit1", NewBackendRoute("tarpit1"))
addRoute("/tarpit2", NewBackendRoute("tarpit2"))
tarpit1 = startTarpitBackend("127.0.0.1:6256", time.Second)
tarpit2 = startTarpitBackend("127.0.0.1:6253", 100*time.Millisecond, 500*time.Millisecond)
addRoute("/tarpit1", NewBackendRoute("slow-1"))
addRoute("/tarpit2", NewBackendRoute("slow-2"))
reloadRoutes(3166)
})

Expand Down Expand Up @@ -118,8 +114,7 @@ var _ = Describe("Functioning as a reverse proxy", func() {

Describe("header handling", func() {
BeforeEach(func() {
recorder = startRecordingBackend()
addBackend("backend", recorder.URL())
recorder = startRecordingBackend(false, backends["backend"])
addRoute("/foo", NewBackendRoute("backend", "prefix"))
reloadRoutes(apiPort)
})
Expand Down Expand Up @@ -242,8 +237,7 @@ var _ = Describe("Functioning as a reverse proxy", func() {

Describe("request verb, path, query and body handling", func() {
BeforeEach(func() {
recorder = startRecordingBackend()
addBackend("backend", recorder.URL())
recorder = startRecordingBackend(false, backends["backend"])
addRoute("/foo", NewBackendRoute("backend", "prefix"))
reloadRoutes(apiPort)
})
Expand Down Expand Up @@ -298,18 +292,20 @@ var _ = Describe("Functioning as a reverse proxy", func() {

Describe("handling a backend with a non '/' path", func() {
BeforeEach(func() {
recorder = startRecordingBackend()
addBackend("backend", recorder.URL()+"/something")
addRoute("/foo/bar", NewBackendRoute("backend", "prefix"))
reloadRoutes(apiPort)
err := startRouter(3167, 3166, []string{"ROUTER_TLS_SKIP_VERIFY=1", "BACKEND_URL_with-path=http://127.0.0.1:6804/something"})
Expect(err).NotTo(HaveOccurred())
recorder = startRecordingBackend(false, backends["with-path"])
addRoute("/foo/bar", NewBackendRoute("with-path", "prefix"))
reloadRoutes(3166)
})

AfterEach(func() {
recorder.Close()
stopRouter(3167)
})

It("should merge the 2 paths", func() {
resp := routerRequest(routerPort, "/foo/bar")
resp := routerRequest(3167, "/foo/bar")
Expect(resp.StatusCode).To(Equal(200))

Expect(recorder.ReceivedRequests()).To(HaveLen(1))
Expand All @@ -318,7 +314,7 @@ var _ = Describe("Functioning as a reverse proxy", func() {
})

It("should preserve the request query string", func() {
resp := routerRequest(routerPort, "/foo/bar?baz=qux")
resp := routerRequest(3167, "/foo/bar?baz=qux")
Expect(resp.StatusCode).To(Equal(200))

Expect(recorder.ReceivedRequests()).To(HaveLen(1))
Expand All @@ -329,8 +325,7 @@ var _ = Describe("Functioning as a reverse proxy", func() {

Describe("handling HTTP/1.0 requests", func() {
BeforeEach(func() {
recorder = startRecordingBackend()
addBackend("backend", recorder.URL())
recorder = startRecordingBackend(false, backends["backend"])
addRoute("/foo", NewBackendRoute("backend", "prefix"))
reloadRoutes(apiPort)
})
Expand Down Expand Up @@ -362,10 +357,9 @@ var _ = Describe("Functioning as a reverse proxy", func() {

Describe("handling requests to a HTTPS backend", func() {
BeforeEach(func() {
err := startRouter(3167, 3166, []string{"ROUTER_TLS_SKIP_VERIFY=1"})
err := startRouter(3167, 3166, []string{"ROUTER_TLS_SKIP_VERIFY=1", "BACKEND_URL_backend=https://127.0.0.1:2486"})
Expect(err).NotTo(HaveOccurred())
recorder = startRecordingTLSBackend()
addBackend("backend", recorder.URL())
recorder = startRecordingBackend(true, "127.0.0.1:2486")
addRoute("/foo", NewBackendRoute("backend", "prefix"))
reloadRoutes(3166)
})
Expand Down
3 changes: 1 addition & 2 deletions integration_tests/redirect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,7 @@ var _ = Describe("Redirection", func() {
var recorder *ghttp.Server

BeforeEach(func() {
recorder = startRecordingBackend()
addBackend("be", recorder.URL())
recorder = startRecordingBackend(false, backends["be"])
addRoute("/guidance/keeping-a-pet-pig-or-micropig", NewBackendRoute("be", "exact"))
addRoute("/GUIDANCE/keeping-a-pet-pig-or-micropig", NewBackendRoute("be", "exact"))
reloadRoutes(apiPort)
Expand Down
6 changes: 0 additions & 6 deletions integration_tests/route_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"time"

"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"

// revive:disable:dot-imports
. "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -91,11 +90,6 @@ func initRouteHelper() error {
return nil
}

func addBackend(id, url string) {
err := routerDB.C("backends").Insert(bson.M{"backend_id": id, "backend_url": url})
Expect(err).NotTo(HaveOccurred())
}

func addRoute(path string, route Route) {
route.IncomingPath = path

Expand Down
38 changes: 2 additions & 36 deletions integration_tests/route_loading_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package integration

import (
"fmt"
"net/http/httptest"

. "github.com/onsi/ginkgo/v2"
Expand All @@ -12,14 +11,11 @@ var _ = Describe("loading routes from the db", func() {
var (
backend1 *httptest.Server
backend2 *httptest.Server
backend3 *httptest.Server
)

BeforeEach(func() {
backend1 = startSimpleBackend("backend 1")
backend2 = startSimpleBackend("backend 2")
addBackend("backend-1", backend1.URL)
addBackend("backend-2", backend2.URL)
backend1 = startSimpleBackend("backend 1", backends["backend-1"])
backend2 = startSimpleBackend("backend 2", backends["backend-2"])
})
AfterEach(func() {
backend1.Close()
Expand Down Expand Up @@ -73,34 +69,4 @@ var _ = Describe("loading routes from the db", func() {
Expect(readBody(resp)).To(Equal("backend 1"))
})
})

Context("a backend an env var overriding the backend_url", func() {
BeforeEach(func() {
// This tests the behaviour of backend.ParseURL overriding the backend_url
// provided in the DB with the value of an env var
blackHole := "240.0.0.0/foo"
backend3 = startSimpleBackend("backend 3")
addBackend("backend-3", blackHole)

stopRouter(routerPort)
err := startRouter(routerPort, apiPort, []string{fmt.Sprintf("BACKEND_URL_backend-3=%s", backend3.URL)})
Expect(err).NotTo(HaveOccurred())

addRoute("/oof", NewBackendRoute("backend-3"))
reloadRoutes(apiPort)
})

AfterEach(func() {
stopRouter(routerPort)
err := startRouter(routerPort, apiPort, nil)
Expect(err).NotTo(HaveOccurred())
backend3.Close()
})

It("should send requests to the backend_url provided in the env var", func() {
resp := routerRequest(routerPort, "/oof")
Expect(resp.StatusCode).To(Equal(200))
Expect(readBody(resp)).To(Equal("backend 3"))
})
})
})
Loading