Set session host

Add ping to L2 router
This commit is contained in:
Jonathan Leibiusky @xetorthio
2017-08-08 10:10:52 -03:00
parent ed7cefcf9c
commit 0e9716d5b0
9 changed files with 76 additions and 61 deletions

View File

@@ -20,7 +20,7 @@ const (
var NameFilter = regexp.MustCompile(PWDHostPortGroupRegex) var NameFilter = regexp.MustCompile(PWDHostPortGroupRegex)
var AliasFilter = regexp.MustCompile(AliasPortGroupRegex) var AliasFilter = regexp.MustCompile(AliasPortGroupRegex)
var SSLPortNumber, PortNumber, Key, Cert, SessionsFile, PWDContainerName, L2ContainerName, L2Subdomain, PWDCName, HashKey, SSHKeyPath string var SSLPortNumber, PortNumber, Key, Cert, SessionsFile, PWDContainerName, L2ContainerName, L2Subdomain, PWDCName, HashKey, SSHKeyPath, L2RouterIP string
var MaxLoadAvg float64 var MaxLoadAvg float64
func ParseFlags() { func ParseFlags() {
@@ -31,6 +31,7 @@ func ParseFlags() {
flag.StringVar(&SessionsFile, "save", "./pwd/sessions", "Tell where to store sessions file") flag.StringVar(&SessionsFile, "save", "./pwd/sessions", "Tell where to store sessions file")
flag.StringVar(&PWDContainerName, "name", "pwd", "Container name used to run PWD (used to be able to connect it to the networks it creates)") flag.StringVar(&PWDContainerName, "name", "pwd", "Container name used to run PWD (used to be able to connect it to the networks it creates)")
flag.StringVar(&L2ContainerName, "l2", "l2", "Container name used to run L2 Router") flag.StringVar(&L2ContainerName, "l2", "l2", "Container name used to run L2 Router")
flag.StringVar(&L2RouterIP, "l2-ip", "", "Host IP address for L2 router ping response")
flag.StringVar(&L2Subdomain, "l2-subdomain", "direct", "Subdomain to the L2 Router") flag.StringVar(&L2Subdomain, "l2-subdomain", "direct", "Subdomain to the L2 Router")
flag.StringVar(&PWDCName, "cname", "", "CNAME given to this host") flag.StringVar(&PWDCName, "cname", "", "CNAME given to this host")
flag.StringVar(&HashKey, "hash_key", "salmonrosado", "Hash key to use for cookies") flag.StringVar(&HashKey, "hash_key", "salmonrosado", "Hash key to use for cookies")

View File

@@ -33,6 +33,7 @@ type DockerApi interface {
CreateNetwork(id string) error CreateNetwork(id string) error
ConnectNetwork(container, network, ip string) (string, error) ConnectNetwork(container, network, ip string) (string, error)
GetDaemonInfo() (types.Info, error) GetDaemonInfo() (types.Info, error)
GetDaemonHost() string
GetSwarmPorts() ([]string, []uint16, error) GetSwarmPorts() ([]string, []uint16, error)
GetPorts() ([]uint16, error) GetPorts() ([]uint16, error)
GetContainerStats(name string) (io.ReadCloser, error) GetContainerStats(name string) (io.ReadCloser, error)
@@ -102,6 +103,10 @@ func (d *docker) GetDaemonInfo() (types.Info, error) {
return d.c.Info(context.Background()) return d.c.Info(context.Background())
} }
func (d *docker) GetDaemonHost() string {
return d.c.DaemonHost()
}
func (d *docker) GetSwarmPorts() ([]string, []uint16, error) { func (d *docker) GetSwarmPorts() ([]string, []uint16, error) {
hosts := []string{} hosts := []string{}
ports := []uint16{} ports := []uint16{}

View File

@@ -28,6 +28,11 @@ func (m *Mock) GetDaemonInfo() (types.Info, error) {
return args.Get(0).(types.Info), args.Error(1) return args.Get(0).(types.Info), args.Error(1)
} }
func (m *Mock) GetDaemonHost() string {
args := m.Called()
return args.String(0)
}
func (m *Mock) GetSwarmPorts() ([]string, []uint16, error) { func (m *Mock) GetSwarmPorts() ([]string, []uint16, error) {
args := m.Called() args := m.Called()
return args.Get(0).([]string), args.Get(1).([]uint16), args.Error(2) return args.Get(0).([]string), args.Get(1).([]uint16), args.Error(2)

View File

@@ -1,23 +1,6 @@
package handlers package handlers
import ( import "net/http"
"log"
"net/http"
"github.com/play-with-docker/play-with-docker/config"
"github.com/shirou/gopsutil/load"
)
func Ping(rw http.ResponseWriter, req *http.Request) { func Ping(rw http.ResponseWriter, req *http.Request) {
// Get system load average of the last 5 minutes and compare it against a threashold.
a, err := load.Avg()
if err != nil {
log.Println("Cannot get system load average!", err)
} else {
if a.Load5 > config.MaxLoadAvg {
log.Printf("System load average is too high [%f]\n", a.Load5)
rw.WriteHeader(http.StatusInsufficientStorage)
}
}
} }

View File

@@ -23,6 +23,7 @@ func TestClientNew(t *testing.T) {
_g.On("NewId").Return("aaaabbbbcccc") _g.On("NewId").Return("aaaabbbbcccc")
_f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil) _f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil)
_d.On("CreateNetwork", "aaaabbbbcccc").Return(nil) _d.On("CreateNetwork", "aaaabbbbcccc").Return(nil)
_d.On("GetDaemonHost").Return("localhost")
_d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil)
_s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil)
_s.On("SessionCount").Return(1, nil) _s.On("SessionCount").Return(1, nil)
@@ -59,6 +60,7 @@ func TestClientCount(t *testing.T) {
_g.On("NewId").Return("aaaabbbbcccc") _g.On("NewId").Return("aaaabbbbcccc")
_f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil) _f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil)
_d.On("CreateNetwork", "aaaabbbbcccc").Return(nil) _d.On("CreateNetwork", "aaaabbbbcccc").Return(nil)
_d.On("GetDaemonHost").Return("localhost")
_d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil)
_s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil)
_s.On("SessionCount").Return(1, nil) _s.On("SessionCount").Return(1, nil)
@@ -93,6 +95,7 @@ func TestClientResizeViewPort(t *testing.T) {
_g.On("NewId").Return("aaaabbbbcccc") _g.On("NewId").Return("aaaabbbbcccc")
_f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil) _f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil)
_d.On("CreateNetwork", "aaaabbbbcccc").Return(nil) _d.On("CreateNetwork", "aaaabbbbcccc").Return(nil)
_d.On("GetDaemonHost").Return("localhost")
_d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil)
_s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil)
_s.On("SessionCount").Return(1, nil) _s.On("SessionCount").Return(1, nil)

