Cleaned up project organization.

This commit is contained in:
Gary Talent 2016-02-15 00:18:56 -06:00
parent a56b340dce
commit 294af41473
4 changed files with 199 additions and 180 deletions

182
dospin.go
View File

@ -1,11 +1,9 @@
package main package main
import ( import (
"errors"
"github.com/digitalocean/godo" "github.com/digitalocean/godo"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"log" "log"
"net"
) )
type TokenSource struct { type TokenSource struct {
@ -29,183 +27,13 @@ func main() {
client := godo.NewClient(oauthClient) client := godo.NewClient(oauthClient)
dm := NewDropletManager(client, settings) dm := NewDropletManager(client, settings)
droplet, err := dm.spinupDroplet("minecraft") _, err = dm.SpinupMachine("minecraft")
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
_, err = client.Droplets.Delete(droplet.ID) //_, err = client.Droplets.Delete(droplet.ID)
if err != nil { //if err != nil {
log.Println(err) // log.Println(err)
} //}
}
// Listens for clients on given ports to spin up droplets for the ports
type ServiceHandler struct {
dropletManager *DropletManager
dropletSvcCnt map[string]int
/*
This should start at 0 and should be incremented any time a cleanup check
shows no connections on this port. Once it reaches 5, the port forward
should be deleted along with that port in the map
*/
portConnStatus map[int]int
}
func NewServiceHandler(dropletManager *DropletManager) *ServiceHandler {
sh := new(ServiceHandler)
sh.dropletManager = dropletManager
return sh
}
func (me *ServiceHandler) setupService(dropletName, port string) {
addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+port)
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)
} else {
// connection accepted
// spinup droplet
droplet, err := me.dropletManager.spinupDroplet(dropletName)
if err == nil {
// setup port forwarding
ip, err := droplet.PrivateIPv4()
if err == nil {
setupPortForward(ip, port)
} else {
log.Print("Could not get private IP address for "+dropletName+": ", err)
}
me.dropletSvcCnt[dropletName]++
} else {
log.Print("Could not spin up Droplet:", err)
}
// close existing connection, not doing anything with it
conn.Close()
}
}
}
}()
}
/*
Periodically checks number of connections to each droplet and deletes
them when they are no longer needed
*/
func (me *ServiceHandler) cleanup() {
}
type DropletManager struct {
client *godo.Client
settings settings
}
func NewDropletManager(client *godo.Client, settings settings) *DropletManager {
retval := new(DropletManager)
retval.client = client
retval.settings = settings
return retval
}
/*
Gets the Droplet if it already exists, instantiates it if it does not.
*/
func (me *DropletManager) spinupDroplet(name string) (*godo.Droplet, error) {
if droplet, err := me.getDroplet(name); err == nil {
return &droplet, nil
} else {
image, err := me.getSnapshot(name)
if err != nil {
return nil, err
}
vd := me.settings.VirtualDroplets[name]
createRequest := &godo.DropletCreateRequest{
Name: name,
Region: vd.Region,
Size: vd.Size,
Image: godo.DropletCreateImage{
ID: image.ID,
},
}
droplet, _, err := me.client.Droplets.Create(createRequest)
if err != nil {
return nil, err
}
return droplet, nil
}
}
func (me *DropletManager) getDroplet(name string) (godo.Droplet, error) {
name = "dospin:" + name
page := 0
perPage := 200
var droplet godo.Droplet
for {
page++
// get list of droplets
opt := &godo.ListOptions{
Page: page,
PerPage: perPage,
}
images, _, err := me.client.Droplets.List(opt)
if err != nil {
break
}
// find droplet
for _, a := range images {
if a.Name == name {
return a, nil
}
}
// check next page?
if len(images) < perPage {
break
}
}
return droplet, errors.New("Could not find droplet")
}
func (me *DropletManager) getSnapshot(name string) (godo.Image, error) {
name = "dospin:" + name
page := 0
perPage := 200
var image godo.Image
for {
page++
// get list of images
opt := &godo.ListOptions{
Page: page,
PerPage: perPage,
}
images, _, err := me.client.Images.ListUser(opt)
if err != nil {
break
}
// find image
for _, a := range images {
if a.Name == name {
return a, nil
}
}
// check next page?
if len(images) < perPage {
break
}
}
return image, errors.New("Could not find image")
} }

