Merge branch 'jonas_master' into refactor_test_1
This commit is contained in:
15
Dockerfile
15
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
|
||||
|
||||
@@ -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.12.0
|
||||
ENV MACHINE_VERSION=v0.10.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
|
||||
|
||||
|
||||
18
README.md
18
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,27 @@ 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<version>-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
|
||||
```
|
||||
|
||||
Don't forget to change your computer default DNS to use the dnsmasq server to resolve.
|
||||
|
||||
### 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<underscore_ip>-<port>.<host#>.labs.play-with-docker.com` (i.e: http://pwd10_2_135_3-80.host3.labs.play-with-docker.com/).
|
||||
|
||||
|
||||
3
build.sh
3
build.sh
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker build -t builder .&& docker run --rm builder | docker build -t franela/play-with-docker:latest -
|
||||
@@ -11,8 +11,14 @@ func Home(w http.ResponseWriter, r *http.Request) {
|
||||
sessionId := vars["sessionId"]
|
||||
|
||||
s := core.SessionGet(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 core.SessionDeployStack(s)
|
||||
}
|
||||
|
||||
http.ServeFile(w, r, "./www/index.html")
|
||||
}
|
||||
|
||||
@@ -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/recaptcha"
|
||||
@@ -25,8 +27,10 @@ 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)
|
||||
if ok, err := stackExists(stack); err != nil {
|
||||
log.Printf("Error retrieving stack: %s", err)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
@@ -39,7 +43,7 @@ func NewSession(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
}
|
||||
duration := config.GetDuration(reqDur)
|
||||
s, err := core.SessionNew(duration, stack, "")
|
||||
s, err := core.SessionNew(duration, stack, stackName)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
//TODO: Return some error code
|
||||
@@ -61,13 +65,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
|
||||
|
||||
}
|
||||
|
||||
@@ -47,11 +47,14 @@ func (p *pwd) SessionNew(duration time.Duration, stack, stackName string) (*Sess
|
||||
s.ExpiresAt = s.CreatedAt.Add(duration)
|
||||
s.Ready = true
|
||||
s.Stack = stack
|
||||
s.StackName = stackName
|
||||
|
||||
if s.Stack != "" {
|
||||
s.Ready = false
|
||||
}
|
||||
if stackName == "" {
|
||||
stackName = "pwd"
|
||||
}
|
||||
s.StackName = stackName
|
||||
|
||||
log.Printf("NewSession id=[%s]\n", s.Id)
|
||||
|
||||
@@ -142,32 +145,32 @@ func (p *pwd) SessionDeployStack(s *Session) error {
|
||||
}
|
||||
|
||||
s.Ready = false
|
||||
p.broadcast.BroadcastTo(s.Id, "session ready", s.Ready)
|
||||
|
||||
p.broadcast.BroadcastTo(s.Id, "session ready", false)
|
||||
i, err := p.InstanceNew(s, InstanceConfig{})
|
||||
if err != nil {
|
||||
log.Printf("Error creating instance for stack [%s]: %s\n", s.Stack, err)
|
||||
return err
|
||||
}
|
||||
err = p.InstanceUploadFromUrl(i, "https://raw.githubusercontent.com/play-with-docker/stacks/master"+s.Stack)
|
||||
err = p.InstanceUploadFromUrl(i, s.Stack)
|
||||
if err != nil {
|
||||
log.Printf("Error uploading stack file [%s]: %s\n", s.Stack, err)
|
||||
return err
|
||||
}
|
||||
|
||||
w := sessionBuilderWriter{sessionId: s.Id, broadcast: p.broadcast}
|
||||
fileName := path.Base(s.Stack)
|
||||
code, err := p.docker.ExecAttach(i.Name, []string{"docker-compose", "-f", "/var/run/pwd/uploads/" + fileName, "up", "-d"}, &w)
|
||||
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 %s", file, file, s.StackName)
|
||||
|
||||
w := sessionBuilderWriter{sessionId: s.Id, broadcast: p.broadcast}
|
||||
code, err := p.docker.ExecAttach(i.Name, []string{"sh", "-c", cmd}, &w)
|
||||
if err != nil {
|
||||
log.Printf("Error executing stack [%s]: %s\n", s.Stack, err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Stack execution finished with code %d\n", code)
|
||||
|
||||
s.Ready = true
|
||||
p.broadcast.BroadcastTo(s.Id, "session ready", s.Ready)
|
||||
|
||||
p.broadcast.BroadcastTo(s.Id, "session ready", true)
|
||||
if err := p.storage.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -176,16 +179,6 @@ func (p *pwd) SessionDeployStack(s *Session) error {
|
||||
|
||||
func (p *pwd) SessionGet(sessionId string) *Session {
|
||||
s := sessions[sessionId]
|
||||
/*
|
||||
if s != nil {
|
||||
for _, instance := range s.Instances {
|
||||
if !instance.IsConnected() {
|
||||
instance.SetSession(s)
|
||||
go instance.Attach()
|
||||
}
|
||||
}
|
||||
|
||||
}*/
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ func TestSessionNew(t *testing.T) {
|
||||
assert.Nil(t, e)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
assert.Equal(t, "pwd", s.StackName)
|
||||
|
||||
assert.NotEmpty(t, s.Id)
|
||||
assert.WithinDuration(t, s.CreatedAt, before, time.Since(before))
|
||||
assert.WithinDuration(t, s.ExpiresAt, before.Add(time.Hour), time.Second)
|
||||
|
||||
@@ -63,6 +63,8 @@
|
||||
KeyboardShortcutService.setResizeFunc($scope.resize);
|
||||
|
||||
$scope.closeSession = function() {
|
||||
// Remove alert before closing browser tab
|
||||
window.onbeforeunload = null;
|
||||
$scope.socket.emit('session close');
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<form id="welcomeFormBypass" method="POST" action="/">
|
||||
<button id="start" type="submit">Start Session</button>
|
||||
<input id="stack" type="hidden" name="stack" value=""/>
|
||||
<input id="stack_name" type="hidden" name="stack_name" value=""/>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
@@ -37,5 +38,9 @@
|
||||
if (stack) {
|
||||
document.getElementById('stack').value = stack;
|
||||
}
|
||||
var stackName = getParameterByName('stack_name');
|
||||
if (stack) {
|
||||
document.getElementById('stack_name').value = stackName;
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<div id="recaptcha" class="g-recaptcha" data-callback="iAmHuman" data-sitekey="{{.}}"></div>
|
||||
<input type="hidden" name="session-duration" value="4h"/>
|
||||
<input id="stack" type="hidden" name="stack" value=""/>
|
||||
<input id="stack_name" type="hidden" name="stack_name" value=""/>
|
||||
<button id="create" style="display:none;">Create session</button>
|
||||
</form>
|
||||
<img src="/assets/large_h.png" />
|
||||
@@ -38,6 +39,10 @@
|
||||
if (stack) {
|
||||
document.getElementById('stack').value = stack;
|
||||
}
|
||||
var stackName = getParameterByName('stack_name');
|
||||
if (stackName) {
|
||||
document.getElementById('stack_name').value = stackName;
|
||||
}
|
||||
if (document.cookie.indexOf('session_id') > -1) {
|
||||
document.getElementById('create').style = "";
|
||||
document.getElementById('recaptcha').style = "display:none;";
|
||||
|
||||
Reference in New Issue
Block a user