2016-02-27 16:21:49 -06:00
|
|
|
/*
|
2017-01-25 17:59:25 -06:00
|
|
|
Copyright 2016-2017 gtalent2@gmail.com
|
2016-02-27 16:21:49 -06:00
|
|
|
|
|
|
|
This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
2016-02-27 16:10:44 -06:00
|
|
|
package main
|
|
|
|
|
2016-03-04 00:13:43 -06:00
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"strconv"
|
2017-01-27 14:49:58 -06:00
|
|
|
"time"
|
2016-03-04 00:13:43 -06:00
|
|
|
)
|
2016-02-27 16:10:44 -06:00
|
|
|
|
|
|
|
const (
|
|
|
|
SERVERMANAGER_SPINUP = iota
|
|
|
|
SERVERMANAGER_SPINDOWN
|
|
|
|
SERVERMANAGER_STOP
|
|
|
|
)
|
|
|
|
|
2017-01-21 16:02:41 -06:00
|
|
|
type serverManagerEvent struct {
|
|
|
|
eventType int
|
|
|
|
tcpConn *net.TCPConn
|
|
|
|
}
|
|
|
|
|
2016-02-29 00:06:31 -06:00
|
|
|
type ServerHandler interface {
|
|
|
|
// Takes snapshot name, and returns the IP to connect to.
|
|
|
|
Spinup(name string) (string, error)
|
|
|
|
Spindown(name string) error
|
|
|
|
}
|
|
|
|
|
2016-02-27 16:10:44 -06:00
|
|
|
type ServerManager struct {
|
2017-01-27 14:49:58 -06:00
|
|
|
name string
|
|
|
|
ports []int
|
|
|
|
in chan serverManagerEvent
|
2017-01-28 00:04:04 -06:00
|
|
|
done chan int
|
2017-01-27 14:49:58 -06:00
|
|
|
connStatus chan ConnStatus
|
|
|
|
lastKeepAliveTime time.Time
|
|
|
|
server ServerHandler
|
2017-01-28 21:28:16 -06:00
|
|
|
activityTimeout time.Duration
|
2016-02-27 16:10:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewServerManager(name string, server ServerHandler, settings Settings) *ServerManager {
|
|
|
|
sm := new(ServerManager)
|
|
|
|
|
2016-02-27 16:34:20 -06:00
|
|
|
sm.name = name
|
2016-02-28 01:03:16 -06:00
|
|
|
sm.ports = settings.Servers[name].Ports
|
2017-01-21 16:02:41 -06:00
|
|
|
sm.in = make(chan serverManagerEvent)
|
2017-01-28 00:04:04 -06:00
|
|
|
sm.done = make(chan int)
|
|
|
|
sm.connStatus = make(chan ConnStatus)
|
2016-02-27 16:10:44 -06:00
|
|
|
sm.server = server
|
2017-01-27 18:06:27 -06:00
|
|
|
sm.lastKeepAliveTime = time.Now()
|
2016-02-27 16:10:44 -06:00
|
|
|
|
2017-01-28 21:28:16 -06:00
|
|
|
activityTimeout, err := time.ParseDuration(settings.Servers[sm.name].ActivityTimeout)
|
|
|
|
if err != nil { // invalid timeout, default to 5 minutes
|
|
|
|
activityTimeout = time.Duration(5 * time.Minute)
|
|
|
|
}
|
|
|
|
sm.activityTimeout = activityTimeout
|
|
|
|
log.Println("ServerManager: ", name, " has activity timeout of ", sm.activityTimeout.String())
|
|
|
|
|
2016-02-27 16:10:44 -06:00
|
|
|
return sm
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Serves channel requests.
|
|
|
|
*/
|
|
|
|
func (me *ServerManager) Serve() {
|
2016-02-27 16:45:20 -06:00
|
|
|
// TODO: see if server is currently up, and setup port forwarding if so
|
|
|
|
|
2017-01-28 21:28:16 -06:00
|
|
|
ticker := time.NewTicker(1 * time.Minute)
|
2017-01-27 14:49:58 -06:00
|
|
|
|
2016-02-27 16:45:20 -06:00
|
|
|
// event loop
|
2016-02-27 16:10:44 -06:00
|
|
|
for running := true; running; {
|
|
|
|
select {
|
2017-01-27 14:49:58 -06:00
|
|
|
case status := <-me.connStatus:
|
|
|
|
if status.Status == CONN_ACTIVE {
|
|
|
|
me.lastKeepAliveTime = time.Now()
|
|
|
|
}
|
2016-02-27 16:10:44 -06:00
|
|
|
case action := <-me.in:
|
|
|
|
running = me.serveAction(action)
|
2017-01-27 14:49:58 -06:00
|
|
|
case <-ticker.C:
|
2017-01-28 21:28:16 -06:00
|
|
|
if time.Since(me.lastKeepAliveTime) > me.activityTimeout {
|
2017-01-27 18:06:27 -06:00
|
|
|
running = me.serveAction(serverManagerEvent{eventType: SERVERMANAGER_SPINDOWN})
|
2017-01-27 14:49:58 -06:00
|
|
|
}
|
2016-02-27 16:10:44 -06:00
|
|
|
}
|
|
|
|
}
|
2016-02-27 16:45:20 -06:00
|
|
|
|
2017-01-27 15:28:16 -06:00
|
|
|
ticker.Stop()
|
|
|
|
|
2016-02-27 16:45:20 -06:00
|
|
|
// notify done
|
2017-01-28 00:04:04 -06:00
|
|
|
me.done <- 0
|
2016-02-27 16:10:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Sends the serve loop a spinup message.
|
|
|
|
*/
|
2017-01-21 16:02:41 -06:00
|
|
|
func (me *ServerManager) Spinup(c *net.TCPConn) {
|
|
|
|
me.in <- serverManagerEvent{eventType: SERVERMANAGER_SPINUP, tcpConn: c}
|
2016-02-27 16:10:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Sends the serve loop a spindown message.
|
|
|
|
*/
|
2017-01-27 14:49:58 -06:00
|
|
|
func (me *ServerManager) Spindown() {
|
2017-01-21 16:02:41 -06:00
|
|
|
me.in <- serverManagerEvent{eventType: SERVERMANAGER_SPINDOWN}
|
2016-02-27 16:10:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Sends the serve loop a quit message.
|
|
|
|
*/
|
|
|
|
func (me *ServerManager) Stop() {
|
2017-01-21 16:02:41 -06:00
|
|
|
me.in <- serverManagerEvent{eventType: SERVERMANAGER_STOP}
|
2016-02-27 16:10:44 -06:00
|
|
|
}
|
|
|
|
|
2016-02-27 16:34:20 -06:00
|
|
|
func (me *ServerManager) Done() {
|
|
|
|
<-me.done
|
|
|
|
}
|
|
|
|
|
2016-03-04 00:13:43 -06:00
|
|
|
func (me *ServerManager) setupListener(port int) {
|
|
|
|
portStr := strconv.Itoa(port)
|
|
|
|
addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+portStr)
|
|
|
|
if err != nil {
|
|
|
|
log.Print("Could not resolve port and listen address:", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// listen on port
|
|
|
|
go func() {
|
|
|
|
l, err := net.ListenTCP("tcp", addr)
|
|
|
|
if err != nil {
|
|
|
|
log.Print("Could not listen for TCP connection:", err)
|
|
|
|
} else {
|
|
|
|
for {
|
|
|
|
conn, err := l.AcceptTCP()
|
|
|
|
if err != nil {
|
|
|
|
log.Print("Could not accept TCP connection:", err)
|
2017-01-27 14:49:58 -06:00
|
|
|
} else { // connection accepted
|
2016-03-04 00:13:43 -06:00
|
|
|
// spinup machine
|
2017-01-21 16:02:41 -06:00
|
|
|
me.Spinup(conn)
|
2016-03-04 00:13:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2017-01-21 16:02:41 -06:00
|
|
|
func (me *ServerManager) serveAction(event serverManagerEvent) bool {
|
2016-02-27 16:10:44 -06:00
|
|
|
running := true
|
2017-01-21 16:02:41 -06:00
|
|
|
switch event.eventType {
|
2016-02-27 16:10:44 -06:00
|
|
|
case SERVERMANAGER_SPINUP:
|
2017-01-21 16:02:41 -06:00
|
|
|
targetIp, err := me.server.Spinup(me.name)
|
2017-01-27 18:06:27 -06:00
|
|
|
me.lastKeepAliveTime = time.Now()
|
2016-02-27 16:15:42 -06:00
|
|
|
if err == nil {
|
2017-01-21 16:02:41 -06:00
|
|
|
log.Println("ServerManager: Got IP for", me.name+":", targetIp)
|
2017-01-27 14:49:58 -06:00
|
|
|
wanAddr := event.tcpConn.LocalAddr().String()
|
|
|
|
_, port, _ := net.SplitHostPort(wanAddr)
|
|
|
|
go portForward(event.tcpConn, targetIp, port, me.connStatus)
|
2016-02-27 16:15:42 -06:00
|
|
|
} else {
|
2016-02-27 16:10:44 -06:00
|
|
|
log.Println("ServerManager: Could not spin up "+me.name+":", err)
|
|
|
|
}
|
|
|
|
case SERVERMANAGER_SPINDOWN:
|
|
|
|
err := me.server.Spindown(me.name)
|
2017-01-27 14:49:58 -06:00
|
|
|
if err != nil {
|
2016-02-27 16:10:44 -06:00
|
|
|
log.Println("ServerManager: Could not spin down "+me.name+":", err)
|
|
|
|
}
|
|
|
|
case SERVERMANAGER_STOP:
|
|
|
|
running = false
|
|
|
|
}
|
|
|
|
return running
|
|
|
|
}
|