diff --git a/Gopkg.lock b/Gopkg.lock
index b36afa3..2339668 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -353,6 +353,12 @@
revision = "fde5e16d32adc7ad637e9cd9ad21d4ebc6192535"
version = "v0.2.0"
+[[projects]]
+ name = "github.com/zbindenren/negroni-prometheus"
+ packages = ["."]
+ revision = "b4860d1377680423b4c7f957ef4fce828deacf65"
+ version = "v0.1.1"
+
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
@@ -434,6 +440,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "e9fd1884fe1cc9d2801810e3934b9caa124434efda447ee2f93435737b2e735f"
+ inputs-digest = "2468168ab5c826aef27405ea9a3e72c6a99fbea6639099f2600470330509f8d9"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index 6abc629..0e7882b 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -84,3 +84,7 @@
[[constraint]]
branch = "master"
name = "github.com/satori/go.uuid"
+
+[[constraint]]
+ name = "github.com/zbindenren/negroni-prometheus"
+ version = "0.1.1"
diff --git a/handlers/bootstrap.go b/handlers/bootstrap.go
index 9d35369..b912000 100644
--- a/handlers/bootstrap.go
+++ b/handlers/bootstrap.go
@@ -19,6 +19,7 @@ import (
"github.com/play-with-docker/play-with-docker/config"
"github.com/play-with-docker/play-with-docker/event"
"github.com/play-with-docker/play-with-docker/pwd"
+ "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/urfave/negroni"
)
@@ -27,8 +28,19 @@ var core pwd.PWDApi
var e event.EventApi
var landings = map[string][]byte{}
+var latencyHistogramVec = prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Name: "pwd_handlers_duration_ms",
+ Help: "How long it took to process a specific handler, in a specific host",
+ Buckets: []float64{300, 1200, 5000},
+}, []string{"action"})
+
type HandlerExtender func(h *mux.Router)
+func init() {
+ prometheus.MustRegister(latencyHistogramVec)
+
+}
+
func Bootstrap(c pwd.PWDApi, ev event.EventApi) {
core = c
e = ev
@@ -53,6 +65,10 @@ func Register(extend HandlerExtender) {
corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", DeleteInstance).Methods("DELETE")
corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/exec", Exec).Methods("POST")
+ r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/editor.html", func(rw http.ResponseWriter, r *http.Request) {
+ http.ServeFile(rw, r, "www/editor.html")
+ })
+
r.HandleFunc("/ooc", func(rw http.ResponseWriter, r *http.Request) {
http.ServeFile(rw, r, "./www/ooc.html")
}).Methods("GET")
@@ -64,9 +80,6 @@ func Register(extend HandlerExtender) {
r.HandleFunc("/robots.txt", func(rw http.ResponseWriter, r *http.Request) {
http.ServeFile(rw, r, "www/robots.txt")
})
- r.HandleFunc("/sdk.js", func(rw http.ResponseWriter, r *http.Request) {
- http.ServeFile(rw, r, "www/sdk.js")
- })
corsRouter.HandleFunc("/sessions/{sessionId}/ws/", WSH)
r.Handle("/metrics", promhttp.Handler())
@@ -90,6 +103,7 @@ func Register(extend HandlerExtender) {
}
n := negroni.Classic()
+
r.PathPrefix("/").Handler(negroni.New(negroni.Wrap(corsHandler(corsRouter))))
n.UseHandler(r)
@@ -128,7 +142,11 @@ func Register(extend HandlerExtender) {
rr.HandleFunc("/ping", Ping).Methods("GET")
rr.Handle("/metrics", promhttp.Handler())
rr.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
- http.Redirect(rw, r, fmt.Sprintf("https://%s", r.Host), http.StatusMovedPermanently)
+ target := fmt.Sprintf("https://%s%s", r.Host, r.URL.Path)
+ if len(r.URL.RawQuery) > 0 {
+ target += "?" + r.URL.RawQuery
+ }
+ http.Redirect(rw, r, target, http.StatusMovedPermanently)
})
nr := negroni.Classic()
nr.UseHandler(rr)
diff --git a/handlers/close_session.go b/handlers/close_session.go
index f8009cc..22753b1 100644
--- a/handlers/close_session.go
+++ b/handlers/close_session.go
@@ -5,16 +5,20 @@ import (
"net/http"
"github.com/gorilla/mux"
+ "github.com/play-with-docker/play-with-docker/storage"
)
func CloseSession(rw http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
sessionId := vars["sessionId"]
- session := core.SessionGet(sessionId)
- if session == nil {
+ session, err := core.SessionGet(sessionId)
+ if err == storage.NotFoundError {
rw.WriteHeader(http.StatusNotFound)
return
+ } else if err != nil {
+ rw.WriteHeader(http.StatusInternalServerError)
+ return
}
if err := core.SessionClose(session); err != nil {
diff --git a/handlers/delete_instance.go b/handlers/delete_instance.go
index d8088c6..8a7d397 100644
--- a/handlers/delete_instance.go
+++ b/handlers/delete_instance.go
@@ -4,6 +4,7 @@ import (
"net/http"
"github.com/gorilla/mux"
+ "github.com/play-with-docker/play-with-docker/storage"
)
func DeleteInstance(rw http.ResponseWriter, req *http.Request) {
@@ -11,7 +12,7 @@ func DeleteInstance(rw http.ResponseWriter, req *http.Request) {
sessionId := vars["sessionId"]
instanceName := vars["instanceName"]
- s := core.SessionGet(sessionId)
+ s, err := core.SessionGet(sessionId)
if s != nil {
i := core.InstanceGet(s, instanceName)
err := core.InstanceDelete(s, i)
@@ -19,8 +20,11 @@ func DeleteInstance(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusInternalServerError)
return
}
- } else {
- rw.WriteHeader(http.StatusNotFound)
+ } else if err == storage.NotFoundError {
+ rw.WriteHeader(http.StatusInternalServerError)
+ return
+ } else if err != nil {
+ rw.WriteHeader(http.StatusInternalServerError)
return
}
}
diff --git a/handlers/exec.go b/handlers/exec.go
index 37370ed..55eb939 100644
--- a/handlers/exec.go
+++ b/handlers/exec.go
@@ -28,7 +28,7 @@ func Exec(rw http.ResponseWriter, req *http.Request) {
return
}
- s := core.SessionGet(sessionId)
+ s, _ := core.SessionGet(sessionId)
if s == nil {
rw.WriteHeader(http.StatusNotFound)
return
diff --git a/handlers/file_upload.go b/handlers/file_upload.go
index dda880b..3578d35 100644
--- a/handlers/file_upload.go
+++ b/handlers/file_upload.go
@@ -7,6 +7,7 @@ import (
"path/filepath"
"github.com/gorilla/mux"
+ "github.com/play-with-docker/play-with-docker/storage"
)
func FileUpload(rw http.ResponseWriter, req *http.Request) {
@@ -14,7 +15,14 @@ func FileUpload(rw http.ResponseWriter, req *http.Request) {
sessionId := vars["sessionId"]
instanceName := vars["instanceName"]
- s := core.SessionGet(sessionId)
+ s, err := core.SessionGet(sessionId)
+ if err == storage.NotFoundError {
+ rw.WriteHeader(http.StatusNotFound)
+ return
+ } else if err != nil {
+ rw.WriteHeader(http.StatusInternalServerError)
+ return
+ }
i := core.InstanceGet(s, instanceName)
// allow up to 32 MB which is the default
diff --git a/handlers/get_session.go b/handlers/get_session.go
index a473f45..66ce321 100644
--- a/handlers/get_session.go
+++ b/handlers/get_session.go
@@ -7,6 +7,7 @@ import (
"github.com/gorilla/mux"
"github.com/play-with-docker/play-with-docker/pwd/types"
+ "github.com/play-with-docker/play-with-docker/storage"
)
type SessionInfo struct {
@@ -18,8 +19,11 @@ func GetSession(rw http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
sessionId := vars["sessionId"]
- session := core.SessionGet(sessionId)
- if session == nil {
+ session, err := core.SessionGet(sessionId)
+ if err == storage.NotFoundError {
+ rw.WriteHeader(http.StatusNotFound)
+ return
+ } else if err != nil {
rw.WriteHeader(http.StatusNotFound)
return
}
diff --git a/handlers/home.go b/handlers/home.go
index 3fc46f9..b203fd3 100644
--- a/handlers/home.go
+++ b/handlers/home.go
@@ -7,17 +7,21 @@ import (
"path/filepath"
"github.com/gorilla/mux"
+ "github.com/play-with-docker/play-with-docker/storage"
)
func Home(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sessionId := vars["sessionId"]
- s := core.SessionGet(sessionId)
- if s == nil {
+ s, err := core.SessionGet(sessionId)
+ if err == storage.NotFoundError {
// Session doesn't exist (can happen if closing the sessions an reloading the page, or similar).
w.WriteHeader(http.StatusNotFound)
return
+ } else if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
}
if s.Stack != "" {
go core.SessionDeployStack(s)
diff --git a/handlers/new_instance.go b/handlers/new_instance.go
index 7ce24ae..f91f685 100644
--- a/handlers/new_instance.go
+++ b/handlers/new_instance.go
@@ -20,7 +20,11 @@ func NewInstance(rw http.ResponseWriter, req *http.Request) {
json.NewDecoder(req.Body).Decode(&body)
- s := core.SessionGet(sessionId)
+ s, err := core.SessionGet(sessionId)
+ if err != nil {
+ rw.WriteHeader(http.StatusInternalServerError)
+ return
+ }
playground := core.PlaygroundGet(s.PlaygroundId)
if playground == nil {
diff --git a/handlers/ping.go b/handlers/ping.go
index a6993d7..33d47ad 100644
--- a/handlers/ping.go
+++ b/handlers/ping.go
@@ -12,6 +12,7 @@ import (
)
func Ping(rw http.ResponseWriter, req *http.Request) {
+ defer latencyHistogramVec.WithLabelValues("ping").Observe(float64(time.Since(time.Now()).Nanoseconds()) / 1000000)
// Get system load average of the last 5 minutes and compare it against a threashold.
c, err := docker.NewEnvClient()
@@ -27,6 +28,7 @@ func Ping(rw http.ResponseWriter, req *http.Request) {
if _, err := c.Info(ctx); err != nil && err == context.DeadlineExceeded {
log.Printf("Docker info took to long to respond\n")
rw.WriteHeader(http.StatusGatewayTimeout)
+ return
}
a, err := load.Avg()
diff --git a/handlers/session_setup.go b/handlers/session_setup.go
index 28799fe..f12d062 100644
--- a/handlers/session_setup.go
+++ b/handlers/session_setup.go
@@ -17,9 +17,13 @@ func SessionSetup(rw http.ResponseWriter, req *http.Request) {
json.NewDecoder(req.Body).Decode(&body)
- s := core.SessionGet(sessionId)
+ s, err := core.SessionGet(sessionId)
+ if err != nil {
+ rw.WriteHeader(http.StatusInternalServerError)
+ return
+ }
- err := core.SessionSetup(s, body)
+ err = core.SessionSetup(s, body)
if err != nil {
if pwd.SessionNotEmpty(err) {
log.Println("Cannot setup a session that contains instances")
diff --git a/handlers/ws.go b/handlers/ws.go
index bdde831..fbf850a 100644
--- a/handlers/ws.go
+++ b/handlers/ws.go
@@ -10,6 +10,7 @@ import (
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"github.com/play-with-docker/play-with-docker/event"
+ "github.com/play-with-docker/play-with-docker/storage"
"github.com/satori/go.uuid"
)
@@ -144,8 +145,8 @@ func ws(so *socket) {
sessionId := vars["sessionId"]
- session := core.SessionGet(sessionId)
- if session == nil {
+ session, err := core.SessionGet(sessionId)
+ if err == storage.NotFoundError {
log.Printf("Session with id [%s] does not exist!\n", sessionId)
return
}
diff --git a/pwd/pwd.go b/pwd/pwd.go
index ddf678a..6f8fcf0 100644
--- a/pwd/pwd.go
+++ b/pwd/pwd.go
@@ -77,7 +77,7 @@ type PWDApi interface {
SessionClose(session *types.Session) error
SessionGetSmallestViewPort(sessionId string) types.ViewPort
SessionDeployStack(session *types.Session) error
- SessionGet(id string) *types.Session
+ SessionGet(id string) (*types.Session, error)
SessionSetup(session *types.Session, conf SessionSetupConf) error
InstanceNew(session *types.Session, conf types.InstanceConfig) (*types.Instance, error)
diff --git a/pwd/session.go b/pwd/session.go
index 228e54a..0470ebf 100644
--- a/pwd/session.go
+++ b/pwd/session.go
@@ -205,17 +205,17 @@ func (p *pwd) SessionDeployStack(s *types.Session) error {
return nil
}
-func (p *pwd) SessionGet(sessionId string) *types.Session {
+func (p *pwd) SessionGet(sessionId string) (*types.Session, error) {
defer observeAction("SessionGet", time.Now())
s, err := p.storage.SessionGet(sessionId)
if err != nil {
log.Println(err)
- return nil
+ return nil, err
}
- return s
+ return s, nil
}
func (p *pwd) SessionSetup(session *types.Session, sconf SessionSetupConf) error {
diff --git a/www/assets/app.js b/www/assets/app.js
index 9a9bb05..b7a1ff6 100644
--- a/www/assets/app.js
+++ b/www/assets/app.js
@@ -1,7 +1,7 @@
(function() {
'use strict';
- var app = angular.module('DockerPlay', ['ngMaterial', 'ngFileUpload']);
+ var app = angular.module('DockerPlay', ['ngMaterial', 'ngFileUpload', 'ngclipboard']);
// Automatically redirects user to a new session when bypassing captcha.
// Controller keeps code/logic separate from the HTML
diff --git a/www/default/index.html b/www/default/index.html
index 59fb4b7..07de953 100644
--- a/www/default/index.html
+++ b/www/default/index.html
@@ -116,6 +116,9 @@
{{deleteInstanceBtnText}}
+ SSH
+ Copied
+
@@ -291,6 +294,8 @@
+
+