Multiple playgrounds support (#215)
* Add Playground struct and basic support for creating it and retrieving it * Add missing functions in pwd mock * Get playground from request domain and validate it exists. If valid set it on the newly created session. * Move playground specific configurations to the playground struct and use it everytime we need that conf. * Don't allow to specify a duration bigger that the allowed in the playground
This commit is contained in:
committed by
GitHub
parent
3dee0d3f0b
commit
3f5b3882dd
12
api.go
12
api.go
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
"github.com/play-with-docker/play-with-docker/docker"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/play-with-docker/play-with-docker/id"
|
||||
"github.com/play-with-docker/play-with-docker/provisioner"
|
||||
"github.com/play-with-docker/play-with-docker/pwd"
|
||||
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||
"github.com/play-with-docker/play-with-docker/scheduler"
|
||||
"github.com/play-with-docker/play-with-docker/scheduler/task"
|
||||
"github.com/play-with-docker/play-with-docker/storage"
|
||||
@@ -41,6 +43,16 @@ func main() {
|
||||
|
||||
sch.Start()
|
||||
|
||||
d, err := time.ParseDuration(config.DefaultSessionDuration)
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot parse duration %s. Got: %v", config.DefaultSessionDuration, err)
|
||||
}
|
||||
|
||||
playground := types.Playground{Domain: config.PlaygroundDomain, DefaultDinDInstanceImage: config.DefaultDinDImage, AllowWindowsInstances: config.NoWindows, DefaultSessionDuration: d, AvailableDinDInstanceImages: []string{config.DefaultDinDImage}}
|
||||
if _, err := core.PlaygroundNew(playground); err != nil {
|
||||
log.Fatalf("Cannot create default playground. Got: %v", err)
|
||||
}
|
||||
|
||||
handlers.Bootstrap(core, e)
|
||||
handlers.Register(nil)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/securecookie"
|
||||
|
||||
@@ -27,10 +24,9 @@ const (
|
||||
var NameFilter = regexp.MustCompile(PWDHostPortGroupRegex)
|
||||
var AliasFilter = regexp.MustCompile(AliasPortGroupRegex)
|
||||
|
||||
var PortNumber, Key, Cert, SessionsFile, PWDContainerName, L2ContainerName, L2Subdomain, PWDCName, HashKey, SSHKeyPath, L2RouterIP, DindVolumeSize, CookieHashKey, CookieBlockKey string
|
||||
var PortNumber, Key, Cert, SessionsFile, PWDContainerName, L2ContainerName, L2Subdomain, HashKey, SSHKeyPath, L2RouterIP, DindVolumeSize, CookieHashKey, CookieBlockKey, DefaultDinDImage, DefaultSessionDuration string
|
||||
var UseLetsEncrypt, ExternalDindVolume, NoWindows bool
|
||||
var LetsEncryptCertsDir string
|
||||
var LetsEncryptDomains stringslice
|
||||
var MaxLoadAvg float64
|
||||
var ForceTLS bool
|
||||
var Providers map[string]*oauth2.Config
|
||||
@@ -40,18 +36,9 @@ var GithubClientID, GithubClientSecret string
|
||||
var FacebookClientID, FacebookClientSecret string
|
||||
var DockerClientID, DockerClientSecret string
|
||||
|
||||
type stringslice []string
|
||||
|
||||
func (i *stringslice) String() string {
|
||||
return fmt.Sprintf("%s", *i)
|
||||
}
|
||||
func (i *stringslice) Set(value string) error {
|
||||
*i = append(*i, value)
|
||||
return nil
|
||||
}
|
||||
var PlaygroundDomain string
|
||||
|
||||
func ParseFlags() {
|
||||
flag.Var(&LetsEncryptDomains, "letsencrypt-domain", "List of domains to validate with let's encrypt")
|
||||
flag.StringVar(&LetsEncryptCertsDir, "letsencrypt-certs-dir", "/certs", "Path where let's encrypt certs will be stored")
|
||||
flag.BoolVar(&UseLetsEncrypt, "letsencrypt-enable", false, "Enabled let's encrypt tls certificates")
|
||||
flag.BoolVar(&ForceTLS, "tls", false, "Use TLS to connect to docker daemons")
|
||||
@@ -63,7 +50,6 @@ func ParseFlags() {
|
||||
flag.StringVar(&L2ContainerName, "l2", "l2", "Container name used to run L2 Router")
|
||||
flag.StringVar(&L2RouterIP, "l2-ip", "", "Host IP address for L2 router ping response")
|
||||
flag.StringVar(&L2Subdomain, "l2-subdomain", "direct", "Subdomain to the L2 Router")
|
||||
flag.StringVar(&PWDCName, "cname", "", "CNAME given to this host")
|
||||
flag.StringVar(&HashKey, "hash_key", "salmonrosado", "Hash key to use for cookies")
|
||||
flag.StringVar(&DindVolumeSize, "dind-volume-size", "5G", "Dind volume folder size")
|
||||
flag.BoolVar(&NoWindows, "win-disable", false, "Disable windows instances")
|
||||
@@ -72,6 +58,8 @@ func ParseFlags() {
|
||||
flag.StringVar(&SSHKeyPath, "ssh_key_path", "", "SSH Private Key to use")
|
||||
flag.StringVar(&CookieHashKey, "cookie-hash-key", "", "Hash key to use to validate cookies")
|
||||
flag.StringVar(&CookieBlockKey, "cookie-block-key", "", "Block key to use to encrypt cookies")
|
||||
flag.StringVar(&DefaultDinDImage, "default-dind-image", "franela/dind", "Default DinD image to use if not specified otherwise")
|
||||
flag.StringVar(&DefaultSessionDuration, "default-session-duration", "4h", "Default session duration if not specified otherwise")
|
||||
|
||||
flag.StringVar(&GithubClientID, "oauth-github-client-id", "", "Github OAuth Client ID")
|
||||
flag.StringVar(&GithubClientSecret, "oauth-github-client-secret", "", "Github OAuth Client Secret")
|
||||
@@ -82,6 +70,8 @@ func ParseFlags() {
|
||||
flag.StringVar(&DockerClientID, "oauth-docker-client-id", "", "Docker OAuth Client ID")
|
||||
flag.StringVar(&DockerClientSecret, "oauth-docker-client-secret", "", "Docker OAuth Client Secret")
|
||||
|
||||
flag.StringVar(&PlaygroundDomain, "playground-domain", "localhost", "Domain to use for the playground")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
SecureCookie = securecookie.New([]byte(CookieHashKey), []byte(CookieBlockKey))
|
||||
@@ -126,38 +116,3 @@ func registerOAuthProviders() {
|
||||
Providers["docker"] = conf
|
||||
}
|
||||
}
|
||||
|
||||
func GetDindImageName() string {
|
||||
dindImage := os.Getenv("DIND_IMAGE")
|
||||
defaultDindImageName := "franela/dind"
|
||||
if len(dindImage) == 0 {
|
||||
dindImage = defaultDindImageName
|
||||
}
|
||||
return dindImage
|
||||
}
|
||||
|
||||
func GetSSHImage() string {
|
||||
sshImage := os.Getenv("SSH_IMAGE")
|
||||
defaultSSHImage := "franela/ssh"
|
||||
if len(sshImage) == 0 {
|
||||
return defaultSSHImage
|
||||
}
|
||||
return sshImage
|
||||
}
|
||||
|
||||
func GetDuration(reqDur string) time.Duration {
|
||||
var defaultDuration = 4 * time.Hour
|
||||
if reqDur != "" {
|
||||
if dur, err := time.ParseDuration(reqDur); err == nil && dur <= defaultDuration {
|
||||
return dur
|
||||
}
|
||||
return defaultDuration
|
||||
}
|
||||
|
||||
envDur := os.Getenv("EXPIRY")
|
||||
if dur, err := time.ParseDuration(envDur); err == nil {
|
||||
return dur
|
||||
}
|
||||
|
||||
return defaultDuration
|
||||
}
|
||||
|
||||
@@ -14,16 +14,13 @@ services:
|
||||
# use the latest golang image
|
||||
image: golang
|
||||
# go to the right place and starts the app
|
||||
command: /bin/sh -c 'ssh-keygen -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key >/dev/null; cd /go/src/github.com/play-with-docker/play-with-docker; go run api.go -save /pwd/sessions -name l2'
|
||||
command: /bin/sh -c 'ssh-keygen -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key >/dev/null; cd /go/src/github.com/play-with-docker/play-with-docker; go run api.go -save /pwd/sessions -name l2 -default-dind-image franela/dind:hybrid'
|
||||
volumes:
|
||||
# since this app creates networks and launches containers, we need to talk to docker daemon
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# mount the box mounted shared folder to the container
|
||||
- $GOPATH/src:/go/src
|
||||
- sessions:/pwd
|
||||
environment:
|
||||
GOOGLE_RECAPTCHA_DISABLED: "true"
|
||||
DIND_IMAGE: "franela/dind:hybrid"
|
||||
l2:
|
||||
container_name: l2
|
||||
# use the latest golang image
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
|
||||
gh "github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"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"
|
||||
@@ -92,10 +94,22 @@ func Register(extend HandlerExtender) {
|
||||
}
|
||||
|
||||
if config.UseLetsEncrypt {
|
||||
domainCache, err := lru.New(5000)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not start domain cache. Got: %v", err)
|
||||
}
|
||||
certManager := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist(config.LetsEncryptDomains...),
|
||||
Cache: autocert.DirCache(config.LetsEncryptCertsDir),
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: func(ctx context.Context, host string) error {
|
||||
if _, found := domainCache.Get(host); !found {
|
||||
if playground := core.PlaygroundFindByDomain(host); playground == nil {
|
||||
return fmt.Errorf("Playground for domain %s was not found", host)
|
||||
}
|
||||
domainCache.Add(host, true)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Cache: autocert.DirCache(config.LetsEncryptCertsDir),
|
||||
}
|
||||
|
||||
httpServer.TLSConfig = &tls.Config{
|
||||
|
||||
@@ -2,15 +2,16 @@ package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
)
|
||||
|
||||
func GetInstanceImages(rw http.ResponseWriter, req *http.Request) {
|
||||
instanceImages := []string{
|
||||
config.GetDindImageName(),
|
||||
"franela/dind:dev",
|
||||
playground := core.PlaygroundFindByDomain(req.Host)
|
||||
if playground == nil {
|
||||
log.Printf("Playground for domain %s was not found!", req.Host)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
json.NewEncoder(rw).Encode(instanceImages)
|
||||
json.NewEncoder(rw).Encode(playground.AvailableDinDInstanceImages)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
)
|
||||
|
||||
func Home(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -21,7 +21,14 @@ func Home(w http.ResponseWriter, r *http.Request) {
|
||||
go core.SessionDeployStack(s)
|
||||
}
|
||||
|
||||
if config.NoWindows {
|
||||
playground := core.PlaygroundGet(s.PlaygroundId)
|
||||
if playground == nil {
|
||||
log.Printf("Playground with id %s for session %s was not found!", s.PlaygroundId, s.Id)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !playground.AllowWindowsInstances {
|
||||
http.ServeFile(w, r, "./www/index-nw.html")
|
||||
} else {
|
||||
http.ServeFile(w, r, "./www/index.html")
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
"github.com/play-with-docker/play-with-docker/provisioner"
|
||||
"github.com/play-with-docker/play-with-docker/pwd"
|
||||
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||
@@ -23,7 +22,14 @@ func NewInstance(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
s := core.SessionGet(sessionId)
|
||||
|
||||
if body.Type == "windows" && config.NoWindows {
|
||||
playground := core.PlaygroundGet(s.PlaygroundId)
|
||||
if playground == nil {
|
||||
log.Printf("Playground with id %s for session %s was not found!", s.PlaygroundId, s.Id)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if body.Type == "windows" && !playground.AllowWindowsInstances {
|
||||
rw.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
"github.com/play-with-docker/play-with-docker/provisioner"
|
||||
@@ -49,8 +50,32 @@ func NewSession(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
}
|
||||
duration := config.GetDuration(reqDur)
|
||||
s, err := core.SessionNew(userId, duration, stack, stackName, imageName)
|
||||
|
||||
playground := core.PlaygroundFindByDomain(req.Host)
|
||||
if playground == nil {
|
||||
log.Printf("Playground for domain %s was not found!", req.Host)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var duration time.Duration
|
||||
if reqDur != "" {
|
||||
d, err := time.ParseDuration(reqDur)
|
||||
if err != nil {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if d > playground.DefaultSessionDuration {
|
||||
log.Printf("Specified session duration was %s but maximum allowed by this playground is %d\n", d.String(), playground.DefaultSessionDuration.String())
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
duration = d
|
||||
} else {
|
||||
duration = playground.DefaultSessionDuration
|
||||
}
|
||||
|
||||
s, err := core.SessionNew(playground, userId, duration, stack, stackName, imageName)
|
||||
if err != nil {
|
||||
if provisioner.OutOfCapacity(err) {
|
||||
http.Redirect(rw, req, "/ooc", http.StatusFound)
|
||||
@@ -62,9 +87,6 @@ func NewSession(rw http.ResponseWriter, req *http.Request) {
|
||||
//TODO: Return some error code
|
||||
} else {
|
||||
hostname := req.Host
|
||||
if config.PWDCName != "" {
|
||||
hostname = fmt.Sprintf("%s.%s", config.PWDCName, req.Host)
|
||||
}
|
||||
// If request is not a form, return sessionId in the body
|
||||
if req.Header.Get("X-Requested-With") == "XMLHttpRequest" {
|
||||
resp := NewSessionResponse{SessionId: s.Id, Hostname: hostname}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"strings"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
"github.com/play-with-docker/play-with-docker/docker"
|
||||
"github.com/play-with-docker/play-with-docker/id"
|
||||
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||
@@ -44,7 +43,11 @@ func checkHostnameExists(sessionId, hostname string, instances []*types.Instance
|
||||
|
||||
func (d *DinD) InstanceNew(session *types.Session, conf types.InstanceConfig) (*types.Instance, error) {
|
||||
if conf.ImageName == "" {
|
||||
conf.ImageName = config.GetDindImageName()
|
||||
playground, err := d.storage.PlaygroundGet(session.PlaygroundId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf.ImageName = playground.DefaultDinDInstanceImage
|
||||
}
|
||||
log.Printf("NewInstance - using image: [%s]\n", conf.ImageName)
|
||||
if conf.Hostname == "" {
|
||||
|
||||
@@ -43,7 +43,9 @@ func TestClientNew(t *testing.T) {
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
|
||||
session, err := p.SessionNew("", time.Hour, "", "", "")
|
||||
playground := &types.Playground{Id: "foobar"}
|
||||
|
||||
session, err := p.SessionNew(playground, "", time.Hour, "", "", "")
|
||||
assert.Nil(t, err)
|
||||
|
||||
client := p.ClientNew("foobar", session)
|
||||
@@ -81,8 +83,9 @@ func TestClientCount(t *testing.T) {
|
||||
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
playground := &types.Playground{Id: "foobar"}
|
||||
|
||||
session, err := p.SessionNew("", time.Hour, "", "", "")
|
||||
session, err := p.SessionNew(playground, "", time.Hour, "", "", "")
|
||||
assert.Nil(t, err)
|
||||
|
||||
p.ClientNew("foobar", session)
|
||||
@@ -122,8 +125,9 @@ func TestClientResizeViewPort(t *testing.T) {
|
||||
_e.M.On("Emit", event.INSTANCE_VIEWPORT_RESIZE, "aaaabbbbcccc", []interface{}{uint(80), uint(24)}).Return()
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
playground := &types.Playground{Id: "foobar"}
|
||||
|
||||
session, err := p.SessionNew("", time.Hour, "", "", "")
|
||||
session, err := p.SessionNew(playground, "", time.Hour, "", "", "")
|
||||
assert.Nil(t, err)
|
||||
client := p.ClientNew("foobar", session)
|
||||
_s.On("ClientFindBySessionId", "aaaabbbbcccc").Return([]*types.Client{client}, nil)
|
||||
|
||||
@@ -70,7 +70,11 @@ func TestInstanceNew(t *testing.T) {
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
|
||||
session, err := p.SessionNew("", time.Hour, "", "", "")
|
||||
playground := &types.Playground{Id: "foobar", DefaultDinDInstanceImage: "franela/dind"}
|
||||
|
||||
_s.On("PlaygroundGet", "foobar").Return(playground, nil)
|
||||
|
||||
session, err := p.SessionNew(playground, "", time.Hour, "", "", "")
|
||||
assert.Nil(t, err)
|
||||
|
||||
expectedInstance := types.Instance{
|
||||
@@ -78,7 +82,7 @@ func TestInstanceNew(t *testing.T) {
|
||||
Hostname: "node1",
|
||||
IP: "10.0.0.1",
|
||||
RoutableIP: "10.0.0.1",
|
||||
Image: config.GetDindImageName(),
|
||||
Image: "franela/dind",
|
||||
SessionId: session.Id,
|
||||
SessionHost: session.Host,
|
||||
ProxyHost: router.EncodeHost(session.Id, "10.0.0.1", router.HostOpts{}),
|
||||
@@ -138,7 +142,8 @@ func TestInstanceNew_WithNotAllowedImage(t *testing.T) {
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
|
||||
session, err := p.SessionNew("", time.Hour, "", "", "")
|
||||
playground := &types.Playground{Id: "foobar"}
|
||||
session, err := p.SessionNew(playground, "", time.Hour, "", "", "")
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -207,7 +212,8 @@ func TestInstanceNew_WithCustomHostname(t *testing.T) {
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
|
||||
session, err := p.SessionNew("", time.Hour, "", "", "")
|
||||
playground := &types.Playground{Id: "foobar"}
|
||||
session, err := p.SessionNew(playground, "", time.Hour, "", "", "")
|
||||
assert.Nil(t, err)
|
||||
|
||||
expectedInstance := types.Instance{
|
||||
|
||||
22
pwd/mock.go
22
pwd/mock.go
@@ -13,7 +13,7 @@ type Mock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *Mock) SessionNew(userId string, duration time.Duration, stack string, stackName, imageName string) (*types.Session, error) {
|
||||
func (m *Mock) SessionNew(playground *types.Playground, userId string, duration time.Duration, stack string, stackName, imageName string) (*types.Session, error) {
|
||||
args := m.Called(duration, stack, stackName, imageName)
|
||||
return args.Get(0).(*types.Session), args.Error(1)
|
||||
}
|
||||
@@ -119,7 +119,27 @@ func (m *Mock) UserLogin(loginRequest *types.LoginRequest, user *types.User) (*t
|
||||
args := m.Called(loginRequest, user)
|
||||
return args.Get(0).(*types.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *Mock) UserGet(id string) (*types.User, error) {
|
||||
args := m.Called(id)
|
||||
return args.Get(0).(*types.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *Mock) PlaygroundNew(playground types.Playground) (*types.Playground, error) {
|
||||
args := m.Called(playground)
|
||||
return args.Get(0).(*types.Playground), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *Mock) PlaygroundGet(id string) *types.Playground {
|
||||
args := m.Called(id)
|
||||
return args.Get(0).(*types.Playground)
|
||||
}
|
||||
|
||||
func (m *Mock) PlaygroundFindByDomain(domain string) *types.Playground {
|
||||
args := m.Called(domain)
|
||||
return args.Get(0).(*types.Playground)
|
||||
}
|
||||
func (m *Mock) PlaygroundList() ([]*types.Playground, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).([]*types.Playground), args.Error(1)
|
||||
}
|
||||
|
||||
36
pwd/playground.go
Normal file
36
pwd/playground.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package pwd
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||
"github.com/twinj/uuid"
|
||||
)
|
||||
|
||||
func (p *pwd) PlaygroundNew(playground types.Playground) (*types.Playground, error) {
|
||||
playground.Id = uuid.NewV5(uuid.NameSpaceURL, uuid.Name(playground.Domain)).String()
|
||||
if err := p.storage.PlaygroundPut(&playground); err != nil {
|
||||
log.Printf("Error saving playground %s. Got: %v\n", playground.Id, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &playground, nil
|
||||
}
|
||||
|
||||
func (p *pwd) PlaygroundGet(id string) *types.Playground {
|
||||
if playground, err := p.storage.PlaygroundGet(id); err != nil {
|
||||
log.Printf("Error retrieving playground %s. Got: %v\n", id, err)
|
||||
return nil
|
||||
} else {
|
||||
return playground
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pwd) PlaygroundFindByDomain(domain string) *types.Playground {
|
||||
id := uuid.NewV5(uuid.NameSpaceURL, uuid.Name(domain)).String()
|
||||
return p.PlaygroundGet(id)
|
||||
}
|
||||
|
||||
func (p *pwd) PlaygroundList() ([]*types.Playground, error) {
|
||||
return p.storage.PlaygroundGetAll()
|
||||
}
|
||||
152
pwd/playground_test.go
Normal file
152
pwd/playground_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package pwd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/play-with-docker/play-with-docker/docker"
|
||||
"github.com/play-with-docker/play-with-docker/event"
|
||||
"github.com/play-with-docker/play-with-docker/id"
|
||||
"github.com/play-with-docker/play-with-docker/provisioner"
|
||||
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||
"github.com/play-with-docker/play-with-docker/storage"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/twinj/uuid"
|
||||
)
|
||||
|
||||
func TestPlaygroundNew(t *testing.T) {
|
||||
_d := &docker.Mock{}
|
||||
_f := &docker.FactoryMock{}
|
||||
_s := &storage.Mock{}
|
||||
_g := &id.MockGenerator{}
|
||||
_e := &event.Mock{}
|
||||
|
||||
ipf := provisioner.NewInstanceProvisionerFactory(provisioner.NewWindowsASG(_f, _s), provisioner.NewDinD(_g, _f, _s))
|
||||
sp := provisioner.NewOverlaySessionProvisioner(_f)
|
||||
|
||||
_s.On("PlaygroundPut", mock.AnythingOfType("*types.Playground")).Return(nil)
|
||||
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
|
||||
expectedPlayground := types.Playground{Domain: "localhost", DefaultDinDInstanceImage: "franela/dind", AllowWindowsInstances: false, DefaultSessionDuration: time.Hour * 3, Extras: types.PlaygroundExtras{"foo": "bar"}}
|
||||
playground, e := p.PlaygroundNew(expectedPlayground)
|
||||
assert.Nil(t, e)
|
||||
assert.NotNil(t, playground)
|
||||
|
||||
expectedPlayground.Id = uuid.NewV5(uuid.NameSpaceURL, uuid.Name("localhost")).String()
|
||||
assert.Equal(t, expectedPlayground, *playground)
|
||||
|
||||
_d.AssertExpectations(t)
|
||||
_f.AssertExpectations(t)
|
||||
_s.AssertExpectations(t)
|
||||
_g.AssertExpectations(t)
|
||||
_e.M.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestPlaygroundGet(t *testing.T) {
|
||||
_d := &docker.Mock{}
|
||||
_f := &docker.FactoryMock{}
|
||||
_s := &storage.Mock{}
|
||||
_g := &id.MockGenerator{}
|
||||
_e := &event.Mock{}
|
||||
|
||||
_s.On("PlaygroundPut", mock.AnythingOfType("*types.Playground")).Return(nil)
|
||||
|
||||
ipf := provisioner.NewInstanceProvisionerFactory(provisioner.NewWindowsASG(_f, _s), provisioner.NewDinD(_g, _f, _s))
|
||||
sp := provisioner.NewOverlaySessionProvisioner(_f)
|
||||
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
|
||||
expectedPlayground := types.Playground{Domain: "localhost", DefaultDinDInstanceImage: "franela/dind", AllowWindowsInstances: false, DefaultSessionDuration: time.Hour * 3, Extras: types.PlaygroundExtras{"foo": "bar"}}
|
||||
playground, e := p.PlaygroundNew(expectedPlayground)
|
||||
assert.Nil(t, e)
|
||||
assert.NotNil(t, playground)
|
||||
|
||||
_s.On("PlaygroundGet", playground.Id).Return(playground, nil)
|
||||
|
||||
playground2 := p.PlaygroundGet(playground.Id)
|
||||
assert.NotNil(t, playground2)
|
||||
|
||||
assert.Equal(t, *playground, *playground2)
|
||||
|
||||
_d.AssertExpectations(t)
|
||||
_f.AssertExpectations(t)
|
||||
_s.AssertExpectations(t)
|
||||
_g.AssertExpectations(t)
|
||||
_e.M.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestPlaygroundFindByDomain(t *testing.T) {
|
||||
_d := &docker.Mock{}
|
||||
_f := &docker.FactoryMock{}
|
||||
_s := &storage.Mock{}
|
||||
_g := &id.MockGenerator{}
|
||||
_e := &event.Mock{}
|
||||
|
||||
_s.On("PlaygroundPut", mock.AnythingOfType("*types.Playground")).Return(nil)
|
||||
|
||||
ipf := provisioner.NewInstanceProvisionerFactory(provisioner.NewWindowsASG(_f, _s), provisioner.NewDinD(_g, _f, _s))
|
||||
sp := provisioner.NewOverlaySessionProvisioner(_f)
|
||||
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
|
||||
expectedPlayground := types.Playground{Domain: "localhost", DefaultDinDInstanceImage: "franela/dind", AllowWindowsInstances: false, DefaultSessionDuration: time.Hour * 3, Extras: types.PlaygroundExtras{"foo": "bar"}}
|
||||
playground, e := p.PlaygroundNew(expectedPlayground)
|
||||
assert.Nil(t, e)
|
||||
assert.NotNil(t, playground)
|
||||
|
||||
_s.On("PlaygroundGet", playground.Id).Return(playground, nil)
|
||||
|
||||
playground2 := p.PlaygroundFindByDomain("localhost")
|
||||
assert.NotNil(t, playground2)
|
||||
|
||||
assert.Equal(t, *playground, *playground2)
|
||||
|
||||
_d.AssertExpectations(t)
|
||||
_f.AssertExpectations(t)
|
||||
_s.AssertExpectations(t)
|
||||
_g.AssertExpectations(t)
|
||||
_e.M.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestPlaygroundList(t *testing.T) {
|
||||
_d := &docker.Mock{}
|
||||
_f := &docker.FactoryMock{}
|
||||
_s := &storage.Mock{}
|
||||
_g := &id.MockGenerator{}
|
||||
_e := &event.Mock{}
|
||||
|
||||
_s.On("PlaygroundPut", mock.AnythingOfType("*types.Playground")).Return(nil)
|
||||
|
||||
ipf := provisioner.NewInstanceProvisionerFactory(provisioner.NewWindowsASG(_f, _s), provisioner.NewDinD(_g, _f, _s))
|
||||
sp := provisioner.NewOverlaySessionProvisioner(_f)
|
||||
|
||||
p := NewPWD(_f, _e, _s, sp, ipf)
|
||||
p.generator = _g
|
||||
|
||||
pd := types.Playground{Domain: "localhost1", DefaultDinDInstanceImage: "franela/dind", AllowWindowsInstances: false, DefaultSessionDuration: time.Hour * 3, Extras: types.PlaygroundExtras{"foo": "bar"}}
|
||||
p1, e := p.PlaygroundNew(pd)
|
||||
assert.Nil(t, e)
|
||||
assert.NotNil(t, p1)
|
||||
|
||||
pd = types.Playground{Domain: "localhost2", DefaultDinDInstanceImage: "franela/dind", AllowWindowsInstances: false, DefaultSessionDuration: time.Hour * 3, Extras: types.PlaygroundExtras{"foo": "bar"}}
|
||||
p2, e := p.PlaygroundNew(pd)
|
||||
assert.Nil(t, e)
|
||||
assert.NotNil(t, p2)
|
||||
|
||||
_s.On("PlaygroundGetAll").Return([]*types.Playground{p1, p2}, nil)
|
||||
|
||||
received, err := p.PlaygroundList()
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, received)
|
||||
|
||||
_d.AssertExpectations(t)
|
||||
_f.AssertExpectations(t)
|
||||
_s.AssertExpectations(t)
|
||||
_g.AssertExpectations(t)
|
||||
_e.M.AssertExpectations(t)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func SessionNotEmpty(e error) bool {
|
||||
}
|
||||
|
||||
type PWDApi interface {
|
||||
SessionNew(userId string, duration time.Duration, stack string, stackName, imageName string) (*types.Session, error)
|
||||
SessionNew(playground *types.Playground, userId string, duration time.Duration, stack string, stackName, imageName string) (*types.Session, error)
|
||||
SessionClose(session *types.Session) error
|
||||
SessionGetSmallestViewPort(sessionId string) types.ViewPort
|
||||
SessionDeployStack(session *types.Session) error
|
||||
@@ -98,6 +98,11 @@ type PWDApi interface {
|
||||
UserGetLoginRequest(id string) (*types.LoginRequest, error)
|
||||
UserLogin(loginRequest *types.LoginRequest, user *types.User) (*types.User, error)
|
||||
UserGet(id string) (*types.User, error)
|
||||
|
||||
PlaygroundNew(playground types.Playground) (*types.Playground, error)
|
||||
PlaygroundGet(id string) *types.Playground
|
||||
PlaygroundFindByDomain(domain string) *types.Playground
|
||||
PlaygroundList() ([]*types.Playground, error)
|
||||
}
|
||||
|
||||
func NewPWD(f docker.FactoryApi, e event.EventApi, s storage.StorageApi, sp provisioner.SessionProvisionerApi, ipf provisioner.InstanceProvisionerFactoryApi) *pwd {
|
||||
|
||||
@@ -44,7 +44,7 @@ type SessionSetupInstanceConf struct {
|
||||
Tls bool `json:"tls"`
|
||||
}
|
||||
|
||||
func (p *pwd) SessionNew(userId string, duration time.Duration, stack, stackName, imageName string) (*types.Session, error) {
|
||||
func (p *pwd) SessionNew(playground *types.Playground, userId string, duration time.Duration, stack, stackName, imageName string) (*types.Session, error) {
|
||||
defer observeAction("SessionNew", time.Now())
|
||||
|
||||
s := &types.Session{}
|
||||
@@ -54,6 +54,7 @@ func (p *pwd) SessionNew(userId string, duration time.Duration, stack, stackName
|
||||
s.Ready = true
|
||||
s.Stack = stack
|
||||
s.UserId = userId
|
||||
s.PlaygroundId = playground.Id
|
||||
|
||||
if s.Stack != "" {
|
||||
s.Ready = false
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/play-with-docker/play-with-docker/event"
|
||||
"github.com/play-with-docker/play-with-docker/id"
|
||||
"github.com/play-with-docker/play-with-docker/provisioner"
|
||||
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||
"github.com/play-with-docker/play-with-docker/storage"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -45,7 +46,8 @@ func TestSessionNew(t *testing.T) {
|
||||
|
||||
before := time.Now()
|
||||
|
||||
s, e := p.SessionNew("", time.Hour, "", "", "")
|
||||
playground := &types.Playground{Id: "foobar"}
|
||||
s, e := p.SessionNew(playground, "", time.Hour, "", "", "")
|
||||
assert.Nil(t, e)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
@@ -56,12 +58,13 @@ func TestSessionNew(t *testing.T) {
|
||||
assert.WithinDuration(t, s.ExpiresAt, before.Add(time.Hour), time.Second)
|
||||
assert.True(t, s.Ready)
|
||||
|
||||
s, _ = p.SessionNew("", time.Hour, "stackPath", "stackName", "imageName")
|
||||
s, _ = p.SessionNew(playground, "", time.Hour, "stackPath", "stackName", "imageName")
|
||||
|
||||
assert.Equal(t, "stackPath", s.Stack)
|
||||
assert.Equal(t, "stackName", s.StackName)
|
||||
assert.Equal(t, "imageName", s.ImageName)
|
||||
assert.Equal(t, "localhost", s.Host)
|
||||
assert.Equal(t, playground.Id, s.PlaygroundId)
|
||||
assert.False(t, s.Ready)
|
||||
|
||||
_d.AssertExpectations(t)
|
||||
|
||||
83
pwd/types/playground.go
Normal file
83
pwd/types/playground.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PlaygroundExtras map[string]interface{}
|
||||
|
||||
func (e PlaygroundExtras) Get(name string) (interface{}, bool) {
|
||||
v, f := e[name]
|
||||
return v, f
|
||||
}
|
||||
func (e PlaygroundExtras) GetInt(name string) (int, bool) {
|
||||
v, f := e[name]
|
||||
if f {
|
||||
if r, ok := v.(int); ok {
|
||||
return r, ok
|
||||
} else if r, ok := v.(float64); ok {
|
||||
return int(r), ok
|
||||
} else if r, ok := v.(string); ok {
|
||||
if v, err := strconv.Atoi(r); err != nil {
|
||||
return 0, false
|
||||
} else {
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
return v.(int), f
|
||||
} else {
|
||||
return 0, f
|
||||
}
|
||||
}
|
||||
|
||||
func (e PlaygroundExtras) GetString(name string) (string, bool) {
|
||||
v, f := e[name]
|
||||
if f {
|
||||
if r, ok := v.(int); ok {
|
||||
return strconv.Itoa(r), ok
|
||||
} else if r, ok := v.(float64); ok {
|
||||
return strconv.FormatFloat(r, 'g', -1, 64), ok
|
||||
} else if r, ok := v.(bool); ok {
|
||||
return strconv.FormatBool(r), ok
|
||||
} else if r, ok := v.(string); ok {
|
||||
return r, ok
|
||||
} else {
|
||||
return "", false
|
||||
}
|
||||
} else {
|
||||
return "", f
|
||||
}
|
||||
}
|
||||
|
||||
func (e PlaygroundExtras) GetDuration(name string) (time.Duration, bool) {
|
||||
v, f := e[name]
|
||||
if f {
|
||||
if r, ok := v.(int); ok {
|
||||
return time.Duration(r), ok
|
||||
} else if r, ok := v.(float64); ok {
|
||||
return time.Duration(r), ok
|
||||
} else if r, ok := v.(string); ok {
|
||||
if d, err := time.ParseDuration(r); err != nil {
|
||||
return time.Duration(0), false
|
||||
} else {
|
||||
return d, true
|
||||
}
|
||||
} else {
|
||||
return time.Duration(0), false
|
||||
}
|
||||
} else {
|
||||
return time.Duration(0), f
|
||||
}
|
||||
}
|
||||
|
||||
type Playground struct {
|
||||
Id string `json:"id" bson:"id"`
|
||||
Domain string `json:"domain" bson:"domain"`
|
||||
DefaultDinDInstanceImage string `json:"default_dind_instance_image" bson:"default_dind_instance_image"`
|
||||
AvailableDinDInstanceImages []string `json:"available_dind_instance_images" bson:"available_dind_instance_images"`
|
||||
AllowWindowsInstances bool `json:"allow_windows_instances" bson:"allow_windows_instances"`
|
||||
DefaultSessionDuration time.Duration `json:"default_session_duration" bson:"default_session_duration"`
|
||||
L2RouterIP string `json:"l2_router_ip" bson:"l2_router_ip"`
|
||||
Extras PlaygroundExtras `json:"extras" bson:"extras"`
|
||||
}
|
||||
119
pwd/types/playground_test.go
Normal file
119
pwd/types/playground_test.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/twinj/uuid"
|
||||
)
|
||||
|
||||
func TestPlayground_Extras_GetInt(t *testing.T) {
|
||||
p := Playground{
|
||||
Id: uuid.NewV4().String(),
|
||||
Domain: "localhost",
|
||||
DefaultDinDInstanceImage: "franel/dind",
|
||||
AllowWindowsInstances: false,
|
||||
DefaultSessionDuration: time.Hour * 4,
|
||||
Extras: PlaygroundExtras{
|
||||
"intFromInt": 10,
|
||||
"intFromFloat": 32.0,
|
||||
"intFromString": "15",
|
||||
},
|
||||
}
|
||||
|
||||
b, err := json.Marshal(p)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var p2 Playground
|
||||
json.Unmarshal(b, &p2)
|
||||
|
||||
v, found := p2.Extras.GetInt("intFromInt")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, 10, v)
|
||||
|
||||
v, found = p2.Extras.GetInt("intFromFloat")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, 32, v)
|
||||
|
||||
v, found = p2.Extras.GetInt("intFromString")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, 15, v)
|
||||
}
|
||||
|
||||
func TestPlayground_Extras_GetString(t *testing.T) {
|
||||
p := Playground{
|
||||
Id: uuid.NewV4().String(),
|
||||
Domain: "localhost",
|
||||
DefaultDinDInstanceImage: "franel/dind",
|
||||
AllowWindowsInstances: false,
|
||||
DefaultSessionDuration: time.Hour * 4,
|
||||
Extras: PlaygroundExtras{
|
||||
"stringFromInt": 10,
|
||||
"stringFromFloat": 32.3,
|
||||
"stringFromString": "15",
|
||||
"stringFromBool": false,
|
||||
},
|
||||
}
|
||||
|
||||
b, err := json.Marshal(p)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var p2 Playground
|
||||
json.Unmarshal(b, &p2)
|
||||
|
||||
v, found := p2.Extras.GetString("stringFromInt")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, "10", v)
|
||||
|
||||
v, found = p2.Extras.GetString("stringFromFloat")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, "32.3", v)
|
||||
|
||||
v, found = p2.Extras.GetString("stringFromString")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, "15", v)
|
||||
|
||||
v, found = p2.Extras.GetString("stringFromBool")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, "false", v)
|
||||
}
|
||||
|
||||
func TestPlayground_Extras_GetDuration(t *testing.T) {
|
||||
p := Playground{
|
||||
Id: uuid.NewV4().String(),
|
||||
Domain: "localhost",
|
||||
DefaultDinDInstanceImage: "franel/dind",
|
||||
AllowWindowsInstances: false,
|
||||
DefaultSessionDuration: time.Hour * 4,
|
||||
Extras: PlaygroundExtras{
|
||||
"durationFromInt": 10,
|
||||
"durationFromFloat": 32.3,
|
||||
"durationFromString": "4h",
|
||||
"durationFromDuration": time.Hour * 3,
|
||||
},
|
||||
}
|
||||
|
||||
b, err := json.Marshal(p)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var p2 Playground
|
||||
json.Unmarshal(b, &p2)
|
||||
|
||||
v, found := p2.Extras.GetDuration("durationFromInt")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, time.Duration(10), v)
|
||||
|
||||
v, found = p2.Extras.GetDuration("durationFromFloat")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, time.Duration(32), v)
|
||||
|
||||
v, found = p2.Extras.GetDuration("durationFromString")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, time.Hour*4, v)
|
||||
|
||||
v, found = p2.Extras.GetDuration("durationFromDuration")
|
||||
assert.True(t, found)
|
||||
assert.Equal(t, time.Hour*3, v)
|
||||
}
|
||||
@@ -15,4 +15,5 @@ type Session struct {
|
||||
ImageName string `json:"image_name" bson:"image_name"`
|
||||
Host string `json:"host" bson:"host"`
|
||||
UserId string `json:"user_id" bson:"user_id"`
|
||||
PlaygroundId string `json:"playground_id" bson:"playground_id"`
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ type DB struct {
|
||||
WindowsInstances map[string]*types.WindowsInstance `json:"windows_instances"`
|
||||
LoginRequests map[string]*types.LoginRequest `json:"login_requests"`
|
||||
Users map[string]*types.User `json:"user"`
|
||||
Playgrounds map[string]*types.Playground `json:"playgrounds"`
|
||||
|
||||
WindowsInstancesBySessionId map[string][]string `json:"windows_instances_by_session_id"`
|
||||
InstancesBySessionId map[string][]string `json:"instances_by_session_id"`
|
||||
@@ -364,6 +365,25 @@ func (store *storage) UserGet(id string) (*types.User, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (store *storage) PlaygroundPut(playground *types.Playground) error {
|
||||
store.rw.Lock()
|
||||
defer store.rw.Unlock()
|
||||
|
||||
store.db.Playgrounds[playground.Id] = playground
|
||||
|
||||
return store.save()
|
||||
}
|
||||
func (store *storage) PlaygroundGet(id string) (*types.Playground, error) {
|
||||
store.rw.Lock()
|
||||
defer store.rw.Unlock()
|
||||
if playground, found := store.db.Playgrounds[id]; !found {
|
||||
return nil, NotFoundError
|
||||
} else {
|
||||
return playground, nil
|
||||
}
|
||||
return nil, NotFoundError
|
||||
}
|
||||
|
||||
func (store *storage) load() error {
|
||||
file, err := os.Open(store.path)
|
||||
|
||||
@@ -376,12 +396,13 @@ func (store *storage) load() error {
|
||||
}
|
||||
} else {
|
||||
store.db = &DB{
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
@@ -392,6 +413,19 @@ func (store *storage) load() error {
|
||||
file.Close()
|
||||
return nil
|
||||
}
|
||||
func (store *storage) PlaygroundGetAll() ([]*types.Playground, error) {
|
||||
store.rw.Lock()
|
||||
defer store.rw.Unlock()
|
||||
|
||||
playgrounds := make([]*types.Playground, len(store.db.Playgrounds))
|
||||
i := 0
|
||||
for _, p := range store.db.Playgrounds {
|
||||
playgrounds[i] = p
|
||||
i++
|
||||
}
|
||||
|
||||
return playgrounds, nil
|
||||
}
|
||||
|
||||
func (store *storage) save() error {
|
||||
file, err := os.Create(store.path)
|
||||
|
||||
@@ -30,12 +30,13 @@ func TestSessionPut(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{s.Id: s},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{s.Id: s},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
@@ -59,12 +60,13 @@ func TestSessionPut(t *testing.T) {
|
||||
func TestSessionGet(t *testing.T) {
|
||||
expectedSession := &types.Session{Id: "aaabbbccc"}
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{expectedSession.Id: expectedSession},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{expectedSession.Id: expectedSession},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
@@ -98,12 +100,13 @@ func TestSessionGetAll(t *testing.T) {
|
||||
s1 := &types.Session{Id: "aaabbbccc"}
|
||||
s2 := &types.Session{Id: "dddeeefff"}
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{s1.Id: s1, s2.Id: s2},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{s1.Id: s1, s2.Id: s2},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
@@ -163,12 +166,13 @@ func TestSessionDelete(t *testing.T) {
|
||||
func TestInstanceGet(t *testing.T) {
|
||||
expectedInstance := &types.Instance{SessionId: "aaabbbccc", Name: "i1", IP: "10.0.0.1"}
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{expectedInstance.Name: expectedInstance},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{expectedInstance.Name: expectedInstance},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{expectedInstance.SessionId: []string{expectedInstance.Name}},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
@@ -217,12 +221,13 @@ func TestInstancePut(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{s.Id: s},
|
||||
Instances: map[string]*types.Instance{i.Name: i},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{s.Id: s},
|
||||
Instances: map[string]*types.Instance{i.Name: i},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{i.SessionId: []string{i.Name}},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
@@ -280,12 +285,13 @@ func TestInstanceFindBySessionId(t *testing.T) {
|
||||
i1 := &types.Instance{SessionId: "aaabbbccc", Name: "c1"}
|
||||
i2 := &types.Instance{SessionId: "aaabbbccc", Name: "c2"}
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{i1.Name: i1, i2.Name: i2},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{i1.Name: i1, i2.Name: i2},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{i1.SessionId: []string{i1.Name, i2.Name}},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
@@ -316,12 +322,13 @@ func TestWindowsInstanceGetAll(t *testing.T) {
|
||||
i1 := &types.WindowsInstance{SessionId: "aaabbbccc", Id: "i1"}
|
||||
i2 := &types.WindowsInstance{SessionId: "aaabbbccc", Id: "i2"}
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{i1.Id: i1, i2.Id: i2},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{i1.Id: i1, i2.Id: i2},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{i1.SessionId: []string{i1.Id, i2.Id}},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
@@ -371,12 +378,13 @@ func TestWindowsInstancePut(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{s.Id: s},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{i.Id: i},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{s.Id: s},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{i.Id: i},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{i.SessionId: []string{i.Id}},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
@@ -433,12 +441,13 @@ func TestWindowsInstanceDelete(t *testing.T) {
|
||||
func TestClientGet(t *testing.T) {
|
||||
c := &types.Client{SessionId: "aaabbbccc", Id: "c1"}
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{c.Id: c},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{c.Id: c},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{c.SessionId: []string{c.Id}},
|
||||
@@ -487,12 +496,13 @@ func TestClientPut(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{s.Id: s},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{c.Id: c},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{s.Id: s},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{c.Id: c},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{c.SessionId: []string{c.Id}},
|
||||
@@ -550,12 +560,13 @@ func TestClientFindBySessionId(t *testing.T) {
|
||||
c1 := &types.Client{SessionId: "aaabbbccc", Id: "c1"}
|
||||
c2 := &types.Client{SessionId: "aaabbbccc", Id: "c2"}
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{c1.Id: c1, c2.Id: c2},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{c1.Id: c1, c2.Id: c2},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{c1.SessionId: []string{c1.Id, c2.Id}},
|
||||
@@ -581,3 +592,120 @@ func TestClientFindBySessionId(t *testing.T) {
|
||||
assert.Subset(t, clients, []*types.Client{c1, c2})
|
||||
assert.Len(t, clients, 2)
|
||||
}
|
||||
|
||||
func TestPlaygroundGet(t *testing.T) {
|
||||
p := &types.Playground{Id: "aaabbbccc"}
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{p.Id: p},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
UsersByProvider: map[string]string{},
|
||||
}
|
||||
|
||||
tmpfile, err := ioutil.TempFile("", "pwd")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
encoder := json.NewEncoder(tmpfile)
|
||||
err = encoder.Encode(&expectedDB)
|
||||
assert.Nil(t, err)
|
||||
tmpfile.Close()
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
storage, err := NewFileStorage(tmpfile.Name())
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
found, err := storage.PlaygroundGet("aaabbbccc")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, p, found)
|
||||
}
|
||||
|
||||
func TestPlaygroundPut(t *testing.T) {
|
||||
tmpfile, err := ioutil.TempFile("", "pwd")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tmpfile.Close()
|
||||
os.Remove(tmpfile.Name())
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
storage, err := NewFileStorage(tmpfile.Name())
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
p := &types.Playground{Id: "aaabbbccc"}
|
||||
|
||||
err = storage.PlaygroundPut(p)
|
||||
assert.Nil(t, err)
|
||||
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{p.Id: p},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
UsersByProvider: map[string]string{},
|
||||
}
|
||||
var loadedDB *DB
|
||||
|
||||
file, err := os.Open(tmpfile.Name())
|
||||
|
||||
assert.Nil(t, err)
|
||||
defer file.Close()
|
||||
|
||||
decoder := json.NewDecoder(file)
|
||||
err = decoder.Decode(&loadedDB)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.EqualValues(t, expectedDB, loadedDB)
|
||||
}
|
||||
|
||||
func TestPlaygroundGetAll(t *testing.T) {
|
||||
p1 := &types.Playground{Id: "aaabbbccc"}
|
||||
p2 := &types.Playground{Id: "dddeeefff"}
|
||||
expectedDB := &DB{
|
||||
Sessions: map[string]*types.Session{},
|
||||
Instances: map[string]*types.Instance{},
|
||||
Clients: map[string]*types.Client{},
|
||||
WindowsInstances: map[string]*types.WindowsInstance{},
|
||||
LoginRequests: map[string]*types.LoginRequest{},
|
||||
Users: map[string]*types.User{},
|
||||
Playgrounds: map[string]*types.Playground{p1.Id: p1, p2.Id: p2},
|
||||
WindowsInstancesBySessionId: map[string][]string{},
|
||||
InstancesBySessionId: map[string][]string{},
|
||||
ClientsBySessionId: map[string][]string{},
|
||||
UsersByProvider: map[string]string{},
|
||||
}
|
||||
|
||||
tmpfile, err := ioutil.TempFile("", "pwd")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
encoder := json.NewEncoder(tmpfile)
|
||||
err = encoder.Encode(&expectedDB)
|
||||
assert.Nil(t, err)
|
||||
tmpfile.Close()
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
storage, err := NewFileStorage(tmpfile.Name())
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
found, err := storage.PlaygroundGetAll()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, []*types.Playground{p1, p2}, found)
|
||||
}
|
||||
|
||||
@@ -106,3 +106,15 @@ func (m *Mock) UserGet(id string) (*types.User, error) {
|
||||
args := m.Called(id)
|
||||
return args.Get(0).(*types.User), args.Error(1)
|
||||
}
|
||||
func (m *Mock) PlaygroundPut(playground *types.Playground) error {
|
||||
args := m.Called(playground)
|
||||
return args.Error(0)
|
||||
}
|
||||
func (m *Mock) PlaygroundGet(id string) (*types.Playground, error) {
|
||||
args := m.Called(id)
|
||||
return args.Get(0).(*types.Playground), args.Error(1)
|
||||
}
|
||||
func (m *Mock) PlaygroundGetAll() ([]*types.Playground, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).([]*types.Playground), args.Error(1)
|
||||
}
|
||||
|
||||
@@ -42,4 +42,8 @@ type StorageApi interface {
|
||||
UserFindByProvider(providerName, providerUserId string) (*types.User, error)
|
||||
UserPut(user *types.User) error
|
||||
UserGet(id string) (*types.User, error)
|
||||
|
||||
PlaygroundPut(playground *types.Playground) error
|
||||
PlaygroundGet(id string) (*types.Playground, error)
|
||||
PlaygroundGetAll() ([]*types.Playground, error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user