Miki Tebeka avatar Miki Tebeka committed ffae64c

seamless

Comments (0)

Files changed (4)

+syntax: glob
+
+seamless
 export GOPATH := $(shell dirname $(shell dirname $(PWD)))
-PACKAGE := seesaw
+PACKAGE := seamless
 
 all:
 	go build $(PACKAGE)
+/* A TCP proxy that allow you to deploy new code then switch traffic to it
+   without downtime.
+
+   Switching server is done with HTTP interface with the following API:
+   /switch?backend=address - will switch traffic to new backend
+   /current - will return (in plain text) current server
+
+   Work flow:
+	   Start first backend at port 4444
+	   Run `./seamless 8080 localhost:4444`
+
+	   Direct all traffic to port 8080 on local machine.
+
+	   When you need to upgrade the backend, start a new one (with new code on
+	   a different port, say 4445).
+	   The `curl http://localhost:6777/switch?backend=localhost:4445. 
+	   New traffic will be directed to new server.
+
+Original forward code by Roger Peppe (see http://bit.ly/Oc1YtF)
+*/
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io"
+	"net"
+	"net/http"
+	"os"
+)
+
+var backend string
+
+func main() {
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, "usage: seamless LISTEN_ADDR BACKEND\n")
+		fmt.Fprintf(os.Stderr, "command line switches:\n")
+		flag.PrintDefaults()
+	}
+	port := flag.Int("httpPort", 6777, "http interface port")
+	flag.Parse()
+	if flag.NArg() != 2 {
+		flag.Usage()
+		os.Exit(1)
+	}
+	localAddr := flag.Arg(0)
+	backend = flag.Arg(1)
+
+	local, err := net.Listen("tcp", localAddr)
+	if local == nil {
+		die(fmt.Sprintf("cannot listen: %v", err))
+	}
+
+	go runHttpServer(*port)
+
+	for {
+		conn, err := local.Accept()
+		if conn == nil {
+			die(fmt.Sprintf("accept failed: %v", err))
+		}
+		go forward(conn, backend)
+	}
+}
+
+func forward(local net.Conn, remoteAddr string) {
+	remote, err := net.Dial("tcp", remoteAddr)
+	if remote == nil {
+		fmt.Fprintf(os.Stderr, "remote dial failed: %v\n", err)
+		return
+	}
+	go io.Copy(local, remote)
+	go io.Copy(remote, local)
+}
+
+func die(msg string) {
+	fmt.Fprintf(os.Stderr, "error: %s\n", msg)
+	os.Exit(1)
+}
+
+func runHttpServer(port int) {
+	http.HandleFunc("/switch", switchHandler)
+	http.HandleFunc("/current", currentHandler)
+	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
+}
+
+func switchHandler(w http.ResponseWriter, req *http.Request) {
+	newBackend := req.FormValue("backend")
+	if len(newBackend) == 0 {
+		http.Error(w, "missing 'backend' parameter", http.StatusBadRequest)
+		return
+	}
+	backend = newBackend
+	currentHandler(w, req)
+}
+
+func currentHandler(w http.ResponseWriter, req *http.Request) {
+	fmt.Fprintf(w, "%s\n", backend)
+}

seesaw.go

-/* Proxy TCP between two servers. Allow you to deploy new code to one server
-   then switch traffic to it without downtime.
-
-   Switching server is done with HTTP interface with the following API:
-   /toggle - will toggle between servers
-   /current - will return (in plain text) current server
-   /servers - will return active, backup servers
-
-Original code by Roger Peppe at http://bit.ly/Oc1YtF
-*/
-package main
-
-import (
-	"flag"
-	"fmt"
-	"io"
-	"net"
-	"net/http"
-	"os"
-)
-
-var remotes = []string{"", ""}
-var current int
-
-func main() {
-	flag.Usage = func() {
-		fmt.Fprintf(os.Stderr, "usage: seesaw LISTEN FIRST SECOND\n")
-		flag.PrintDefaults()
-	}
-	port := flag.Int("httpPort", 6777, "http interface port")
-	flag.Parse()
-	if flag.NArg() != 3 {
-		flag.Usage()
-		os.Exit(1)
-	}
-	localAddr := flag.Arg(0)
-	remotes[0] = flag.Arg(1)
-	remotes[1] = flag.Arg(2)
-
-	local, err := net.Listen("tcp", localAddr)
-	if local == nil {
-		die(fmt.Sprintf("cannot listen: %v", err))
-	}
-
-	go runHttpServer(*port)
-
-	for {
-		conn, err := local.Accept()
-		if conn == nil {
-			die(fmt.Sprintf("accept failed: %v", err))
-		}
-		go forward(conn, remotes[current])
-	}
-}
-
-func forward(local net.Conn, remoteAddr string) {
-	remote, err := net.Dial("tcp", remoteAddr)
-	if remote == nil {
-		fmt.Fprintf(os.Stderr, "remote dial failed: %v\n", err)
-		return
-	}
-	go io.Copy(local, remote)
-	go io.Copy(remote, local)
-}
-
-func die(msg string) {
-	fmt.Fprintf(os.Stderr, "error: %s\n", msg)
-	os.Exit(1)
-}
-
-func runHttpServer(port int) {
-	http.HandleFunc("/toggle", toggleHandler)
-	http.HandleFunc("/current", currentHandler)
-	http.HandleFunc("/servers", serversHandler)
-	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
-}
-
-func toggleHandler(w http.ResponseWriter, req *http.Request) {
-	current = 1 - current
-	currentHandler(w, req)
-}
-
-func currentHandler(w http.ResponseWriter, req *http.Request) {
-	fmt.Fprintf(w, "%s\n", remotes[current])
-}
-
-func serversHandler(w http.ResponseWriter, req *http.Request) {
-	fmt.Fprintf(w, "%s\n", remotes[current])
-	fmt.Fprintf(w, "%s\n", remotes[1-current])
-}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.