diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e253afb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +play-with-docker + diff --git a/README.md b/README.md index 8124ae6..59ab094 100644 --- a/README.md +++ b/README.md @@ -1 +1,29 @@ -# play-with-docker \ No newline at end of file +# play-with-docker + +Play With Docker gives you the experience of having a free Alpine Linux Virtual Machine in the cloud +where you can build and run Docker containers and even create clusters with Docker features like Swarm Mode. + +Under the hood DIND or Docker-in-Docker is used to give the effect of multiple VMs/PCs. + +A live version is available at: http://play-with-docker.com/ + +## Installation + +Start the Docker daemon on your machine and run `docker pull docker:1.12.2-rc2-dind`. + +1) Install go 1.7.1 with `brew` on Mac or through a package manager. + +2) `go get` + +3) `go build` + +4) Run the binary produced as `play-with-docker` + +5) Point to http://localhost:3000/ and click "New Instance" + +Notes: + +* There is a hard-coded limit to 5 Docker playgrounds per session. After 1 hour sessions are deleted. +* If you want to override the DIND version or image then set the environmental variable i.e. + `DIND_IMAGE=docker:dind` + diff --git a/services/docker.go b/services/docker.go index c88130d..861d0c0 100644 --- a/services/docker.go +++ b/services/docker.go @@ -29,10 +29,14 @@ func GetContainerInfo(id string) (types.ContainerJSON, error) { } func CreateNetwork(name string) error { + // TODO: This line appears to give an error when running on localhost:3000 + // when driver is specified a name must be given. opts := types.NetworkCreate{Attachable: true, Driver: "overlay"} _, err := c.NetworkCreate(context.Background(), name, opts) if err != nil { + log.Printf("Starting session err [%s]\n", err) + return err } @@ -70,10 +74,14 @@ func AttachExecConnection(execId string, ctx context.Context) (*types.HijackedRe return &conn, nil } -func CreateInstance(net string) (*ptypes.Instance, error) { +func CreateInstance(net string, dindImage string) (*ptypes.Instance, error) { + var maximumPidLimit int64 + maximumPidLimit = 150 // Set a ulimit value to prevent misuse h := &container.HostConfig{NetworkMode: container.NetworkMode(net), Privileged: true} - conf := &container.Config{Image: "docker:dind"} + h.Resources.PidsLimit = maximumPidLimit + + conf := &container.Config{Image: dindImage} container, err := c.ContainerCreate(context.Background(), conf, h, nil, "") if err != nil { diff --git a/services/instance.go b/services/instance.go index 949f27d..e95b8e2 100644 --- a/services/instance.go +++ b/services/instance.go @@ -1,19 +1,37 @@ package services -import "github.com/franela/play-with-docker/types" +import ( + "log" + "os" + + "github.com/franela/play-with-docker/types" +) var instances map[string]map[string]*types.Instance +var dindImage string +var defaultDindImageName string + func init() { instances = make(map[string]map[string]*types.Instance) + dindImage = getDindImageName() +} + +func getDindImageName() string { + dindImage := os.Getenv("DIND_IMAGE") + defaultDindImageName = "docker:1.12.2-rc2-dind" + if len(dindImage) == 0 { + dindImage = defaultDindImageName + } + return dindImage } func NewInstance(session *types.Session) (*types.Instance, error) { - //TODO: Validate that a session can only have 10 instances + //TODO: Validate that a session can only have 5 instances //TODO: Create in redis - - instance, err := CreateInstance(session.Id) + log.Printf("NewInstance - using image: [%s]\n", dindImage) + instance, err := CreateInstance(session.Id, dindImage) if err != nil { return nil, err diff --git a/services/session.go b/services/session.go index 4c8ced6..6f82613 100644 --- a/services/session.go +++ b/services/session.go @@ -18,6 +18,7 @@ func NewSession() (*types.Session, error) { s := &types.Session{} s.Id = uuid.NewV4().String() s.Instances = map[string]*types.Instance{} + log.Printf("NewSession id=[%s]\n", s.Id) //TODO: Store in something like redis sessions[s.Id] = s