From 294af414736baf919e1d76fe9feac6e2cb63c6fa Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Mon, 15 Feb 2016 00:18:56 -0600 Subject: [PATCH] Cleaned up project organization. --- dospin.go | 182 ++-------------------------------------------- dropletmanager.go | 108 +++++++++++++++++++++++++++ servicemanager.go | 83 +++++++++++++++++++++ settings.go | 6 +- 4 files changed, 199 insertions(+), 180 deletions(-) create mode 100644 dropletmanager.go create mode 100644 servicemanager.go diff --git a/dospin.go b/dospin.go index 303380e..a38ed96 100644 --- a/dospin.go +++ b/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) + //} } diff --git a/dropletmanager.go b/dropletmanager.go new file mode 100644 index 0000000..a74b47a --- /dev/null +++ b/dropletmanager.go @@ -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") +} diff --git a/servicemanager.go b/servicemanager.go new file mode 100644 index 0000000..ddc4906 --- /dev/null +++ b/servicemanager.go @@ -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() { +} diff --git a/settings.go b/settings.go index 1666f6d..113a9fb 100644 --- a/settings.go +++ b/settings.go @@ -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