Add provisioners
This commit is contained in:
@@ -6,14 +6,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/play-with-docker/play-with-docker/pwd"
|
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewInstance(rw http.ResponseWriter, req *http.Request) {
|
func NewInstance(rw http.ResponseWriter, req *http.Request) {
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
sessionId := vars["sessionId"]
|
sessionId := vars["sessionId"]
|
||||||
|
|
||||||
body := pwd.InstanceConfig{Host: req.Host}
|
body := types.InstanceConfig{Host: req.Host}
|
||||||
|
|
||||||
json.NewDecoder(req.Body).Decode(&body)
|
json.NewDecoder(req.Body).Decode(&body)
|
||||||
|
|
||||||
|
|||||||
206
provisioner/dind.go
Normal file
206
provisioner/dind.go
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
package provisioner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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/pwd/types"
|
||||||
|
"github.com/play-with-docker/play-with-docker/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DinD struct {
|
||||||
|
factory docker.FactoryApi
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDinD(f docker.FactoryApi) *DinD {
|
||||||
|
return &DinD{factory: f}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHostnameExists(session *types.Session, hostname string) bool {
|
||||||
|
containerName := fmt.Sprintf("%s_%s", session.Id[:8], hostname)
|
||||||
|
exists := false
|
||||||
|
for _, instance := range session.Instances {
|
||||||
|
if instance.Name == containerName {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DinD) InstanceAllowedImages() []string {
|
||||||
|
return []string{
|
||||||
|
config.GetDindImageName(),
|
||||||
|
"franela/dind:overlay2-dev",
|
||||||
|
"franela/ucp:2.4.1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DinD) InstanceNew(session *types.Session, conf types.InstanceConfig) (*types.Instance, error) {
|
||||||
|
if conf.ImageName == "" {
|
||||||
|
conf.ImageName = config.GetDindImageName()
|
||||||
|
}
|
||||||
|
log.Printf("NewInstance - using image: [%s]\n", conf.ImageName)
|
||||||
|
if conf.Hostname == "" {
|
||||||
|
var nodeName string
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
nodeName = fmt.Sprintf("node%d", i)
|
||||||
|
exists := checkHostnameExists(session, nodeName)
|
||||||
|
if !exists {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conf.Hostname = nodeName
|
||||||
|
}
|
||||||
|
containerName := fmt.Sprintf("%s_%s", session.Id[:8], conf.Hostname)
|
||||||
|
opts := docker.CreateContainerOpts{
|
||||||
|
Image: conf.ImageName,
|
||||||
|
SessionId: session.Id,
|
||||||
|
PwdIpAddress: session.PwdIpAddress,
|
||||||
|
ContainerName: containerName,
|
||||||
|
Hostname: conf.Hostname,
|
||||||
|
ServerCert: conf.ServerCert,
|
||||||
|
ServerKey: conf.ServerKey,
|
||||||
|
CACert: conf.CACert,
|
||||||
|
Privileged: false,
|
||||||
|
HostFQDN: conf.Host,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, imageName := range d.InstanceAllowedImages() {
|
||||||
|
if conf.ImageName == imageName {
|
||||||
|
opts.Privileged = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dockerClient, err := d.factory.GetForSession(session.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ip, err := dockerClient.CreateContainer(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := &types.Instance{}
|
||||||
|
instance.Image = opts.Image
|
||||||
|
instance.IP = ip
|
||||||
|
instance.SessionId = session.Id
|
||||||
|
instance.Name = containerName
|
||||||
|
instance.Hostname = conf.Hostname
|
||||||
|
instance.Cert = conf.Cert
|
||||||
|
instance.Key = conf.Key
|
||||||
|
instance.ServerCert = conf.ServerCert
|
||||||
|
instance.ServerKey = conf.ServerKey
|
||||||
|
instance.CACert = conf.CACert
|
||||||
|
instance.Session = session
|
||||||
|
instance.ProxyHost = router.EncodeHost(session.Id, ip, router.HostOpts{})
|
||||||
|
instance.SessionHost = session.Host
|
||||||
|
// For now this condition holds through. In the future we might need a more complex logic.
|
||||||
|
instance.IsDockerHost = opts.Privileged
|
||||||
|
|
||||||
|
return instance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DinD) InstanceDelete(session *types.Session, instance *types.Instance) error {
|
||||||
|
dockerClient, err := d.factory.GetForSession(session.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = dockerClient.DeleteContainer(instance.Name)
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "No such container") {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DinD) InstanceResizeTerminal(instance *types.Instance, rows, cols uint) error {
|
||||||
|
dockerClient, err := d.factory.GetForSession(instance.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return dockerClient.ContainerResize(instance.Name, rows, cols)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DinD) InstanceGetTerminal(instance *types.Instance) (net.Conn, error) {
|
||||||
|
dockerClient, err := d.factory.GetForSession(instance.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dockerClient.CreateAttachConnection(instance.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DinD) InstanceUploadFromUrl(instance *types.Instance, fileName, dest, url string) error {
|
||||||
|
log.Printf("Downloading file [%s]\n", url)
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not download file [%s]. Error: %s\n", url, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("Could not download file [%s]. Status code: %d\n", url, resp.StatusCode)
|
||||||
|
}
|
||||||
|
dockerClient, err := d.factory.GetForSession(instance.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
copyErr := dockerClient.CopyToContainer(instance.Name, dest, fileName, resp.Body)
|
||||||
|
|
||||||
|
if copyErr != nil {
|
||||||
|
return fmt.Errorf("Error while downloading file [%s]. Error: %s\n", url, copyErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DinD) getInstanceCWD(instance *types.Instance) (string, error) {
|
||||||
|
dockerClient, err := d.factory.GetForSession(instance.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b := bytes.NewBufferString("")
|
||||||
|
|
||||||
|
if c, err := dockerClient.ExecAttach(instance.Name, []string{"bash", "-c", `pwdx $(</var/run/cwd)`}, b); c > 0 {
|
||||||
|
return "", fmt.Errorf("Error %d trying to get CWD", c)
|
||||||
|
} else if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
cwd := strings.TrimSpace(strings.Split(b.String(), ":")[1])
|
||||||
|
|
||||||
|
return cwd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DinD) InstanceUploadFromReader(instance *types.Instance, fileName, dest string, reader io.Reader) error {
|
||||||
|
dockerClient, err := d.factory.GetForSession(instance.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var finalDest string
|
||||||
|
if filepath.IsAbs(dest) {
|
||||||
|
finalDest = dest
|
||||||
|
} else {
|
||||||
|
if cwd, err := d.getInstanceCWD(instance); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
finalDest = fmt.Sprintf("%s/%s", cwd, dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copyErr := dockerClient.CopyToContainer(instance.Name, finalDest, fileName, reader)
|
||||||
|
|
||||||
|
if copyErr != nil {
|
||||||
|
return fmt.Errorf("Error while uploading file [%s]. Error: %s\n", fileName, copyErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
19
provisioner/provisioner.go
Normal file
19
provisioner/provisioner.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package provisioner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProvisionerApi interface {
|
||||||
|
InstanceNew(session *types.Session, conf types.InstanceConfig) (*types.Instance, error)
|
||||||
|
InstanceDelete(session *types.Session, instance *types.Instance) error
|
||||||
|
|
||||||
|
InstanceResizeTerminal(instance *types.Instance, cols, rows uint) error
|
||||||
|
InstanceGetTerminal(instance *types.Instance) (net.Conn, error)
|
||||||
|
|
||||||
|
InstanceUploadFromUrl(instance *types.Instance, fileName, dest, url string) error
|
||||||
|
InstanceUploadFromReader(instance *types.Instance, fileName, dest string, reader io.Reader) error
|
||||||
|
}
|
||||||
42
provisioner/windows.go
Normal file
42
provisioner/windows.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package provisioner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/play-with-docker/play-with-docker/docker"
|
||||||
|
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type windows struct {
|
||||||
|
factory docker.FactoryApi
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWindows(f docker.FactoryApi) *windows {
|
||||||
|
return &windows{factory: f}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *windows) InstanceNew(session *types.Session, conf types.InstanceConfig) (*types.Instance, error) {
|
||||||
|
return nil, fmt.Errorf("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *windows) InstanceDelete(session *types.Session, instance *types.Instance) error {
|
||||||
|
return fmt.Errorf("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *windows) InstanceResizeTerminal(instance *types.Instance, cols, rows uint) error {
|
||||||
|
return fmt.Errorf("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *windows) InstanceGetTerminal(instance *types.Instance) (net.Conn, error) {
|
||||||
|
return nil, fmt.Errorf("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *windows) InstanceUploadFromUrl(instance *types.Instance, fileName, dest, url string) error {
|
||||||
|
return fmt.Errorf("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *windows) InstanceUploadFromReader(instance *types.Instance, fileName, dest string, reader io.Reader) error {
|
||||||
|
return fmt.Errorf("Not implemented")
|
||||||
|
}
|
||||||
192
pwd/instance.go
192
pwd/instance.go
@@ -1,107 +1,53 @@
|
|||||||
package pwd
|
package pwd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"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/event"
|
"github.com/play-with-docker/play-with-docker/event"
|
||||||
|
"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/pwd/types"
|
||||||
"github.com/play-with-docker/play-with-docker/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type InstanceConfig struct {
|
|
||||||
ImageName string
|
|
||||||
Hostname string
|
|
||||||
ServerCert []byte
|
|
||||||
ServerKey []byte
|
|
||||||
CACert []byte
|
|
||||||
Cert []byte
|
|
||||||
Key []byte
|
|
||||||
Host string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pwd) InstanceResizeTerminal(instance *types.Instance, rows, cols uint) error {
|
func (p *pwd) InstanceResizeTerminal(instance *types.Instance, rows, cols uint) error {
|
||||||
defer observeAction("InstanceResizeTerminal", time.Now())
|
defer observeAction("InstanceResizeTerminal", time.Now())
|
||||||
return p.docker(instance.SessionId).ContainerResize(instance.Name, rows, cols)
|
prov, err := p.getProvisioner(instance.Type)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return prov.InstanceResizeTerminal(instance, rows, cols)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pwd) InstanceGetTerminal(instance *types.Instance) (net.Conn, error) {
|
func (p *pwd) InstanceGetTerminal(instance *types.Instance) (net.Conn, error) {
|
||||||
defer observeAction("InstanceGetTerminal", time.Now())
|
defer observeAction("InstanceGetTerminal", time.Now())
|
||||||
conn, err := p.docker(instance.SessionId).CreateAttachConnection(instance.Name)
|
prov, err := p.getProvisioner(instance.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return prov.InstanceGetTerminal(instance)
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pwd) InstanceUploadFromUrl(instance *types.Instance, fileName, dest string, url string) error {
|
func (p *pwd) InstanceUploadFromUrl(instance *types.Instance, fileName, dest string, url string) error {
|
||||||
defer observeAction("InstanceUploadFromUrl", time.Now())
|
defer observeAction("InstanceUploadFromUrl", time.Now())
|
||||||
log.Printf("Downloading file [%s]\n", url)
|
prov, err := p.getProvisioner(instance.Type)
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Could not download file [%s]. Error: %s\n", url, err)
|
return err
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return fmt.Errorf("Could not download file [%s]. Status code: %d\n", url, resp.StatusCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
copyErr := p.docker(instance.SessionId).CopyToContainer(instance.Name, dest, fileName, resp.Body)
|
return prov.InstanceUploadFromUrl(instance, fileName, dest, url)
|
||||||
|
|
||||||
if copyErr != nil {
|
|
||||||
return fmt.Errorf("Error while downloading file [%s]. Error: %s\n", url, copyErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pwd) getInstanceCWD(instance *types.Instance) (string, error) {
|
|
||||||
b := bytes.NewBufferString("")
|
|
||||||
|
|
||||||
if c, err := p.docker(instance.SessionId).ExecAttach(instance.Name, []string{"bash", "-c", `pwdx $(</var/run/cwd)`}, b); c > 0 {
|
|
||||||
log.Println(b.String())
|
|
||||||
return "", fmt.Errorf("Error %d trying to get CWD", c)
|
|
||||||
} else if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
cwd := strings.TrimSpace(strings.Split(b.String(), ":")[1])
|
|
||||||
|
|
||||||
return cwd, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pwd) InstanceUploadFromReader(instance *types.Instance, fileName, dest string, reader io.Reader) error {
|
func (p *pwd) InstanceUploadFromReader(instance *types.Instance, fileName, dest string, reader io.Reader) error {
|
||||||
defer observeAction("InstanceUploadFromReader", time.Now())
|
defer observeAction("InstanceUploadFromReader", time.Now())
|
||||||
|
|
||||||
var finalDest string
|
prov, err := p.getProvisioner(instance.Type)
|
||||||
if filepath.IsAbs(dest) {
|
if err != nil {
|
||||||
finalDest = dest
|
return err
|
||||||
} else {
|
|
||||||
if cwd, err := p.getInstanceCWD(instance); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
finalDest = fmt.Sprintf("%s/%s", cwd, dest)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
copyErr := p.docker(instance.SessionId).CopyToContainer(instance.Name, finalDest, fileName, reader)
|
return prov.InstanceUploadFromReader(instance, fileName, dest, reader)
|
||||||
|
|
||||||
if copyErr != nil {
|
|
||||||
return fmt.Errorf("Error while uploading file [%s]. Error: %s\n", fileName, copyErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pwd) InstanceGet(session *types.Session, name string) *types.Instance {
|
func (p *pwd) InstanceGet(session *types.Session, name string) *types.Instance {
|
||||||
@@ -109,8 +55,8 @@ func (p *pwd) InstanceGet(session *types.Session, name string) *types.Instance {
|
|||||||
return session.Instances[name]
|
return session.Instances[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pwd) InstanceFind(sessionId, ip string) *types.Instance {
|
func (p *pwd) InstanceFindByIP(sessionId, ip string) *types.Instance {
|
||||||
defer observeAction("InstanceFind", time.Now())
|
defer observeAction("InstanceFindByIP", time.Now())
|
||||||
i, err := p.storage.InstanceFindByIP(sessionId, ip)
|
i, err := p.storage.InstanceFindByIP(sessionId, ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -122,8 +68,13 @@ func (p *pwd) InstanceFind(sessionId, ip string) *types.Instance {
|
|||||||
func (p *pwd) InstanceDelete(session *types.Session, instance *types.Instance) error {
|
func (p *pwd) InstanceDelete(session *types.Session, instance *types.Instance) error {
|
||||||
defer observeAction("InstanceDelete", time.Now())
|
defer observeAction("InstanceDelete", time.Now())
|
||||||
|
|
||||||
err := p.docker(session.Id).DeleteContainer(instance.Name)
|
prov, err := p.getProvisioner(instance.Type)
|
||||||
if err != nil && !strings.Contains(err.Error(), "No such container") {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = prov.InstanceDelete(session, instance)
|
||||||
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -139,90 +90,26 @@ func (p *pwd) InstanceDelete(session *types.Session, instance *types.Instance) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pwd) checkHostnameExists(session *types.Session, hostname string) bool {
|
func (p *pwd) InstanceNew(session *types.Session, conf types.InstanceConfig) (*types.Instance, error) {
|
||||||
containerName := fmt.Sprintf("%s_%s", session.Id[:8], hostname)
|
|
||||||
exists := false
|
|
||||||
for _, instance := range session.Instances {
|
|
||||||
if instance.Name == containerName {
|
|
||||||
exists = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exists
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pwd) InstanceNew(session *types.Session, conf InstanceConfig) (*types.Instance, error) {
|
|
||||||
defer observeAction("InstanceNew", time.Now())
|
defer observeAction("InstanceNew", time.Now())
|
||||||
session.Lock()
|
session.Lock()
|
||||||
defer session.Unlock()
|
defer session.Unlock()
|
||||||
|
|
||||||
if conf.ImageName == "" {
|
prov, err := p.getProvisioner(conf.Type)
|
||||||
conf.ImageName = config.GetDindImageName()
|
|
||||||
}
|
|
||||||
log.Printf("NewInstance - using image: [%s]\n", conf.ImageName)
|
|
||||||
|
|
||||||
if conf.Hostname == "" {
|
|
||||||
var nodeName string
|
|
||||||
for i := 1; ; i++ {
|
|
||||||
nodeName = fmt.Sprintf("node%d", i)
|
|
||||||
exists := p.checkHostnameExists(session, nodeName)
|
|
||||||
if !exists {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conf.Hostname = nodeName
|
|
||||||
}
|
|
||||||
containerName := fmt.Sprintf("%s_%s", session.Id[:8], conf.Hostname)
|
|
||||||
|
|
||||||
opts := docker.CreateContainerOpts{
|
|
||||||
Image: conf.ImageName,
|
|
||||||
SessionId: session.Id,
|
|
||||||
PwdIpAddress: session.PwdIpAddress,
|
|
||||||
ContainerName: containerName,
|
|
||||||
Hostname: conf.Hostname,
|
|
||||||
ServerCert: conf.ServerCert,
|
|
||||||
ServerKey: conf.ServerKey,
|
|
||||||
CACert: conf.CACert,
|
|
||||||
Privileged: false,
|
|
||||||
HostFQDN: conf.Host,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, imageName := range p.InstanceAllowedImages() {
|
|
||||||
if conf.ImageName == imageName {
|
|
||||||
opts.Privileged = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ip, err := p.docker(session.Id).CreateContainer(opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
instance, err := prov.InstanceNew(session, conf)
|
||||||
instance := &types.Instance{}
|
if err != nil {
|
||||||
instance.Image = opts.Image
|
log.Println(err)
|
||||||
instance.IP = ip
|
return nil, err
|
||||||
instance.SessionId = session.Id
|
}
|
||||||
instance.Name = containerName
|
|
||||||
instance.Hostname = conf.Hostname
|
|
||||||
instance.Cert = conf.Cert
|
|
||||||
instance.Key = conf.Key
|
|
||||||
instance.ServerCert = conf.ServerCert
|
|
||||||
instance.ServerKey = conf.ServerKey
|
|
||||||
instance.CACert = conf.CACert
|
|
||||||
instance.Session = session
|
|
||||||
instance.ProxyHost = router.EncodeHost(session.Id, ip, router.HostOpts{})
|
|
||||||
instance.SessionHost = session.Host
|
|
||||||
// For now this condition holds through. In the future we might need a more complex logic.
|
|
||||||
instance.IsDockerHost = opts.Privileged
|
|
||||||
|
|
||||||
if session.Instances == nil {
|
if session.Instances == nil {
|
||||||
session.Instances = make(map[string]*types.Instance)
|
session.Instances = make(map[string]*types.Instance)
|
||||||
}
|
}
|
||||||
session.Instances[instance.Name] = instance
|
session.Instances[instance.Name] = instance
|
||||||
|
|
||||||
// go p.InstanceAttachTerminal(instance)
|
|
||||||
|
|
||||||
err = p.storage.InstanceCreate(session.Id, instance)
|
err = p.storage.InstanceCreate(session.Id, instance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -235,18 +122,13 @@ func (p *pwd) InstanceNew(session *types.Session, conf InstanceConfig) (*types.I
|
|||||||
return instance, nil
|
return instance, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pwd) InstanceAllowedImages() []string {
|
|
||||||
defer observeAction("InstanceAllowedImages", time.Now())
|
|
||||||
|
|
||||||
return []string{
|
|
||||||
config.GetDindImageName(),
|
|
||||||
"franela/dind:overlay2-dev",
|
|
||||||
"franela/ucp:2.4.1",
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pwd) InstanceExec(instance *types.Instance, cmd []string) (int, error) {
|
func (p *pwd) InstanceExec(instance *types.Instance, cmd []string) (int, error) {
|
||||||
defer observeAction("InstanceExec", time.Now())
|
defer observeAction("InstanceExec", time.Now())
|
||||||
return p.docker(instance.SessionId).Exec(instance.Name, cmd)
|
return p.docker(instance.SessionId).Exec(instance.Name, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *pwd) InstanceAllowedImages() []string {
|
||||||
|
defer observeAction("InstanceAllowedImages", time.Now())
|
||||||
|
|
||||||
|
return p.dindProvisioner.(*provisioner.DinD).InstanceAllowedImages()
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func TestInstanceNew(t *testing.T) {
|
|||||||
_s.On("InstanceCreate", "aaaabbbbcccc", mock.AnythingOfType("*types.Instance")).Return(nil)
|
_s.On("InstanceCreate", "aaaabbbbcccc", mock.AnythingOfType("*types.Instance")).Return(nil)
|
||||||
_e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_node1", "10.0.0.1", "node1"}).Return()
|
_e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_node1", "10.0.0.1", "node1"}).Return()
|
||||||
|
|
||||||
instance, err := p.InstanceNew(session, InstanceConfig{Host: "something.play-with-docker.com"})
|
instance, err := p.InstanceNew(session, types.InstanceConfig{Host: "something.play-with-docker.com"})
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, expectedInstance, *instance)
|
assert.Equal(t, expectedInstance, *instance)
|
||||||
@@ -149,7 +149,7 @@ func TestInstanceNew_WithNotAllowedImage(t *testing.T) {
|
|||||||
_s.On("InstanceCreate", "aaaabbbbcccc", mock.AnythingOfType("*types.Instance")).Return(nil)
|
_s.On("InstanceCreate", "aaaabbbbcccc", mock.AnythingOfType("*types.Instance")).Return(nil)
|
||||||
_e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_node1", "10.0.0.1", "node1"}).Return()
|
_e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_node1", "10.0.0.1", "node1"}).Return()
|
||||||
|
|
||||||
instance, err := p.InstanceNew(session, InstanceConfig{ImageName: "redis"})
|
instance, err := p.InstanceNew(session, types.InstanceConfig{ImageName: "redis"})
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, expectedInstance, *instance)
|
assert.Equal(t, expectedInstance, *instance)
|
||||||
@@ -211,7 +211,7 @@ func TestInstanceNew_WithCustomHostname(t *testing.T) {
|
|||||||
_s.On("InstanceCreate", "aaaabbbbcccc", mock.AnythingOfType("*types.Instance")).Return(nil)
|
_s.On("InstanceCreate", "aaaabbbbcccc", mock.AnythingOfType("*types.Instance")).Return(nil)
|
||||||
_e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_redis-master", "10.0.0.1", "redis-master"}).Return()
|
_e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_redis-master", "10.0.0.1", "redis-master"}).Return()
|
||||||
|
|
||||||
instance, err := p.InstanceNew(session, InstanceConfig{ImageName: "redis", Hostname: "redis-master"})
|
instance, err := p.InstanceNew(session, types.InstanceConfig{ImageName: "redis", Hostname: "redis-master"})
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -223,15 +223,3 @@ func TestInstanceNew_WithCustomHostname(t *testing.T) {
|
|||||||
_g.AssertExpectations(t)
|
_g.AssertExpectations(t)
|
||||||
_e.M.AssertExpectations(t)
|
_e.M.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInstanceAllowedImages(t *testing.T) {
|
|
||||||
_f := &docker.FactoryMock{}
|
|
||||||
_s := &storage.Mock{}
|
|
||||||
_e := &event.Mock{}
|
|
||||||
|
|
||||||
p := NewPWD(_f, _e, _s)
|
|
||||||
|
|
||||||
expectedImages := []string{config.GetDindImageName(), "franela/dind:overlay2-dev", "franela/ucp:2.4.1"}
|
|
||||||
|
|
||||||
assert.Equal(t, expectedImages, p.InstanceAllowedImages())
|
|
||||||
}
|
|
||||||
|
|||||||
14
pwd/mock.go
14
pwd/mock.go
@@ -43,7 +43,7 @@ func (m *Mock) SessionSetup(session *types.Session, conf SessionSetupConf) error
|
|||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mock) InstanceNew(session *types.Session, conf InstanceConfig) (*types.Instance, error) {
|
func (m *Mock) InstanceNew(session *types.Session, conf types.InstanceConfig) (*types.Instance, error) {
|
||||||
args := m.Called(session, conf)
|
args := m.Called(session, conf)
|
||||||
return args.Get(0).(*types.Instance), args.Error(1)
|
return args.Get(0).(*types.Instance), args.Error(1)
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ func (m *Mock) InstanceGet(session *types.Session, name string) *types.Instance
|
|||||||
return args.Get(0).(*types.Instance)
|
return args.Get(0).(*types.Instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mock) InstanceFind(session, ip string) *types.Instance {
|
func (m *Mock) InstanceFindByIP(session, ip string) *types.Instance {
|
||||||
args := m.Called(session, ip)
|
args := m.Called(session, ip)
|
||||||
return args.Get(0).(*types.Instance)
|
return args.Get(0).(*types.Instance)
|
||||||
}
|
}
|
||||||
@@ -83,16 +83,16 @@ func (m *Mock) InstanceDelete(session *types.Session, instance *types.Instance)
|
|||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mock) InstanceAllowedImages() []string {
|
|
||||||
args := m.Called()
|
|
||||||
return args.Get(0).([]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Mock) InstanceExec(instance *types.Instance, cmd []string) (int, error) {
|
func (m *Mock) InstanceExec(instance *types.Instance, cmd []string) (int, error) {
|
||||||
args := m.Called(instance, cmd)
|
args := m.Called(instance, cmd)
|
||||||
return args.Int(0), args.Error(1)
|
return args.Int(0), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Mock) InstanceAllowedImages() []string {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Get(0).([]string)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Mock) ClientNew(id string, session *types.Session) *types.Client {
|
func (m *Mock) ClientNew(id string, session *types.Session) *types.Client {
|
||||||
args := m.Called(id, session)
|
args := m.Called(id, session)
|
||||||
return args.Get(0).(*types.Client)
|
return args.Get(0).(*types.Client)
|
||||||
|
|||||||
30
pwd/pwd.go
30
pwd/pwd.go
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/play-with-docker/play-with-docker/docker"
|
"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/event"
|
||||||
|
"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/pwd/types"
|
||||||
"github.com/play-with-docker/play-with-docker/storage"
|
"github.com/play-with-docker/play-with-docker/storage"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
@@ -47,11 +48,13 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type pwd struct {
|
type pwd struct {
|
||||||
dockerFactory docker.FactoryApi
|
dockerFactory docker.FactoryApi
|
||||||
event event.EventApi
|
event event.EventApi
|
||||||
storage storage.StorageApi
|
storage storage.StorageApi
|
||||||
generator IdGenerator
|
generator IdGenerator
|
||||||
clientCount int32
|
clientCount int32
|
||||||
|
windowsProvisioner provisioner.ProvisionerApi
|
||||||
|
dindProvisioner provisioner.ProvisionerApi
|
||||||
}
|
}
|
||||||
|
|
||||||
type IdGenerator interface {
|
type IdGenerator interface {
|
||||||
@@ -82,17 +85,16 @@ type PWDApi interface {
|
|||||||
SessionGet(id string) *types.Session
|
SessionGet(id string) *types.Session
|
||||||
SessionSetup(session *types.Session, conf SessionSetupConf) error
|
SessionSetup(session *types.Session, conf SessionSetupConf) error
|
||||||
|
|
||||||
InstanceNew(session *types.Session, conf InstanceConfig) (*types.Instance, error)
|
InstanceNew(session *types.Session, conf types.InstanceConfig) (*types.Instance, error)
|
||||||
InstanceResizeTerminal(instance *types.Instance, cols, rows uint) error
|
InstanceResizeTerminal(instance *types.Instance, cols, rows uint) error
|
||||||
InstanceGetTerminal(instance *types.Instance) (net.Conn, error)
|
InstanceGetTerminal(instance *types.Instance) (net.Conn, error)
|
||||||
InstanceUploadFromUrl(instance *types.Instance, fileName, dest, url string) error
|
InstanceUploadFromUrl(instance *types.Instance, fileName, dest, url string) error
|
||||||
InstanceUploadFromReader(instance *types.Instance, fileName, dest string, reader io.Reader) error
|
InstanceUploadFromReader(instance *types.Instance, fileName, dest string, reader io.Reader) error
|
||||||
InstanceGet(session *types.Session, name string) *types.Instance
|
InstanceGet(session *types.Session, name string) *types.Instance
|
||||||
// TODO remove this function when we add the session prefix to the PWD url
|
InstanceFindByIP(sessionId, ip string) *types.Instance
|
||||||
InstanceFind(sessionId, ip string) *types.Instance
|
|
||||||
InstanceDelete(session *types.Session, instance *types.Instance) error
|
InstanceDelete(session *types.Session, instance *types.Instance) error
|
||||||
InstanceAllowedImages() []string
|
|
||||||
InstanceExec(instance *types.Instance, cmd []string) (int, error)
|
InstanceExec(instance *types.Instance, cmd []string) (int, error)
|
||||||
|
InstanceAllowedImages() []string
|
||||||
|
|
||||||
ClientNew(id string, session *types.Session) *types.Client
|
ClientNew(id string, session *types.Session) *types.Client
|
||||||
ClientResizeViewPort(client *types.Client, cols, rows uint)
|
ClientResizeViewPort(client *types.Client, cols, rows uint)
|
||||||
@@ -101,7 +103,15 @@ type PWDApi interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewPWD(f docker.FactoryApi, e event.EventApi, s storage.StorageApi) *pwd {
|
func NewPWD(f docker.FactoryApi, e event.EventApi, s storage.StorageApi) *pwd {
|
||||||
return &pwd{dockerFactory: f, event: e, storage: s, generator: xidGenerator{}}
|
return &pwd{dockerFactory: f, event: e, storage: s, generator: xidGenerator{}, windowsProvisioner: provisioner.NewWindows(f), dindProvisioner: provisioner.NewDinD(f)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pwd) getProvisioner(t string) (provisioner.ProvisionerApi, error) {
|
||||||
|
if t == "windows" {
|
||||||
|
return p.windowsProvisioner, nil
|
||||||
|
} else {
|
||||||
|
return p.dindProvisioner, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pwd) docker(sessionId string) docker.DockerApi {
|
func (p *pwd) docker(sessionId string) docker.DockerApi {
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ func (p *pwd) SessionDeployStack(s *types.Session) error {
|
|||||||
|
|
||||||
s.Ready = false
|
s.Ready = false
|
||||||
p.event.Emit(event.SESSION_READY, s.Id, false)
|
p.event.Emit(event.SESSION_READY, s.Id, false)
|
||||||
i, err := p.InstanceNew(s, InstanceConfig{ImageName: s.ImageName, Host: s.Host})
|
i, err := p.InstanceNew(s, types.InstanceConfig{ImageName: s.ImageName, Host: s.Host})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error creating instance for stack [%s]: %s\n", s.Stack, err)
|
log.Printf("Error creating instance for stack [%s]: %s\n", s.Stack, err)
|
||||||
return err
|
return err
|
||||||
@@ -201,7 +201,7 @@ func (p *pwd) SessionSetup(session *types.Session, conf SessionSetupConf) error
|
|||||||
// first look for a swarm manager and create it
|
// first look for a swarm manager and create it
|
||||||
for _, conf := range conf.Instances {
|
for _, conf := range conf.Instances {
|
||||||
if conf.IsSwarmManager {
|
if conf.IsSwarmManager {
|
||||||
instanceConf := InstanceConfig{
|
instanceConf := types.InstanceConfig{
|
||||||
ImageName: conf.Image,
|
ImageName: conf.Image,
|
||||||
Hostname: conf.Hostname,
|
Hostname: conf.Hostname,
|
||||||
Host: session.Host,
|
Host: session.Host,
|
||||||
@@ -232,7 +232,7 @@ func (p *pwd) SessionSetup(session *types.Session, conf SessionSetupConf) error
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(c SessionSetupInstanceConf) {
|
go func(c SessionSetupInstanceConf) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
instanceConf := InstanceConfig{
|
instanceConf := types.InstanceConfig{
|
||||||
ImageName: c.Image,
|
ImageName: c.Image,
|
||||||
Hostname: c.Hostname,
|
Hostname: c.Hostname,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,19 @@ type Instance struct {
|
|||||||
SessionId string `json:"session_id" bson:"session_id"`
|
SessionId string `json:"session_id" bson:"session_id"`
|
||||||
ProxyHost string `json:"proxy_host" bson:"proxy_host"`
|
ProxyHost string `json:"proxy_host" bson:"proxy_host"`
|
||||||
SessionHost string `json:"session_host" bson:"session_host"`
|
SessionHost string `json:"session_host" bson:"session_host"`
|
||||||
|
Type string `json:"type" bson:"type"`
|
||||||
Session *Session `json:"-" bson:"-"`
|
Session *Session `json:"-" bson:"-"`
|
||||||
ctx context.Context `json:"-" bson:"-"`
|
ctx context.Context `json:"-" bson:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InstanceConfig struct {
|
||||||
|
ImageName string
|
||||||
|
Hostname string
|
||||||
|
ServerCert []byte
|
||||||
|
ServerKey []byte
|
||||||
|
CACert []byte
|
||||||
|
Cert []byte
|
||||||
|
Key []byte
|
||||||
|
Host string
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user