From 55028efa88ec71738fdb5c8650f5b99b7e6c0f45 Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Wed, 17 May 2017 13:02:27 -0300 Subject: [PATCH 01/13] Update development instructions --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cd0d4bf..8e12e43 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ the daemon won't load it automatically. Run the following command for that purpo Start the Docker daemon on your machine and run `docker pull franela/dind`. -1) Install go 1.7.1 with `brew` on Mac or through a package manager. +1) Install go 1.7.1+ with `brew` on Mac or through a package manager. 2) `go get -v -d -t ./...` @@ -35,13 +35,25 @@ Notes: * There is a hard-coded limit to 5 Docker playgrounds per session. After 4 hours sessions are deleted. * If you want to override the DIND version or image then set the environmental variable i.e. `DIND_IMAGE=franela/docker-rc:dind`. Take into account that you can't use standard `dind` images, only [franela](https://hub.docker.com/r/franela/) ones work. + +### Port forwarding +In order for port forwarding to work correctly in development you need to make `*.localhost` to resolve to `127.0.0.1`. That way when you try to access to `pwd10-0-0-1-8080.host1.localhost`, then you're forwarded correctly to your local PWD server. + +You can achieve this by setting up a `dnsmasq` server (you can run it in a docker container also) and adding the following configuration: + +``` +address=/localhost/127.0.0.1 +``` + +### Building the dind image myself. + +If you want to make changes to the `dind` image being used, make your changes to the `Dockerfile.dind` file and then build it using this command: `docker build --build-arg docker_storage_driver=vfs -f Dockerfile.dind -t franela/dind .` ## FAQ ### How can I connect to a published port from the outside world? -~~We're planning to setup a reverse proxy that handles redirection automatically, in the meantime you can use [ngrok](https://ngrok.com) within PWD running `docker run --name supergrok -d jpetazzo/supergrok` then `docker logs --follow supergrok` , it will give you a ngrok URL, now you can go to that URL and add the IP+port that you want to connect to… e.g. if your PWD instance is 10.0.42.3, you can go to http://xxxxxx.ngrok.io/10.0.42.3:8000 (where the xxxxxx is given to you in the supergrok logs).~~ If you need to access your services from outside, use the following URL pattern `http://pwd-..labs.play-with-docker.com` (i.e: http://pwd10_2_135_3-80.host3.labs.play-with-docker.com/). From dced4910d1c31b8edc4abe315491b082f3af1b4b Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Wed, 17 May 2017 13:08:34 -0300 Subject: [PATCH 02/13] Add dind build instructions --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8e12e43..2a8e4a7 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ You can achieve this by setting up a `dnsmasq` server (you can run it in a docke address=/localhost/127.0.0.1 ``` +Don't forget to change your computer default DNS to + ### Building the dind image myself. If you want to make changes to the `dind` image being used, make your changes to the `Dockerfile.dind` file and then build it using this command: `docker build --build-arg docker_storage_driver=vfs -f Dockerfile.dind -t franela/dind .` From caf42df624d3856e979a9267a842ad053cbe84bb Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Wed, 17 May 2017 13:22:15 -0300 Subject: [PATCH 03/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a8e4a7..6f92fe8 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ You can achieve this by setting up a `dnsmasq` server (you can run it in a docke address=/localhost/127.0.0.1 ``` -Don't forget to change your computer default DNS to +Don't forget to change your computer default DNS to use the dnsmasq server to resolve. ### Building the dind image myself. From 317509d3dff2cfd56141cec979adc1c081babbf9 Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Thu, 18 May 2017 10:57:35 -0300 Subject: [PATCH 04/13] Update compose and machine versions --- Dockerfile.dind | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.dind b/Dockerfile.dind index aa7dbf2..ca3f7a0 100644 --- a/Dockerfile.dind +++ b/Dockerfile.dind @@ -3,8 +3,8 @@ FROM docker:${VERSION} RUN apk add --no-cache git tmux py2-pip apache2-utils vim build-base gettext-dev curl bash-completion bash util-linux jq -ENV COMPOSE_VERSION=1.12.0 -ENV MACHINE_VERSION=v0.10.0 +ENV COMPOSE_VERSION=1.13.0 +ENV MACHINE_VERSION=v0.11.0 # Install Compose and Machine RUN pip install docker-compose==${COMPOSE_VERSION} RUN curl -L https://github.com/docker/machine/releases/download/${MACHINE_VERSION}/docker-machine-Linux-x86_64 \ From 637b0149319308f531d233f271de88cf1e4d6057 Mon Sep 17 00:00:00 2001 From: "Jonathan Leibiusky @xetorthio" Date: Thu, 18 May 2017 13:16:34 -0300 Subject: [PATCH 05/13] Allow external stacks. Also default to `stack.yml` if file was not specified. --- handlers/new_session.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/handlers/new_session.go b/handlers/new_session.go index fc344de..43f6e99 100644 --- a/handlers/new_session.go +++ b/handlers/new_session.go @@ -5,6 +5,8 @@ import ( "fmt" "log" "net/http" + "path" + "strings" "github.com/play-with-docker/play-with-docker/config" "github.com/play-with-docker/play-with-docker/services" @@ -27,6 +29,7 @@ func NewSession(rw http.ResponseWriter, req *http.Request) { stack := req.Form.Get("stack") if stack != "" { + stack = formatStack(stack) if ok, err := stackExists(stack); err != nil { log.Printf("Error retrieving stack: %s", err) rw.WriteHeader(http.StatusInternalServerError) @@ -61,13 +64,24 @@ func NewSession(rw http.ResponseWriter, req *http.Request) { } } +func formatStack(stack string) string { + if !strings.HasSuffix(stack, ".yml") { + // If it doesn't end with ".yml", assume it hasn't been specified, then default to "stack.yml" + stack = path.Join(stack, "stack.yml") + } + if strings.HasPrefix(stack, "/") { + // The host is anonymous, then use our own stack repo. + stack = fmt.Sprintf("%s%s", "https://raw.githubusercontent.com/play-with-docker/stacks/master", stack) + } + return stack +} + func stackExists(stack string) (bool, error) { - resp, err := http.Head("https://raw.githubusercontent.com/play-with-docker/stacks/master" + stack) + resp, err := http.Head(stack) if err != nil { return false, err } defer resp.Body.Close() return resp.StatusCode == 200, nil - } From 0a061c235b06fb91f229800bc3812a381a62b283 Mon Sep 17 00:00:00 2001 From: "Jonathan Leibiusky @xetorthio" Date: Thu, 18 May 2017 17:19:49 -0300 Subject: [PATCH 06/13] Initialize swarm and do stack deploy instead of docker compose --- services/session.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/session.go b/services/session.go index 7ea360a..a068a00 100644 --- a/services/session.go +++ b/services/session.go @@ -111,9 +111,14 @@ func (s *Session) DeployStack() error { return err } + _, err = Exec(i.Name, []string{"docker", "swarm", "init", "--advertise-addr", "eth0"}) + if err != nil { + log.Printf("Error executing stack [%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) + code, err := ExecAttach(i.Name, []string{"docker", "stack", "deploy", "-c", "/var/run/pwd/uploads/" + fileName, "pwd"}, &w) if err != nil { log.Printf("Error executing stack [%s]: %s\n", s.Stack, err) return err From f40c527e3c8c867f2478d8ede2b04cd05750e585 Mon Sep 17 00:00:00 2001 From: "Jonathan Leibiusky @xetorthio" Date: Thu, 18 May 2017 17:30:47 -0300 Subject: [PATCH 07/13] Do everything in a single command and pull all images before deploying the stack to the swarm. --- services/session.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/services/session.go b/services/session.go index a068a00..e347a4e 100644 --- a/services/session.go +++ b/services/session.go @@ -111,14 +111,12 @@ func (s *Session) DeployStack() error { return err } - _, err = Exec(i.Name, []string{"docker", "swarm", "init", "--advertise-addr", "eth0"}) - if err != nil { - log.Printf("Error executing stack [%s]: %s\n", s.Stack, err) - return err - } - w := sessionBuilderWriter{session: s} fileName := path.Base(s.Stack) - code, err := ExecAttach(i.Name, []string{"docker", "stack", "deploy", "-c", "/var/run/pwd/uploads/" + fileName, "pwd"}, &w) + file := fmt.Sprintf("/var/run/pwd/uploads/%s", fileName) + cmd := fmt.Sprintf("docker swarm init --advertise-addr eth0 && docker-compose -f %s pull && docker stack deploy -c %s pwd", file, file) + + w := sessionBuilderWriter{session: s} + code, err := ExecAttach(i.Name, []string{"sh", "-c", cmd}, &w) if err != nil { log.Printf("Error executing stack [%s]: %s\n", s.Stack, err) return err From 99ea172c30d76b6cfa19e0e7ef61d8bd6f939853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20Vilari=C3=B1o?= Date: Thu, 18 May 2017 18:33:33 -0300 Subject: [PATCH 08/13] Remove close alert when user is signed out (#143) * Remove close browser tab alert when signed out * Remove whitespace line --- www/assets/app.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/assets/app.js b/www/assets/app.js index a4d9dc0..e90b8d0 100644 --- a/www/assets/app.js +++ b/www/assets/app.js @@ -23,7 +23,7 @@ $scope.newInstanceBtnText = '+ Add new instance'; $scope.deleteInstanceBtnText = 'Delete'; $scope.isInstanceBeingDeleted = false; - + var selectedKeyboardShortcuts = KeyboardShortcutService.getCurrentShortcuts(); angular.element($window).bind('resize', function() { @@ -55,6 +55,8 @@ KeyboardShortcutService.setResizeFunc($scope.resize); $scope.closeSession = function() { + // Remove alert before closing browser tab + window.onbeforeunload = null; $scope.socket.emit('session close'); } From a703f229970cd55c432c53406927226090842eea Mon Sep 17 00:00:00 2001 From: "Jonathan Leibiusky @xetorthio" Date: Fri, 19 May 2017 10:16:00 -0300 Subject: [PATCH 09/13] Fixes #145 --- handlers/home.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/handlers/home.go b/handlers/home.go index 6731098..d03d521 100644 --- a/handlers/home.go +++ b/handlers/home.go @@ -12,6 +12,12 @@ func Home(w http.ResponseWriter, r *http.Request) { sessionId := vars["sessionId"] s := services.GetSession(sessionId) + if s == nil { + // Session doesn't exist (can happen if closing the sessions an reloading the page, or similar). + w.WriteHeader(http.StatusNotFound) + return + } + if s.Stack != "" { go s.DeployStack() } From 55062234fd08475d2cce285006f551836fa11416 Mon Sep 17 00:00:00 2001 From: "Jonathan Leibiusky @xetorthio" Date: Fri, 19 May 2017 10:42:30 -0300 Subject: [PATCH 10/13] Allow to specify stack name as a parameter --- handlers/new_session.go | 3 ++- services/session.go | 9 +++++++-- www/bypass.html | 5 +++++ www/welcome.html | 5 +++++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/handlers/new_session.go b/handlers/new_session.go index 43f6e99..dde29d1 100644 --- a/handlers/new_session.go +++ b/handlers/new_session.go @@ -27,6 +27,7 @@ func NewSession(rw http.ResponseWriter, req *http.Request) { reqDur := req.Form.Get("session-duration") stack := req.Form.Get("stack") + stackName := req.Form.Get("stack_name") if stack != "" { stack = formatStack(stack) @@ -42,7 +43,7 @@ func NewSession(rw http.ResponseWriter, req *http.Request) { } duration := services.GetDuration(reqDur) - s, err := services.NewSession(duration, stack) + s, err := services.NewSession(duration, stack, stackName) if err != nil { log.Println(err) //TODO: Return some error code diff --git a/services/session.go b/services/session.go index e347a4e..7c26593 100644 --- a/services/session.go +++ b/services/session.go @@ -59,6 +59,7 @@ type Session struct { PwdIpAddress string `json:"pwd_ip_address"` Ready bool `json:"ready"` Stack string `json:"stack"` + StackName string `json:"stack_name"` } type sessionBuilderWriter struct { @@ -113,7 +114,7 @@ func (s *Session) DeployStack() error { fileName := path.Base(s.Stack) file := fmt.Sprintf("/var/run/pwd/uploads/%s", fileName) - cmd := fmt.Sprintf("docker swarm init --advertise-addr eth0 && docker-compose -f %s pull && docker stack deploy -c %s pwd", file, file) + cmd := fmt.Sprintf("docker swarm init --advertise-addr eth0 && docker-compose -f %s pull && docker stack deploy -c %s %s", file, file, s.StackName) w := sessionBuilderWriter{session: s} code, err := ExecAttach(i.Name, []string{"sh", "-c", cmd}, &w) @@ -300,7 +301,7 @@ func GetDuration(reqDur string) time.Duration { return defaultDuration } -func NewSession(duration time.Duration, stack string) (*Session, error) { +func NewSession(duration time.Duration, stack, stackName string) (*Session, error) { s := &Session{} s.Id = uuid.NewV4().String() s.Instances = map[string]*Instance{} @@ -310,6 +311,10 @@ func NewSession(duration time.Duration, stack string) (*Session, error) { s.Ready = true } s.Stack = stack + if stackName == "" { + stackName = "pwd" + } + s.StackName = stackName log.Printf("NewSession id=[%s]\n", s.Id) // Schedule cleanup of the session diff --git a/www/bypass.html b/www/bypass.html index 9a47d4a..c33922f 100644 --- a/www/bypass.html +++ b/www/bypass.html @@ -12,6 +12,7 @@
+
@@ -37,5 +38,9 @@ if (stack) { document.getElementById('stack').value = stack; } + var stackName = getParameterByName('stack_name'); + if (stack) { + document.getElementById('stack_name').value = stackName; + } diff --git a/www/welcome.html b/www/welcome.html index b51b7fd..4ed88ca 100644 --- a/www/welcome.html +++ b/www/welcome.html @@ -15,6 +15,7 @@
+ @@ -38,6 +39,10 @@ if (stack) { document.getElementById('stack').value = stack; } + var stackName = getParameterByName('stack_name'); + if (stack) { + document.getElementById('stack_name').value = stackName; + } if (document.cookie.indexOf('session_id') > -1) { document.getElementById('create').style = ""; document.getElementById('recaptcha').style = "display:none;"; From c319f0040e14da9628f66fd1e27e320280ed9682 Mon Sep 17 00:00:00 2001 From: "Jonathan Leibiusky @xetorthio" Date: Fri, 19 May 2017 17:25:30 -0300 Subject: [PATCH 11/13] Bad parameter checking --- www/welcome.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/welcome.html b/www/welcome.html index 4ed88ca..6effe5f 100644 --- a/www/welcome.html +++ b/www/welcome.html @@ -40,7 +40,7 @@ document.getElementById('stack').value = stack; } var stackName = getParameterByName('stack_name'); - if (stack) { + if (stackName) { document.getElementById('stack_name').value = stackName; } if (document.cookie.indexOf('session_id') > -1) { From 3ecdd1ea5204e7809f441f9a27e9485ee57a4b65 Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Tue, 23 May 2017 15:50:16 -0300 Subject: [PATCH 12/13] Fix launching instances with specific image --- services/docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/docker.go b/services/docker.go index bed7ece..dcaa2f8 100644 --- a/services/docker.go +++ b/services/docker.go @@ -285,7 +285,7 @@ func CreateInstance(session *Session, conf InstanceConfig) (*Instance, error) { env = append(env, fmt.Sprintf("PWD_IP_ADDRESS=%s", session.PwdIpAddress)) cf := &container.Config{Hostname: nodeName, - Image: dindImage, + Image: conf.ImageName, Tty: true, OpenStdin: true, AttachStdin: true, From db1c06a99f39116c2f099ccbe9b7984941ea498b Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Tue, 23 May 2017 16:18:02 -0300 Subject: [PATCH 13/13] Add multi-stage builds and fix safari scrollbar --- Dockerfile | 15 +++++++++++---- Dockerfile.dind | 17 +++++++++-------- build.sh | 3 --- www/assets/style.css | 12 +++++++++++- 4 files changed, 31 insertions(+), 16 deletions(-) delete mode 100755 build.sh diff --git a/Dockerfile b/Dockerfile index 1183ed9..a88c2b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,8 +12,15 @@ RUN go get -v -d ./... RUN CGO_ENABLED=0 go build -a -installsuffix nocgo -o /go/bin/play-with-docker . -# Set the workdir to be /go/bin which is where the binaries are built -WORKDIR /go/bin +FROM alpine -# Export the WORKDIR as a tar stream -CMD tar -cf - . +RUN apk --update add ca-certificates +RUN mkdir -p /app/pwd + +COPY --from=0 /go/bin/play-with-docker /app/play-with-docker +COPY ./www /app/www + +WORKDIR /app +CMD ["./play-with-docker"] + +EXPOSE 3000 diff --git a/Dockerfile.dind b/Dockerfile.dind index ca3f7a0..bba9a2d 100644 --- a/Dockerfile.dind +++ b/Dockerfile.dind @@ -1,14 +1,8 @@ -ARG VERSION=17.05.0-ce-dind -FROM docker:${VERSION} +ARG VERSION=docker:17.05.0-ce-dind +FROM ${VERSION} RUN apk add --no-cache git tmux py2-pip apache2-utils vim build-base gettext-dev curl bash-completion bash util-linux jq -ENV COMPOSE_VERSION=1.13.0 -ENV MACHINE_VERSION=v0.11.0 -# Install Compose and Machine -RUN pip install docker-compose==${COMPOSE_VERSION} -RUN curl -L https://github.com/docker/machine/releases/download/${MACHINE_VERSION}/docker-machine-Linux-x86_64 \ - -o /usr/bin/docker-machine && chmod +x /usr/bin/docker-machine # Compile and install httping # (used in orchestration workshop, and very useful anyway) @@ -18,6 +12,13 @@ RUN mkdir -p /opt && cd /opt && \ ./configure && make install LDFLAGS=-lintl && \ rm -rf httping-2.5 +ENV COMPOSE_VERSION=1.13.0 +ENV MACHINE_VERSION=v0.11.0 +# Install Compose and Machine +RUN pip install docker-compose==${COMPOSE_VERSION} +RUN curl -L https://github.com/docker/machine/releases/download/${MACHINE_VERSION}/docker-machine-Linux-x86_64 \ + -o /usr/bin/docker-machine && chmod +x /usr/bin/docker-machine + # Add bash completion RUN mkdir /etc/bash_completion.d && curl https://raw.githubusercontent.com/docker/docker/master/contrib/completion/bash/docker -o /etc/bash_completion.d/docker diff --git a/build.sh b/build.sh deleted file mode 100755 index 062ae61..0000000 --- a/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t builder .&& docker run --rm builder | docker build -t franela/play-with-docker:latest - diff --git a/www/assets/style.css b/www/assets/style.css index 6bfdd36..a6cbefe 100644 --- a/www/assets/style.css +++ b/www/assets/style.css @@ -1,7 +1,7 @@ @import url('https://fonts.googleapis.com/css?family=Rationale'); .selected button { - background-color: rgba(158,158,158,0.2); + background-color: rgba(158,158,158,0.2); } md-card-content.terminal-container { @@ -49,3 +49,13 @@ md-input-container .md-errors-spacer { .stats { min-height: 230px; } + +::-webkit-scrollbar { + -webkit-appearance: none; + width: 7px; +} +::-webkit-scrollbar-thumb { + border-radius: 4px; + background-color: rgba(0,0,0,.5); + -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5); +}