WIP
This commit is contained in:
226
pwd/session.go
226
pwd/session.go
@@ -1,25 +1,58 @@
|
||||
package pwd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/franela/play-with-docker.old/config"
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
"github.com/twinj/uuid"
|
||||
)
|
||||
|
||||
func (p *pwd) NewSession(duration time.Duration, stack, stackName string) (*Session, error) {
|
||||
type sessionBuilderWriter struct {
|
||||
sessionId string
|
||||
broadcast BroadcastApi
|
||||
}
|
||||
|
||||
func (s *sessionBuilderWriter) Write(p []byte) (n int, err error) {
|
||||
s.broadcast.BroadcastTo(s.sessionId, "session builder out", string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
rw sync.Mutex
|
||||
Id string `json:"id"`
|
||||
Instances map[string]*Instance `json:"instances"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
PwdIpAddress string `json:"pwd_ip_address"`
|
||||
Ready bool `json:"ready"`
|
||||
Stack string `json:"stack"`
|
||||
StackName string `json:"stack_name"`
|
||||
closingTimer *time.Timer `json:"-"`
|
||||
scheduled bool `json:"-"`
|
||||
clients []*Client `json:"-"`
|
||||
ticker *time.Ticker `json:"-"`
|
||||
}
|
||||
|
||||
func (p *pwd) SessionNew(duration time.Duration, stack, stackName string) (*Session, error) {
|
||||
s := &Session{}
|
||||
s.Id = uuid.NewV4().String()
|
||||
s.Instances = map[string]*Instance{}
|
||||
s.CreatedAt = time.Now()
|
||||
s.ExpiresAt = s.CreatedAt.Add(duration)
|
||||
/*
|
||||
if stack == "" {
|
||||
s.Ready = true
|
||||
}
|
||||
s.Stack = stack
|
||||
*/
|
||||
s.Ready = true
|
||||
s.Stack = stack
|
||||
s.StackName = stackName
|
||||
|
||||
if s.Stack != "" {
|
||||
s.Ready = false
|
||||
}
|
||||
|
||||
log.Printf("NewSession id=[%s]\n", s.Id)
|
||||
|
||||
if err := p.docker.CreateNetwork(s.Id); err != nil {
|
||||
@@ -28,39 +61,192 @@ func (p *pwd) NewSession(duration time.Duration, stack, stackName string) (*Sess
|
||||
}
|
||||
log.Printf("Network [%s] created for session [%s]\n", s.Id, s.Id)
|
||||
|
||||
s.Prepare()
|
||||
if err := p.prepareSession(s); err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessions[s.Id] = s
|
||||
if err := p.storage.Save(); err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setGauges()
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (p *pwd) SessionClose(s *Session) error {
|
||||
s.rw.Lock()
|
||||
defer s.rw.Unlock()
|
||||
|
||||
if s.ticker != nil {
|
||||
s.ticker.Stop()
|
||||
}
|
||||
p.broadcast.BroadcastTo(s.Id, "session end")
|
||||
p.broadcast.BroadcastTo(s.Id, "disconnect")
|
||||
log.Printf("Starting clean up of session [%s]\n", s.Id)
|
||||
for _, i := range s.Instances {
|
||||
err := p.InstanceDelete(s, i)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Disconnect PWD daemon from the network
|
||||
if err := p.docker.DisconnectNetwork(config.PWDContainerName, s.Id); err != nil {
|
||||
if !strings.Contains(err.Error(), "is not connected to the network") {
|
||||
log.Println("ERROR NETWORKING")
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Printf("Disconnected pwd from network [%s]\n", s.Id)
|
||||
if err := p.docker.DeleteNetwork(s.Id); err != nil {
|
||||
if !strings.Contains(err.Error(), "not found") {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
delete(sessions, s.Id)
|
||||
|
||||
// We store sessions as soon as we delete one
|
||||
if err := p.storage.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
setGauges()
|
||||
log.Printf("Cleaned up session [%s]\n", s.Id)
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (p *pwd) SessionGetSmallestViewPort(s *Session) ViewPort {
|
||||
minRows := s.clients[0].viewPort.Rows
|
||||
minCols := s.clients[0].viewPort.Cols
|
||||
|
||||
for _, c := range s.clients {
|
||||
minRows = uint(math.Min(float64(minRows), float64(c.viewPort.Rows)))
|
||||
minCols = uint(math.Min(float64(minCols), float64(c.viewPort.Cols)))
|
||||
}
|
||||
|
||||
return ViewPort{Rows: minRows, Cols: minCols}
|
||||
}
|
||||
|
||||
func (p *pwd) SessionDeployStack(s *Session) error {
|
||||
s.rw.Lock()
|
||||
defer s.rw.Unlock()
|
||||
|
||||
if s.Ready {
|
||||
// a stack was already deployed on this session, just ignore
|
||||
return nil
|
||||
}
|
||||
|
||||
s.Ready = false
|
||||
p.broadcast.BroadcastTo(s.Id, "session ready", s.Ready)
|
||||
|
||||
i, err := p.InstanceNew(s, InstanceConfig{})
|
||||
if err != nil {
|
||||
log.Printf("Error creating instance for stack [%s]: %s\n", s.Stack, err)
|
||||
return err
|
||||
}
|
||||
err = p.InstanceUploadFromUrl(i, "https://raw.githubusercontent.com/play-with-docker/stacks/master"+s.Stack)
|
||||
if err != nil {
|
||||
log.Printf("Error uploading stack file [%s]: %s\n", s.Stack, err)
|
||||
return err
|
||||
}
|
||||
|
||||
w := sessionBuilderWriter{sessionId: s.Id, broadcast: p.broadcast}
|
||||
fileName := path.Base(s.Stack)
|
||||
code, err := p.docker.ExecAttach(i.Name, []string{"docker-compose", "-f", "/var/run/pwd/uploads/" + fileName, "up", "-d"}, &w)
|
||||
if err != nil {
|
||||
log.Printf("Error executing stack [%s]: %s\n", s.Stack, err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Stack execution finished with code %d\n", code)
|
||||
|
||||
s.Ready = true
|
||||
p.broadcast.BroadcastTo(s.Id, "session ready", s.Ready)
|
||||
|
||||
if err := p.storage.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pwd) SessionGet(sessionId string) *Session {
|
||||
s := sessions[sessionId]
|
||||
/*
|
||||
if s != nil {
|
||||
for _, instance := range s.Instances {
|
||||
if !instance.IsConnected() {
|
||||
instance.SetSession(s)
|
||||
go instance.Attach()
|
||||
}
|
||||
}
|
||||
|
||||
}*/
|
||||
return s
|
||||
}
|
||||
|
||||
func (p *pwd) SessionLoadAndPrepare() error {
|
||||
err := p.storage.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range sessions {
|
||||
err := p.prepareSession(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, i := range s.Instances {
|
||||
// wire the session back to the instance
|
||||
i.session = s
|
||||
go p.InstanceAttachTerminal(i)
|
||||
}
|
||||
// Connect PWD daemon to the new network
|
||||
if s.PwdIpAddress == "" {
|
||||
return fmt.Errorf("Cannot load stored sessions as they don't have the pwd ip address stored with them")
|
||||
}
|
||||
}
|
||||
|
||||
setGauges()
|
||||
|
||||
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 (s *Session) Prepare() error {
|
||||
s.scheduleSessionClose()
|
||||
func (p *pwd) prepareSession(session *Session) error {
|
||||
p.scheduleSessionClose(session)
|
||||
|
||||
// Connect PWD daemon to the new network
|
||||
s.connectToNetwork()
|
||||
if err := p.connectToNetwork(session); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Schedule periodic tasks
|
||||
p.tasks.Schedule(session)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) scheduleSessionClose() {
|
||||
func (p *pwd) scheduleSessionClose(s *Session) {
|
||||
timeLeft := s.ExpiresAt.Sub(time.Now())
|
||||
s.closingTimer = time.AfterFunc(timeLeft, func() {
|
||||
s.Close()
|
||||
p.SessionClose(s)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Session) Close() {
|
||||
}
|
||||
|
||||
func (s *Session) connectToNetwork() {
|
||||
ip, err := ConnectNetwork(config.PWDContainerName, s.Id, "")
|
||||
func (p *pwd) connectToNetwork(s *Session) error {
|
||||
ip, err := p.docker.ConnectNetwork(config.PWDContainerName, s.Id, s.PwdIpAddress)
|
||||
if err != nil {
|
||||
log.Println("ERROR NETWORKING")
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
s.PwdIpAddress = ip
|
||||
log.Printf("Connected %s to network [%s]\n", config.PWDContainerName, s.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user