View File

@@ -47,6 +47,7 @@ func TestInstanceNew(t *testing.T) {
_g.On("NewId").Return("aaaabbbbcccc") _g.On("NewId").Return("aaaabbbbcccc")
_f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil) _f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil)
_d.On("CreateNetwork", "aaaabbbbcccc").Return(nil) _d.On("CreateNetwork", "aaaabbbbcccc").Return(nil)
_d.On("GetDaemonHost").Return("localhost")
_d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil)
_s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil)
_s.On("SessionCount").Return(1, nil) _s.On("SessionCount").Return(1, nil)
@@ -69,6 +70,7 @@ func TestInstanceNew(t *testing.T) {
IsDockerHost: true, IsDockerHost: true,
SessionId: session.Id, SessionId: session.Id,
Session: session, Session: session,
SessionHost: session.Host,
ProxyHost: router.EncodeHost(session.Id, "10.0.0.1", router.HostOpts{}), ProxyHost: router.EncodeHost(session.Id, "10.0.0.1", router.HostOpts{}),
} }
expectedContainerOpts := docker.CreateContainerOpts{ expectedContainerOpts := docker.CreateContainerOpts{
@@ -109,6 +111,7 @@ func TestInstanceNew_WithNotAllowedImage(t *testing.T) {
_g.On("NewId").Return("aaaabbbbcccc") _g.On("NewId").Return("aaaabbbbcccc")
_f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil) _f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil)
_d.On("CreateNetwork", "aaaabbbbcccc").Return(nil) _d.On("CreateNetwork", "aaaabbbbcccc").Return(nil)
_d.On("GetDaemonHost").Return("localhost")
_d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil)
_s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil)
_s.On("SessionCount").Return(1, nil) _s.On("SessionCount").Return(1, nil)
@@ -132,6 +135,7 @@ func TestInstanceNew_WithNotAllowedImage(t *testing.T) {
SessionId: session.Id, SessionId: session.Id,
IsDockerHost: false, IsDockerHost: false,
Session: session, Session: session,
SessionHost: session.Host,
ProxyHost: router.EncodeHost(session.Id, "10.0.0.1", router.HostOpts{}), ProxyHost: router.EncodeHost(session.Id, "10.0.0.1", router.HostOpts{}),
} }
expectedContainerOpts := docker.CreateContainerOpts{ expectedContainerOpts := docker.CreateContainerOpts{
@@ -171,6 +175,7 @@ func TestInstanceNew_WithCustomHostname(t *testing.T) {
_g.On("NewId").Return("aaaabbbbcccc") _g.On("NewId").Return("aaaabbbbcccc")
_f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil) _f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil)
_d.On("CreateNetwork", "aaaabbbbcccc").Return(nil) _d.On("CreateNetwork", "aaaabbbbcccc").Return(nil)
_d.On("GetDaemonHost").Return("localhost")
_d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil)
_s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil)
_s.On("SessionCount").Return(1, nil) _s.On("SessionCount").Return(1, nil)
@@ -192,6 +197,7 @@ func TestInstanceNew_WithCustomHostname(t *testing.T) {
Image: "redis", Image: "redis",
IsDockerHost: false, IsDockerHost: false,
Session: session, Session: session,
SessionHost: session.Host,
SessionId: session.Id, SessionId: session.Id,
ProxyHost: router.EncodeHost(session.Id, "10.0.0.1", router.HostOpts{}), ProxyHost: router.EncodeHost(session.Id, "10.0.0.1", router.HostOpts{}),
} }

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"math" "math"
"net/url"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -61,15 +62,28 @@ func (p *pwd) SessionNew(duration time.Duration, stack, stackName, imageName str
log.Printf("NewSession id=[%s]\n", s.Id) log.Printf("NewSession id=[%s]\n", s.Id)
if err := p.docker(s.Id).CreateNetwork(s.Id); err != nil { dockerClient := p.docker(s.Id)
u, _ := url.Parse(dockerClient.GetDaemonHost())
if u.Host == "" {
s.Host = "localhost"
} else {
chunks := strings.Split(u.Host, ":")
s.Host = chunks[0]
}
if err := dockerClient.CreateNetwork(s.Id); err != nil {
log.Println("ERROR NETWORKING") log.Println("ERROR NETWORKING")
return nil, err return nil, err
} }
log.Printf("Network [%s] created for session [%s]\n", s.Id, s.Id) log.Printf("Network [%s] created for session [%s]\n", s.Id, s.Id)
if err := p.connectToNetwork(s); err != nil { ip, err := dockerClient.ConnectNetwork(config.L2ContainerName, s.Id, s.PwdIpAddress)
if err != nil {
log.Println(err)
return nil, err return nil, err
} }
s.PwdIpAddress = ip
log.Printf("Connected %s to network [%s]\n", config.PWDContainerName, s.Id)
if err := p.storage.SessionPut(s); err != nil { if err := p.storage.SessionPut(s); err != nil {
log.Println(err) log.Println(err)
@@ -278,42 +292,3 @@ func (p *pwd) SessionSetup(session *types.Session, conf SessionSetupConf) error
return nil return nil
} }
/*
// This function should be called any time a session needs to be prepared:
// 1. Like when it is created
// 2. When it was loaded from storage
func (p *pwd) prepareSession(session *types.Session) (bool, error) {
session.Lock()
defer session.Unlock()
if isSessionPrepared(session.Id) {
return false, nil
}
// Connect PWD daemon to the new network
if err := p.connectToNetwork(session); err != nil {
return false, err
}
for _, i := range session.Instances {
// wire the session back to the instance
i.Session = session
go p.InstanceAttachTerminal(i)
}
preparedSessions[session.Id] = true
return true, nil
}
*/
func (p *pwd) connectToNetwork(s *types.Session) error {
ip, err := p.docker(s.Id).ConnectNetwork(config.L2ContainerName, s.Id, s.PwdIpAddress)
if err != nil {
log.Println("ERROR NETWORKING")
return err
}
s.PwdIpAddress = ip
log.Printf("Connected %s to network [%s]\n", config.PWDContainerName, s.Id)
return nil
}

View File

@@ -24,6 +24,7 @@ func TestSessionNew(t *testing.T) {
_g.On("NewId").Return("aaaabbbbcccc") _g.On("NewId").Return("aaaabbbbcccc")
_f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil) _f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil)
_d.On("CreateNetwork", "aaaabbbbcccc").Return(nil) _d.On("CreateNetwork", "aaaabbbbcccc").Return(nil)
_d.On("GetDaemonHost").Return("localhost")
_d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil)
_s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil)
_s.On("SessionCount").Return(1, nil) _s.On("SessionCount").Return(1, nil)
@@ -53,6 +54,7 @@ func TestSessionNew(t *testing.T) {
assert.Equal(t, "stackPath", s.Stack) assert.Equal(t, "stackPath", s.Stack)
assert.Equal(t, "stackName", s.StackName) assert.Equal(t, "stackName", s.StackName)
assert.Equal(t, "imageName", s.ImageName) assert.Equal(t, "imageName", s.ImageName)
assert.Equal(t, "localhost", s.Host)
assert.False(t, s.Ready) assert.False(t, s.Ready)
assert.Equal(t, "10.0.0.1", s.PwdIpAddress) assert.Equal(t, "10.0.0.1", s.PwdIpAddress)
@@ -74,13 +76,14 @@ func TestSessionSetup(t *testing.T) {
_g.On("NewId").Return("aaaabbbbcccc") _g.On("NewId").Return("aaaabbbbcccc")
_f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil) _f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil)
_d.On("CreateNetwork", "aaaabbbbcccc").Return(nil) _d.On("CreateNetwork", "aaaabbbbcccc").Return(nil)
_d.On("GetDaemonHost").Return("localhost")
_d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil)
_s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil)
_s.On("InstanceCreate", "aaaabbbbcccc", mock.AnythingOfType("*types.Instance")).Return(nil) _s.On("InstanceCreate", "aaaabbbbcccc", mock.AnythingOfType("*types.Instance")).Return(nil)
_s.On("SessionCount").Return(1, nil) _s.On("SessionCount").Return(1, nil)
_s.On("InstanceCount").Return(0, nil) _s.On("InstanceCount").Return(0, nil)
_d.On("CreateContainer", docker.CreateContainerOpts{Image: "franela/dind", SessionId: "aaaabbbbcccc", PwdIpAddress: "10.0.0.1", ContainerName: "aaaabbbb_manager1", Hostname: "manager1", Privileged: true}).Return("10.0.0.2", nil) _d.On("CreateContainer", docker.CreateContainerOpts{Image: "franela/dind", SessionId: "aaaabbbbcccc", PwdIpAddress: "10.0.0.1", ContainerName: "aaaabbbb_manager1", Hostname: "manager1", Privileged: true, HostFQDN: "localhost"}).Return("10.0.0.2", nil)
_f.On("GetForInstance", "aaaabbbbcccc", "aaaabbbb_manager1").Return(_d, nil) _f.On("GetForInstance", "aaaabbbbcccc", "aaaabbbb_manager1").Return(_d, nil)
_d.On("SwarmInit").Return(&docker.SwarmTokens{Manager: "managerToken", Worker: "workerToken"}, nil) _d.On("SwarmInit").Return(&docker.SwarmTokens{Manager: "managerToken", Worker: "workerToken"}, nil)
_e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_manager1", "10.0.0.2", "manager1"}).Return() _e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_manager1", "10.0.0.2", "manager1"}).Return()

