Skip to content

Commit

Permalink
Merge branch 'routing_refactor' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
marcosnils committed Jul 18, 2017
2 parents 8aa95b7 + 4731d8e commit 4eda861
Show file tree
Hide file tree
Showing 24 changed files with 1,442 additions and 502 deletions.
30 changes: 30 additions & 0 deletions Dockerfile.l2
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM golang:1.8

# Copy the runtime dockerfile into the context as Dockerfile
COPY . /go/src/github.com/play-with-docker/play-with-docker

WORKDIR /go/src/github.com/play-with-docker/play-with-docker

RUN go get -v -d ./...

RUN ssh-keygen -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key >/dev/null

WORKDIR /go/src/github.com/play-with-docker/play-with-docker/router/l2

RUN CGO_ENABLED=0 go build -a -installsuffix nocgo -o /go/bin/play-with-docker-l2 .


FROM alpine

RUN apk --update add ca-certificates
RUN mkdir /app

COPY --from=0 /go/bin/play-with-docker-l2 /app/play-with-docker-l2
COPY --from=0 /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key

WORKDIR /app
CMD ["./play-with-docker-l2", "-ssh_key_path", "/etc/ssh/ssh_host_rsa_key"]

EXPOSE 22
EXPOSE 53
EXPOSE 443
49 changes: 10 additions & 39 deletions api.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package main

