Cleaned up project organization.
This commit is contained in:
parent
a56b340dce
commit
294af41473
182
dospin.go
182
dospin.go
@ -1,11 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/digitalocean/godo"
|
||||
"golang.org/x/oauth2"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
type TokenSource struct {
|
||||
@ -29,183 +27,13 @@ func main() {
|
||||
client := godo.NewClient(oauthClient)
|
||||
dm := NewDropletManager(client, settings)
|
||||
|
||||
droplet, err := dm.spinupDroplet("minecraft")
|
||||
_, err = dm.SpinupMachine("minecraft")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
_, err = client.Droplets.Delete(droplet.ID)
|
||||
if err != nil {
|
||||
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")
|
||||
//_, err = client.Droplets.Delete(droplet.ID)
|
||||
//if err != nil {
|
||||
// log.Println(err)
|
||||
//}
|
||||
}
|
||||
|
108
dropletmanager.go
Normal file
108
dropletmanager.go
Normal 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
83
servicemanager.go
Normal 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() {
|
||||
}
|
@ -5,7 +5,7 @@ import (
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type settings struct {
|
||||
type Settings struct {
|
||||
Token string
|
||||
VirtualDroplets map[string]VirtualDroplet
|
||||
}
|
||||
@ -15,8 +15,8 @@ type VirtualDroplet struct {
|
||||
Region string
|
||||
}
|
||||
|
||||
func loadSettings(path string) (settings, error) {
|
||||
var s settings
|
||||
func loadSettings(path string) (Settings, error) {
|
||||
var s Settings
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return s, err
|
||||
|
Loading…
Reference in New Issue
Block a user