diff --git a/handlers/home.go b/handlers/home.go
index b3bd008..6731098 100644
--- a/handlers/home.go
+++ b/handlers/home.go
@@ -1,9 +1,7 @@
package handlers
import (
- "log"
"net/http"
- "path"
"github.com/gorilla/mux"
"github.com/play-with-docker/play-with-docker/services"
@@ -13,29 +11,9 @@ func Home(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sessionId := vars["sessionId"]
- stack := r.URL.Query().Get("stack")
s := services.GetSession(sessionId)
- if stack != "" {
- go deployStack(s, stack)
+ if s.Stack != "" {
+ go s.DeployStack()
}
http.ServeFile(w, r, "./www/index.html")
}
-
-func deployStack(s *services.Session, stack string) {
- i, err := services.NewInstance(s, services.InstanceConfig{})
- if err != nil {
- log.Printf("Error creating instance for stack [%s]: %s\n", stack, err)
- }
- err = i.UploadFromURL("https://raw.githubusercontent.com/play-with-docker/stacks/master" + stack)
- if err != nil {
- log.Printf("Error uploading stack file [%s]: %s\n", stack, err)
- }
-
- fileName := path.Base(stack)
- code, err := services.Exec(i.Name, []string{"docker-compose", "-f", "/var/run/pwd/uploads/" + fileName, "up", "-d"})
- if err != nil {
- log.Printf("Error executing stack [%s]: %s\n", stack, err)
- }
-
- log.Printf("Stack execution finished with code %d\n", code)
-}
diff --git a/handlers/new_session.go b/handlers/new_session.go
index cf79084..fc344de 100644
--- a/handlers/new_session.go
+++ b/handlers/new_session.go
@@ -39,7 +39,7 @@ func NewSession(rw http.ResponseWriter, req *http.Request) {
}
duration := services.GetDuration(reqDur)
- s, err := services.NewSession(duration)
+ s, err := services.NewSession(duration, stack)
if err != nil {
log.Println(err)
//TODO: Return some error code
@@ -54,7 +54,7 @@ func NewSession(rw http.ResponseWriter, req *http.Request) {
}
if stack != "" {
- http.Redirect(rw, req, fmt.Sprintf("http://%s/p/%s?stack=%s", hostname, s.Id, stack), http.StatusFound)
+ http.Redirect(rw, req, fmt.Sprintf("http://%s/p/%s", hostname, s.Id), http.StatusFound)
return
}
http.Redirect(rw, req, fmt.Sprintf("http://%s/p/%s", hostname, s.Id), http.StatusFound)
diff --git a/services/docker.go b/services/docker.go
index 02af4be..bed7ece 100644
--- a/services/docker.go
+++ b/services/docker.go
@@ -365,3 +365,27 @@ func Exec(instanceName string, command []string) (int, error) {
return ins.ExitCode, nil
}
+func ExecAttach(instanceName string, command []string, out io.Writer) (int, error) {
+ e, err := c.ContainerExecCreate(context.Background(), instanceName, types.ExecConfig{Cmd: command, AttachStdout: true, AttachStderr: true, Tty: true})
+ if err != nil {
+ return 0, err
+ }
+ resp, err := c.ContainerExecAttach(context.Background(), e.ID, types.ExecConfig{AttachStdout: true, AttachStderr: true, Tty: true})
+ if err != nil {
+ return 0, err
+ }
+ io.Copy(out, resp.Reader)
+ var ins types.ContainerExecInspect
+ for _ = range time.Tick(1 * time.Second) {
+ ins, err = c.ContainerExecInspect(context.Background(), e.ID)
+ if ins.Running {
+ continue
+ }
+ if err != nil {
+ return 0, err
+ }
+ break
+ }
+ return ins.ExitCode, nil
+
+}
diff --git a/services/session.go b/services/session.go
index 61babac..7ea360a 100644
--- a/services/session.go
+++ b/services/session.go
@@ -9,6 +9,7 @@ import (
"net"
"net/http"
"os"
+ "path"
"sort"
"strings"
"sync"
@@ -56,6 +57,17 @@ type Session struct {
scheduled bool `json:"-"`
ticker *time.Ticker `json:"-"`
PwdIpAddress string `json:"pwd_ip_address"`
+ Ready bool `json:"ready"`
+ Stack string `json:"stack"`
+}
+
+type sessionBuilderWriter struct {
+ session *Session
+}
+
+func (s *sessionBuilderWriter) Write(p []byte) (n int, err error) {
+ wsServer.BroadcastTo(s.session.Id, "session builder out", string(p))
+ return len(p), nil
}
func (s *Session) Lock() {
@@ -78,6 +90,48 @@ func (s *Session) GetSmallestViewPort() ViewPort {
return ViewPort{Rows: minRows, Cols: minCols}
}
+func (s *Session) DeployStack() error {
+ s.Lock()
+ defer s.Unlock()
+
+ if s.Ready {
+ // a stack was already deployed on this session, just ignore
+ return nil
+ }
+
+ s.setReady(false)
+ i, err := NewInstance(s, InstanceConfig{})
+ if err != nil {
+ log.Printf("Error creating instance for stack [%s]: %s\n", s.Stack, err)
+ return err
+ }
+ err = i.UploadFromURL("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{session: s}
+ fileName := path.Base(s.Stack)
+ code, err := 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.setReady(true)
+ if err := saveSessionsToDisk(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (s *Session) setReady(ready bool) {
+ s.Ready = ready
+ wsServer.BroadcastTo(s.Id, "session ready", s.Ready)
+}
+
func (s *Session) AddNewClient(c *Client) {
s.clients = append(s.clients, c)
setGauges()
@@ -243,12 +297,16 @@ func GetDuration(reqDur string) time.Duration {
return defaultDuration
}
-func NewSession(duration time.Duration) (*Session, error) {
+func NewSession(duration time.Duration, stack 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
log.Printf("NewSession id=[%s]\n", s.Id)
// Schedule cleanup of the session
diff --git a/www/assets/app.js b/www/assets/app.js
index b07930f..e1fb396 100644
--- a/www/assets/app.js
+++ b/www/assets/app.js
@@ -11,6 +11,14 @@
}, 500);
}]);
+ function SessionBuilderModalController($mdDialog, $scope) {
+ $scope.createBuilderTerminal();
+
+ $scope.closeSessionBuilder = function() {
+ $mdDialog.cancel();
+ }
+ }
+
app.controller('PlayController', ['$scope', '$log', '$http', '$location', '$timeout', '$mdDialog', '$window', 'TerminalService', 'KeyboardShortcutService', 'InstanceService', function($scope, $log, $http, $location, $timeout, $mdDialog, $window, TerminalService, KeyboardShortcutService, InstanceService) {
$scope.sessionId = window.location.pathname.replace('/p/', '');
$scope.instances = [];
@@ -23,7 +31,7 @@
$scope.newInstanceBtnText = '+ Add new instance';
$scope.deleteInstanceBtnText = 'Delete';
$scope.isInstanceBeingDeleted = false;
-
+
var selectedKeyboardShortcuts = KeyboardShortcutService.getCurrentShortcuts();
angular.element($window).bind('resize', function() {
@@ -90,11 +98,28 @@
});
}
+ $scope.setSessionState = function(state) {
+ $scope.ready = state;
+
+ if (!state) {
+ $mdDialog.show({
+ controller: SessionBuilderModalController,
+ templateUrl: "session-builder-modal.html",
+ parent: angular.element(document.body),
+ clickOutsideToClose: false,
+ scope: $scope,
+ preserveScope: true
+ });
+ }
+ }
+
$scope.getSession = function(sessionId) {
$http({
method: 'GET',
url: '/sessions/' + $scope.sessionId,
}).then(function(response) {
+ $scope.setSessionState(response.data.ready);
+
if (response.data.created_at) {
$scope.expiresAt = moment(response.data.expires_at);
setInterval(function() {
@@ -104,6 +129,14 @@
}
var socket = io({ path: '/sessions/' + sessionId + '/ws' });
+ socket.on('session ready', function(ready) {
+ $scope.setSessionState(ready);
+ });
+
+ socket.on('session builder out', function(data) {
+ $scope.builderTerminal.write(data);
+ });
+
socket.on('terminal out', function(name, data) {
var instance = $scope.idx[name];
@@ -236,6 +269,21 @@
$scope.getSession($scope.sessionId);
+ $scope.createBuilderTerminal = function() {
+ var builderTerminalContainer = document.getElementById('builder-terminal');
+ // For some reason the dialog DOM might not be ready, so we just keep trying
+ if (!builderTerminalContainer) {
+ setTimeout($scope.createBuilderTerminal, 100);
+ return;
+ }
+ var term = new Terminal({
+ cursorBlink: false
+ });
+
+ term.open(builderTerminalContainer);
+ term.resize(80, 24);
+ $scope.builderTerminal = term;
+ }
function createTerminal(instance, cb) {
if (instance.term) {
return instance.term;
diff --git a/www/index.html b/www/index.html
index 089e506..3fc2ae3 100644
--- a/www/index.html
+++ b/www/index.html
@@ -113,6 +113,39 @@
+
+