108
dropletmanager.go Normal file
View File

@ -0,0 +1,108 @@
package main
import (
"errors"
"github.com/digitalocean/godo"
)
type DropletManager struct {
client *godo.Client
settings Settings
}
func NewDropletManager(client *godo.Client, settings Settings) *DropletManager {
retval := new(DropletManager)
retval.client = client
retval.settings = settings
return retval
}
/*
Gets the Droplet if it already exists, instantiates it if it does not.
*/
func (me *DropletManager) SpinupMachine(name string) (string, error) {
if droplet, err := me.getDroplet(name); err == nil {
return droplet.PrivateIPv4()
} else {
image, err := me.getSnapshot(name)
if err != nil {
return "", err
}
vd := me.settings.VirtualDroplets[name]
createRequest := &godo.DropletCreateRequest{
Name: name,
Region: vd.Region,
Size: vd.Size,
Image: godo.DropletCreateImage{
ID: image.ID,
},
}
droplet, _, err := me.client.Droplets.Create(createRequest)
if err != nil {
return "", err
}
return droplet.PrivateIPv4()
}
}
func (me *DropletManager) getDroplet(name string) (godo.Droplet, error) {
name = "dospin:" + name
page := 0
perPage := 200
var droplet godo.Droplet
for {
page++
// get list of droplets
opt := &godo.ListOptions{
Page: page,
PerPage: perPage,
}
images, _, err := me.client.Droplets.List(opt)
if err != nil {
break
}
// find droplet
for _, a := range images {
if a.Name == name {
return a, nil
}
}
// check next page?
if len(images) < perPage {
break
}
}
return droplet, errors.New("Could not find droplet")
}
func (me *DropletManager) getSnapshot(name string) (godo.Image, error) {
name = "dospin:" + name
page := 0
perPage := 200
var image godo.Image
for {
page++
// get list of images
opt := &godo.ListOptions{
Page: page,
PerPage: perPage,
}
images, _, err := me.client.Images.ListUser(opt)
if err != nil {
break
}
// find image
for _, a := range images {
if a.Name == name {
return a, nil
}
}
// check next page?
if len(images) < perPage {
break
}
}
return image, errors.New("Could not find image")
}

83
servicemanager.go Normal file
View File

@ -0,0 +1,83 @@
package main
import (
"log"
"net"
"strconv"
)
type MachineManager interface {
// Takes snapshot name, and returns the IP to connect to.
SpinupMachine(name string) (string, error)
}
type service struct {
name string
/*
This should start at 0 and should be incremented any time a cleanup check
shows no connections on this port. Once it reaches 5, the port forward
should be deleted along with that port in the map
*/
connectionStatus int
}
// Listens for clients on given ports to spin up machines for the ports
type ServiceManager struct {
machineManager MachineManager
machineSvcCnt map[string]int
svcConnStatus map[int]service
}
func NewServiceHandler(mm MachineManager) *ServiceManager {
sh := new(ServiceManager)
sh.machineManager = mm
return sh
}
func (me *ServiceManager) setupService(serviceName, machineName string, 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)
} else {
// connection accepted
// spinup machine
ip, err := me.machineManager.SpinupMachine(machineName)
// setup port forwarding
if err == nil {
setupPortForward(ip, portStr)
me.machineSvcCnt[machineName]++
me.svcConnStatus[port] = service{name: serviceName, connectionStatus: 0}
} else {
log.Print("Could not setup machine "+machineName+":", err)
}
// close existing connection, not doing anything with it
conn.Close()
}
}
}
}()
}
/*
Periodically checks number of connections to each machine and deletes
them when they are no longer needed
*/
func (me *ServiceManager) cleanup() {
}

View File

@ -5,7 +5,7 @@ import (
"io/ioutil" "io/ioutil"
) )
type settings struct { type Settings struct {
Token string Token string
VirtualDroplets map[string]VirtualDroplet VirtualDroplets map[string]VirtualDroplet
} }
@ -15,8 +15,8 @@ type VirtualDroplet struct {
Region string Region string
} }
func loadSettings(path string) (settings, error) { func loadSettings(path string) (Settings, error) {
var s settings var s Settings
data, err := ioutil.ReadFile(path) data, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return s, err return s, err