import (
"fmt"
"log"
"net/http"
"os"
"time"

"github.com/googollee/go-socket.io"
gh "github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/miekg/dns"
"github.com/play-with-docker/play-with-docker/config"
"github.com/play-with-docker/play-with-docker/handlers"
"github.com/play-with-docker/play-with-docker/templates"
Expand All @@ -18,47 +17,26 @@ import (
)

func main() {

config.ParseFlags()
handlers.Bootstrap()

bypassCaptcha := len(os.Getenv("GOOGLE_RECAPTCHA_DISABLED")) > 0

// Start the DNS server
dns.HandleFunc(".", handlers.DnsRequest)
udpDnsServer := &dns.Server{Addr: ":53", Net: "udp"}
go func() {
err := udpDnsServer.ListenAndServe()
if err != nil {
log.Fatal(err)
}
}()
tcpDnsServer := &dns.Server{Addr: ":53", Net: "tcp"}
go func() {
err := tcpDnsServer.ListenAndServe()
if err != nil {
log.Fatal(err)
}
}()
server, err := socketio.NewServer(nil)
if err != nil {
log.Fatal(err)
}
server.On("connection", handlers.WS)
server.On("error", handlers.WSError)

server := handlers.Broadcast.GetHandler()
handlers.RegisterEvents(server)

r := mux.NewRouter()
corsRouter := mux.NewRouter()

// Reverse proxy (needs to be the first route, to make sure it is the first thing we check)
//proxyHandler := handlers.NewMultipleHostReverseProxy()
//websocketProxyHandler := handlers.NewMultipleHostWebsocketReverseProxy()

tcpHandler := handlers.NewTCPProxy()

corsHandler := gh.CORS(gh.AllowCredentials(), gh.AllowedHeaders([]string{"x-requested-with", "content-type"}), gh.AllowedOrigins([]string{"*"}))

// Specific routes
r.Host(fmt.Sprintf("{subdomain:.*}pwd{node:%s}-{port:%s}.{tld:.*}", config.PWDHostnameRegex, config.PortRegex)).Handler(tcpHandler)
r.Host(fmt.Sprintf("{subdomain:.*}pwd{node:%s}.{tld:.*}", config.PWDHostnameRegex)).Handler(tcpHandler)
r.Host(fmt.Sprintf("pwd{alias:%s}-{session:%s}-{port:%s}.{tld:.*}", config.AliasnameRegex, config.AliasSessionRegex, config.PortRegex)).Handler(tcpHandler)
r.Host(fmt.Sprintf("pwd{alias:%s}-{session:%s}.{tld:.*}", config.AliasnameRegex, config.AliasSessionRegex)).Handler(tcpHandler)
r.HandleFunc("/ping", handlers.Ping).Methods("GET")
corsRouter.HandleFunc("/instances/images", handlers.GetInstanceImages).Methods("GET")
corsRouter.HandleFunc("/sessions/{sessionId}", handlers.GetSession).Methods("GET")
Expand Down Expand Up @@ -106,13 +84,6 @@ func main() {
ReadHeaderTimeout: 5 * time.Second,
}

go func() {
log.Println("Listening on port " + config.PortNumber)
log.Fatal(httpServer.ListenAndServe())
}()

go handlers.ListenSSHProxy("0.0.0.0:1022")

// Now listen for TLS connections that need to be proxied
handlers.StartTLSProxy(config.SSLPortNumber)
log.Println("Listening on port " + config.PortNumber)
log.Fatal(httpServer.ListenAndServe())
}
8 changes: 6 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"flag"
"log"
"os"
"regexp"
"time"
Expand All @@ -13,14 +14,14 @@ const (
AliasnameRegex = "[0-9|a-z|A-Z|-]*"
AliasSessionRegex = "[0-9|a-z|A-Z]{8}"
AliasGroupRegex = "(" + AliasnameRegex + ")-(" + AliasSessionRegex + ")"
PWDHostPortGroupRegex = "^.*pwd(" + PWDHostnameRegex + ")(?:-?(" + PortRegex + "))?\\..*$"
PWDHostPortGroupRegex = "^.*ip(" + PWDHostnameRegex + ")(?:-?(" + PortRegex + "))?(?:\\..*)?$"
AliasPortGroupRegex = "^.*pwd" + AliasGroupRegex + "(?:-?(" + PortRegex + "))?\\..*$"
)

var NameFilter = regexp.MustCompile(PWDHostPortGroupRegex)
var AliasFilter = regexp.MustCompile(AliasPortGroupRegex)

var SSLPortNumber, PortNumber, Key, Cert, SessionsFile, PWDContainerName, PWDCName, HashKey string
var SSLPortNumber, PortNumber, Key, Cert, SessionsFile, PWDContainerName, PWDCName, HashKey, SSHKeyPath string
var MaxLoadAvg float64

func ParseFlags() {
Expand All @@ -33,7 +34,10 @@ func ParseFlags() {
flag.StringVar(&PWDCName, "cname", "host1", "CNAME given to this host")
flag.StringVar(&HashKey, "hash_key", "salmonrosado", "Hash key to use for cookies")
flag.Float64Var(&MaxLoadAvg, "maxload", 100, "Maximum allowed load average before failing ping requests")
flag.StringVar(&SSHKeyPath, "ssh_key_path", "", "SSH Private Key to use")
flag.Parse()

log.Println("*****************************", SSHKeyPath)
}
func GetDindImageName() string {
dindImage := os.Getenv("DIND_IMAGE")
Expand Down
25 changes: 25 additions & 0 deletions event/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package event

type EventType string

func (e EventType) String() string {
return string(e)
}

const INSTANCE_VIEWPORT_RESIZE EventType = "instance viewport resize"
const INSTANCE_DELETE EventType = "instance delete"
const INSTANCE_NEW EventType = "instance new"
const INSTANCE_STATS EventType = "instance stats"
const INSTANCE_TERMINAL_OUT EventType = "instance terminal out"
const SESSION_END EventType = "session end"
const SESSION_READY EventType = "session ready"
const SESSION_BUILDER_OUT EventType = "session builder out"

type Handler func(sessionId string, args ...interface{})
type AnyHandler func(eventType EventType, sessionId string, args ...interface{})

type EventApi interface {
Emit(name EventType, sessionId string, args ...interface{})
On(name EventType, handler Handler)
OnAny(handler AnyHandler)
}
47 changes: 47 additions & 0 deletions event/local_broker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package event

import "sync"

type localBroker struct {
sync.Mutex

handlers map[EventType][]Handler
anyHandlers []AnyHandler
}

func NewLocalBroker() *localBroker {
return &localBroker{handlers: map[EventType][]Handler{}, anyHandlers: []AnyHandler{}}
}

func (b *localBroker) On(name EventType, handler Handler) {
b.Lock()
defer b.Unlock()

if b.handlers[name] == nil {
b.handlers[name] = []Handler{}
}
b.handlers[name] = append(b.handlers[name], handler)
}

func (b *localBroker) OnAny(handler AnyHandler) {
b.Lock()
defer b.Unlock()

b.anyHandlers = append(b.anyHandlers, handler)
}

func (b *localBroker) Emit(name EventType, sessionId string, args ...interface{}) {
go func() {
b.Lock()
defer b.Unlock()

for _, handler := range b.anyHandlers {
handler(name, sessionId, args...)
}
if b.handlers[name] != nil {
for _, handler := range b.handlers[name] {
handler(sessionId, args...)
}
}
}()
}
60 changes: 60 additions & 0 deletions event/local_broker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package event

import (
"sync"
"testing"

"github.com/stretchr/testify/assert"
)

func TestLocalBroker_On(t *testing.T) {
broker := NewLocalBroker()

called := 0
receivedSessionId := ""
receivedArgs := []interface{}{}

wg := sync.WaitGroup{}
wg.Add(1)

broker.On(INSTANCE_NEW, func(sessionId string, args ...interface{}) {
called++
receivedSessionId = sessionId
receivedArgs = args
wg.Done()
})
broker.Emit(SESSION_READY, "1")
broker.Emit(INSTANCE_NEW, "2", "foo", "bar")

wg.Wait()

assert.Equal(t, 1, called)
assert.Equal(t, "2", receivedSessionId)
assert.Equal(t, []interface{}{"foo", "bar"}, receivedArgs)
}

func TestLocalBroker_OnAny(t *testing.T) {
broker := NewLocalBroker()

var receivedEvent EventType
receivedSessionId := ""
receivedArgs := []interface{}{}

wg := sync.WaitGroup{}
wg.Add(1)

broker.OnAny(func(eventType EventType, sessionId string, args ...interface{}) {
receivedSessionId = sessionId
receivedArgs = args
receivedEvent = eventType
wg.Done()
})
broker.Emit(SESSION_READY, "1")

wg.Wait()

var expectedArgs []interface{}
assert.Equal(t, SESSION_READY, receivedEvent)
assert.Equal(t, "1", receivedSessionId)
assert.Equal(t, expectedArgs, receivedArgs)
}
23 changes: 16 additions & 7 deletions handlers/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import (
"os"

"github.com/docker/docker/client"
"github.com/googollee/go-socket.io"
"github.com/play-with-docker/play-with-docker/config"
"github.com/play-with-docker/play-with-docker/docker"
"github.com/play-with-docker/play-with-docker/event"
"github.com/play-with-docker/play-with-docker/pwd"
"github.com/play-with-docker/play-with-docker/storage"
)

var core pwd.PWDApi
var Broadcast pwd.BroadcastApi
var e event.EventApi
var ws *socketio.Server

func Bootstrap() {
c, err := client.NewEnvClient()
Expand All @@ -22,18 +25,24 @@ func Bootstrap() {

d := docker.NewDocker(c)

Broadcast, err = pwd.NewBroadcast(WS, WSError)
if err != nil {
log.Fatal(err)
}
e = event.NewLocalBroker()

t := pwd.NewScheduler(Broadcast, d)
t := pwd.NewScheduler(e, d)

s, err := storage.NewFileStorage(config.SessionsFile)

if err != nil && !os.IsNotExist(err) {
log.Fatal("Error initializing StorageAPI: ", err)
}
core = pwd.NewPWD(d, t, Broadcast, s)
core = pwd.NewPWD(d, t, e, s)

}

func RegisterEvents(s *socketio.Server) {
ws = s
e.OnAny(broadcastEvent)
}

func broadcastEvent(eventType event.EventType, sessionId string, args ...interface{}) {
ws.BroadcastTo(sessionId, eventType.String(), args...)
}
Loading

0 comments on commit 4eda861

Please sign in to comment.