Skip to content

Commit

Permalink
Add session templates
Browse files Browse the repository at this point in the history
  • Loading branch information
xetorthio committed Jun 6, 2017
1 parent fe8ea11 commit 6d992b5
Show file tree
Hide file tree
Showing 11 changed files with 535 additions and 3 deletions.
1 change: 1 addition & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func main() {
r.HandleFunc("/ping", handlers.Ping).Methods("GET")
corsRouter.HandleFunc("/instances/images", handlers.GetInstanceImages).Methods("GET")
corsRouter.HandleFunc("/sessions/{sessionId}", handlers.GetSession).Methods("GET")
corsRouter.HandleFunc("/sessions/{sessionId}/setup", handlers.SessionSetup).Methods("POST")
corsRouter.HandleFunc("/sessions/{sessionId}/instances", handlers.NewInstance).Methods("POST")
corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/uploads", handlers.FileUpload).Methods("POST")
corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", handlers.DeleteInstance).Methods("DELETE")
Expand Down
80 changes: 80 additions & 0 deletions docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,27 @@ import (
"archive/tar"
"bytes"
"context"
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"strconv"
"strings"
"time"

"github.com/docker/distribution/reference"
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/go-connections/tlsconfig"
)

const (
Expand All @@ -44,6 +49,14 @@ type DockerApi interface {
DisconnectNetwork(containerId, networkId string) error
DeleteNetwork(id string) error
Exec(instanceName string, command []string) (int, error)
New(ip string, cert, key []byte) (DockerApi, error)
SwarmInit() (*SwarmTokens, error)
SwarmJoin(addr, token string) error
}

type SwarmTokens struct {
Manager string
Worker string
}

type docker struct {
Expand Down Expand Up @@ -396,6 +409,73 @@ func (d *docker) DeleteNetwork(id string) error {
return nil
}

func (d *docker) New(ip string, cert, key []byte) (DockerApi, error) {
// We check if the client needs to use TLS
var tlsConfig *tls.Config
if len(cert) > 0 && len(key) > 0 {
tlsConfig = tlsconfig.ClientDefault()
tlsConfig.InsecureSkipVerify = true
tlsCert, err := tls.X509KeyPair(cert, key)
if err != nil {
return nil, err
}
tlsConfig.Certificates = []tls.Certificate{tlsCert}
}

transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 1 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext}
if tlsConfig != nil {
transport.TLSClientConfig = tlsConfig
}
cli := &http.Client{
Transport: transport,
}
c, err := client.NewClient(fmt.Sprintf("http://%s:2375", ip), api.DefaultVersion, cli, nil)
if err != nil {
return nil, fmt.Errorf("Could not connect to DinD docker daemon. %s", err)
}
// try to connect up to 5 times and then give up
for i := 0; i < 5; i++ {
_, err := c.Ping(context.Background())
if err != nil {
if client.IsErrConnectionFailed(err) {
// connection has failed, maybe instance is not ready yet, sleep and retry
log.Printf("Connection to [%s] has failed, maybe instance is not ready yet, sleeping and retrying in 1 second. Try #%d\n", fmt.Sprintf("http://%s:2375", ip), i+1)
time.Sleep(time.Second)
continue
}
return nil, err
}
}
return NewDocker(c), nil
}

func (d *docker) SwarmInit() (*SwarmTokens, error) {
req := swarm.InitRequest{AdvertiseAddr: "eth0", ListenAddr: "0.0.0.0:2377"}
_, err := d.c.SwarmInit(context.Background(), req)

if err != nil {
return nil, err
}

swarmInfo, err := d.c.SwarmInspect(context.Background())
if err != nil {
return nil, err
}

return &SwarmTokens{
Worker: swarmInfo.JoinTokens.Worker,
Manager: swarmInfo.JoinTokens.Manager,
}, nil
}
func (d *docker) SwarmJoin(addr, token string) error {
req := swarm.JoinRequest{RemoteAddrs: []string{addr}, JoinToken: token, ListenAddr: "0.0.0.0:2377", AdvertiseAddr: "eth0"}
return d.c.SwarmJoin(context.Background(), req)
}

func NewDocker(c *client.Client) *docker {
return &docker{c: c}
}
35 changes: 35 additions & 0 deletions handlers/session_setup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package handlers

import (
"encoding/json"
"log"
"net/http"

"github.com/gorilla/mux"
"github.com/play-with-docker/play-with-docker/pwd"
)

func SessionSetup(rw http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
sessionId := vars["sessionId"]

body := pwd.SessionSetupConf{}

json.NewDecoder(req.Body).Decode(&body)

s := core.SessionGet(sessionId)

if len(s.Instances) > 0 {
log.Println("Cannot setup a session that contains instances")
rw.WriteHeader(http.StatusConflict)
rw.Write([]byte("Cannot setup a session that contains instances"))
return
}

err := core.SessionSetup(s, body)
if err != nil {
log.Println(err)
rw.WriteHeader(http.StatusInternalServerError)
return
}
}
25 changes: 25 additions & 0 deletions pwd/docker_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ type mockDocker struct {
connectNetwork func(container, network, ip string) (string, error)
containerResize func(string, uint, uint) error
createContainer func(opts docker.CreateContainerOpts) (string, error)
execAttach func(instanceName string, command []string, out io.Writer) (int, error)
new func(ip string, cert, key []byte) (docker.DockerApi, error)
swarmInit func() (*docker.SwarmTokens, error)
swarmJoin func(addr, token string) error
}

func (m *mockDocker) CreateNetwork(id string) error {
Expand Down Expand Up @@ -64,6 +68,9 @@ func (m *mockDocker) CreateContainer(opts docker.CreateContainerOpts) (string, e
return "10.0.0.1", nil
}
func (m *mockDocker) ExecAttach(instanceName string, command []string, out io.Writer) (int, error) {
if m.execAttach != nil {
return m.execAttach(instanceName, command, out)
}
return 0, nil
}
func (m *mockDocker) DisconnectNetwork(containerId, networkId string) error {
Expand All @@ -75,6 +82,24 @@ func (m *mockDocker) DeleteNetwork(id string) error {
func (m *mockDocker) Exec(instanceName string, command []string) (int, error) {
return 0, nil
}
func (m *mockDocker) New(ip string, cert, key []byte) (docker.DockerApi, error) {
if m.new != nil {
return m.new(ip, cert, key)
}
return nil, nil
}
func (m *mockDocker) SwarmInit() (*docker.SwarmTokens, error) {
if m.swarmInit != nil {
return m.swarmInit()
}
return nil, nil
}
func (m *mockDocker) SwarmJoin(addr, token string) error {
if m.swarmJoin != nil {
return m.swarmJoin(addr, token)
}
return nil
}

type mockConn struct {
}
Expand Down
1 change: 1 addition & 0 deletions pwd/pwd.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type PWDApi interface {
SessionDeployStack(session *Session) error
SessionGet(id string) *Session
SessionLoadAndPrepare() error
SessionSetup(session *Session, conf SessionSetupConf) error

InstanceNew(session *Session, conf InstanceConfig) (*Instance, error)
InstanceResizeTerminal(instance *Instance, cols, rows uint) error
Expand Down
66 changes: 66 additions & 0 deletions pwd/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/play-with-docker/play-with-docker/config"
"github.com/play-with-docker/play-with-docker/docker"
"github.com/twinj/uuid"
)

Expand All @@ -23,6 +24,17 @@ func (s *sessionBuilderWriter) Write(p []byte) (n int, err error) {
return len(p), nil
}

type SessionSetupConf struct {
Instances []SessionSetupInstanceConf `json:"instances"`
}

type SessionSetupInstanceConf struct {
Image string `json:"image"`
Hostname string `json:"hostname"`
IsSwarmManager bool `json:"is_swarm_manager"`
IsSwarmWorker bool `json:"is_swarm_worker"`
}

type Session struct {
rw sync.Mutex
Id string `json:"id"`
Expand Down Expand Up @@ -209,6 +221,60 @@ func (p *pwd) SessionLoadAndPrepare() error {
return nil
}

func (p *pwd) SessionSetup(session *Session, conf SessionSetupConf) error {
var tokens *docker.SwarmTokens = nil
var firstSwarmManager *Instance = nil

// First create all instances and record who is a swarm manager and who is a swarm worker
for _, conf := range conf.Instances {
instanceConf := InstanceConfig{
ImageName: conf.Image,
Hostname: conf.Hostname,
}
i, err := p.InstanceNew(session, instanceConf)
if err != nil {
return err
}
if conf.IsSwarmManager || conf.IsSwarmWorker {
// check if we have connection to the daemon, if not, create it
if i.docker == nil {
dock, err := p.docker.New(i.IP, i.Cert, i.Key)
if err != nil {
return err
}
i.docker = dock
}
}
if conf.IsSwarmManager {
// this is a swarm manager
// if no swarm cluster has been initiated, then initiate it!
if firstSwarmManager == nil {
tkns, err := i.docker.SwarmInit()
if err != nil {
return err
}
tokens = tkns
firstSwarmManager = i
} else {
// cluster has already been initiated, join as manager
err := i.docker.SwarmJoin(fmt.Sprintf("%s:2377", firstSwarmManager.IP), tokens.Manager)
if err != nil {
return err
}
}
}
if conf.IsSwarmWorker {
// this is a swarm worker
err := i.docker.SwarmJoin(fmt.Sprintf("%s:2377", firstSwarmManager.IP), tokens.Worker)
if err != nil {
return err
}
}
}

return nil
}

// This function should be called any time a session needs to be prepared:
// 1. Like when it is created
// 2. When it was loaded from storage
Expand Down
Loading

0 comments on commit 6d992b5

Please sign in to comment.