View File

@@ -6,14 +6,19 @@ import (
"fmt" "fmt"
"log" "log"
"net" "net"
"net/http"
"os" "os"
"time"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/gorilla/mux"
"github.com/play-with-docker/play-with-docker/config" "github.com/play-with-docker/play-with-docker/config"
"github.com/play-with-docker/play-with-docker/router" "github.com/play-with-docker/play-with-docker/router"
"github.com/shirou/gopsutil/load"
"github.com/urfave/negroni"
) )
func director(protocol router.Protocol, host string) (*net.TCPAddr, error) { func director(protocol router.Protocol, host string) (*net.TCPAddr, error) {
@@ -128,7 +133,36 @@ func main() {
} }
go monitorNetworks() go monitorNetworks()
ro := mux.NewRouter()
ro.HandleFunc("/ping", ping).Methods("GET")
n := negroni.Classic()
n.UseHandler(ro)
httpServer := http.Server{
Addr: "0.0.0.0:8080",
Handler: n,
IdleTimeout: 30 * time.Second,
ReadHeaderTimeout: 5 * time.Second,
}
go httpServer.ListenAndServe()
r := router.NewRouter(director, config.SSHKeyPath) r := router.NewRouter(director, config.SSHKeyPath)
r.ListenAndWait(":443", ":53", ":22") r.ListenAndWait(":443", ":53", ":22")
defer r.Close() defer r.Close()
} }
func ping(rw http.ResponseWriter, req *http.Request) {
// Get system load average of the last 5 minutes and compare it against a threashold.
a, err := load.Avg()
if err != nil {
log.Println("Cannot get system load average!", err)
} else {
if a.Load5 > config.MaxLoadAvg {
log.Printf("System load average is too high [%f]\n", a.Load5)
rw.WriteHeader(http.StatusInsufficientStorage)
}
}
fmt.Fprintf(rw, `{"ip": "%s"}`, config.L2RouterIP)
}