HTTPS and File Uploads (#139)
* Add a few fixes * Use CopyToContainer instead of bind mounts * Remove a local compose file * Changes according to the comments * Rebase with master
This commit is contained in:
committed by
Marcos Nils
parent
61a0bb4db1
commit
8df6373327
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
play-with-docker
|
play-with-docker
|
||||||
node_modules
|
node_modules
|
||||||
|
docker-compose.single.yml
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ ENV DOCKER_STORAGE_DRIVER=$docker_storage_driver
|
|||||||
# Move to our home
|
# Move to our home
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
|
|
||||||
|
RUN mkdir -p /var/run/pwd/certs && mkdir -p /var/run/pwd/uploads
|
||||||
|
|
||||||
# Remove IPv6 alias for localhost and start docker in the background ...
|
# Remove IPv6 alias for localhost and start docker in the background ...
|
||||||
CMD cat /etc/hosts >/etc/hosts.bak && \
|
CMD cat /etc/hosts >/etc/hosts.bak && \
|
||||||
sed 's/^::1.*//' /etc/hosts.bak > /etc/hosts && \
|
sed 's/^::1.*//' /etc/hosts.bak > /etc/hosts && \
|
||||||
|
|||||||
29
api.go
29
api.go
@@ -1,12 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
gh "github.com/gorilla/handlers"
|
gh "github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
@@ -71,9 +68,9 @@ func main() {
|
|||||||
corsRouter.HandleFunc("/instances/images", handlers.GetInstanceImages).Methods("GET")
|
corsRouter.HandleFunc("/instances/images", handlers.GetInstanceImages).Methods("GET")
|
||||||
corsRouter.HandleFunc("/sessions/{sessionId}", handlers.GetSession).Methods("GET")
|
corsRouter.HandleFunc("/sessions/{sessionId}", handlers.GetSession).Methods("GET")
|
||||||
corsRouter.HandleFunc("/sessions/{sessionId}/instances", handlers.NewInstance).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")
|
corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", handlers.DeleteInstance).Methods("DELETE")
|
||||||
corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/exec", handlers.Exec).Methods("POST")
|
corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/exec", handlers.Exec).Methods("POST")
|
||||||
r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/keys", handlers.SetKeys).Methods("POST")
|
|
||||||
|
|
||||||
h := func(w http.ResponseWriter, r *http.Request) {
|
h := func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.ServeFile(w, r, "./www/index.html")
|
http.ServeFile(w, r, "./www/index.html")
|
||||||
@@ -115,26 +112,6 @@ func main() {
|
|||||||
log.Fatal(http.ListenAndServe("0.0.0.0:"+config.PortNumber, n))
|
log.Fatal(http.ListenAndServe("0.0.0.0:"+config.PortNumber, n))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ssl := mux.NewRouter()
|
// Now listen for TLS connections that need to be proxied
|
||||||
sslProxyHandler := handlers.NewSSLDaemonHandler()
|
handlers.StartTLSProxy(config.SSLPortNumber)
|
||||||
ssl.Host(`{subdomain:.*}{node:pwd[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}}-2375.{tld:.*}`).Handler(sslProxyHandler)
|
|
||||||
log.Println("Listening TLS on port " + config.SSLPortNumber)
|
|
||||||
|
|
||||||
s := &http.Server{Addr: "0.0.0.0:" + config.SSLPortNumber, Handler: ssl}
|
|
||||||
s.TLSConfig = &tls.Config{}
|
|
||||||
s.TLSConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
||||||
|
|
||||||
chunks := strings.Split(clientHello.ServerName, ".")
|
|
||||||
chunks = strings.Split(chunks[0], "-")
|
|
||||||
ip := strings.Replace(strings.TrimPrefix(chunks[0], "pwd"), "_", ".", -1)
|
|
||||||
i := services.FindInstanceByIP(ip)
|
|
||||||
if i == nil {
|
|
||||||
return nil, fmt.Errorf("Instance %s doesn't exist", clientHello.ServerName)
|
|
||||||
}
|
|
||||||
if i.GetCertificate() == nil {
|
|
||||||
return nil, fmt.Errorf("Instance %s doesn't have a certificate", clientHello.ServerName)
|
|
||||||
}
|
|
||||||
return i.GetCertificate(), nil
|
|
||||||
}
|
|
||||||
log.Fatal(s.ListenAndServeTLS("", ""))
|
|
||||||
}
|
}
|
||||||
|
|||||||
47
handlers/file_upload.go
Normal file
47
handlers/file_upload.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/play-with-docker/play-with-docker/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FileUpload(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
sessionId := vars["sessionId"]
|
||||||
|
instanceName := vars["instanceName"]
|
||||||
|
|
||||||
|
s := services.GetSession(sessionId)
|
||||||
|
i := services.GetInstance(s, instanceName)
|
||||||
|
|
||||||
|
// allow up to 32 MB which is the default
|
||||||
|
|
||||||
|
// has a url query parameter, ignore body
|
||||||
|
if url := req.URL.Query().Get("url"); url != "" {
|
||||||
|
err := i.UploadFromURL(req.URL.Query().Get("url"))
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
// This is for multipart upload
|
||||||
|
log.Println("Not implemented yet")
|
||||||
|
|
||||||
|
/*
|
||||||
|
err := req.ParseMultipartForm(32 << 20)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ func NewInstance(rw http.ResponseWriter, req *http.Request) {
|
|||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
sessionId := vars["sessionId"]
|
sessionId := vars["sessionId"]
|
||||||
|
|
||||||
body := struct{ ImageName, Alias string }{}
|
body := services.InstanceConfig{}
|
||||||
|
|
||||||
json.NewDecoder(req.Body).Decode(&body)
|
json.NewDecoder(req.Body).Decode(&body)
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ func NewInstance(rw http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
i, err := services.NewInstance(s, body.ImageName, body.Alias)
|
i, err := services.NewInstance(s, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
rw.WriteHeader(http.StatusInternalServerError)
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
|||||||
90
handlers/tlsproxy.go
Normal file
90
handlers/tlsproxy.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
vhost "github.com/inconshreveable/go-vhost"
|
||||||
|
"github.com/play-with-docker/play-with-docker/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StartTLSProxy(port string) {
|
||||||
|
var validProxyHost = regexp.MustCompile(`^.*pwd([0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}(?:-[0-9]{1,5})?)\..*$`)
|
||||||
|
|
||||||
|
tlsListener, tlsErr := net.Listen("tcp", fmt.Sprintf(":%s", port))
|
||||||
|
log.Println("Listening on port " + port)
|
||||||
|
if tlsErr != nil {
|
||||||
|
log.Fatal(tlsErr)
|
||||||
|
}
|
||||||
|
defer tlsListener.Close()
|
||||||
|
for {
|
||||||
|
// Wait for TLS Connection
|
||||||
|
conn, err := tlsListener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not accept new TLS connection. Error: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Handle connection on a new goroutine and continue accepting other new connections
|
||||||
|
go func(c net.Conn) {
|
||||||
|
defer c.Close()
|
||||||
|
vhostConn, err := vhost.TLS(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Incoming TLS connection produced an error. Error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer vhostConn.Close()
|
||||||
|
|
||||||
|
host := vhostConn.ClientHelloMsg.ServerName
|
||||||
|
match := validProxyHost.FindStringSubmatch(host)
|
||||||
|
if len(match) == 2 {
|
||||||
|
// This is a valid proxy host, keep only the important part
|
||||||
|
host = match[1]
|
||||||
|
} else {
|
||||||
|
// Not a valid proxy host, just close connection.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetIP string
|
||||||
|
targetPort := "443"
|
||||||
|
|
||||||
|
hostPort := strings.Split(host, ":")
|
||||||
|
if len(hostPort) > 1 && hostPort[1] != config.SSLPortNumber {
|
||||||
|
targetPort = hostPort[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
target := strings.Split(hostPort[0], "-")
|
||||||
|
if len(target) > 1 {
|
||||||
|
targetPort = target[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := strings.Replace(target[0], "_", ".", -1)
|
||||||
|
|
||||||
|
if net.ParseIP(ip) == nil {
|
||||||
|
// Not a valid IP, so treat this is a hostname.
|
||||||
|
} else {
|
||||||
|
targetIP = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
dest := fmt.Sprintf("%s:%s", targetIP, targetPort)
|
||||||
|
d, err := net.Dial("tcp", dest)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error dialing backend %s: %v\n", dest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
errc := make(chan error, 2)
|
||||||
|
cp := func(dst io.Writer, src io.Reader) {
|
||||||
|
_, err := io.Copy(dst, src)
|
||||||
|
errc <- err
|
||||||
|
}
|
||||||
|
go cp(d, vhostConn)
|
||||||
|
go cp(vhostConn, d)
|
||||||
|
<-errc
|
||||||
|
}(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -199,7 +202,64 @@ func ResizeConnection(name string, cols, rows uint) error {
|
|||||||
return c.ContainerResize(context.Background(), name, types.ResizeOptions{Height: rows, Width: cols})
|
return c.ContainerResize(context.Background(), name, types.ResizeOptions{Height: rows, Width: cols})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateInstance(session *Session, dindImage string) (*Instance, error) {
|
func CopyToContainer(containerName, destination, fileName string, content io.Reader) error {
|
||||||
|
r, w := io.Pipe()
|
||||||
|
b, readErr := ioutil.ReadAll(content)
|
||||||
|
if readErr != nil {
|
||||||
|
return readErr
|
||||||
|
}
|
||||||
|
t := tar.NewWriter(w)
|
||||||
|
go func() {
|
||||||
|
t.WriteHeader(&tar.Header{Name: fileName, Mode: 0600, Size: int64(len(b))})
|
||||||
|
t.Write(b)
|
||||||
|
t.Close()
|
||||||
|
w.Close()
|
||||||
|
}()
|
||||||
|
return c.CopyToContainer(context.Background(), containerName, destination, r, types.CopyToContainerOptions{AllowOverwriteDirWithFile: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateInstance(session *Session, conf InstanceConfig) (*Instance, error) {
|
||||||
|
var nodeName string
|
||||||
|
var containerName string
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
nodeName = fmt.Sprintf("node%d", i)
|
||||||
|
containerName = fmt.Sprintf("%s_%s", session.Id[:8], nodeName)
|
||||||
|
exists := false
|
||||||
|
for _, instance := range session.Instances {
|
||||||
|
if instance.Name == containerName {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure directories are available for the new instance container
|
||||||
|
containerDir := "/var/run/pwd"
|
||||||
|
containerCertDir := fmt.Sprintf("%s/certs", containerDir)
|
||||||
|
|
||||||
|
env := []string{}
|
||||||
|
|
||||||
|
// Write certs to container cert dir
|
||||||
|
if len(conf.ServerCert) > 0 {
|
||||||
|
env = append(env, "DOCKER_TLSCERT=/var/run/pwd/certs/cert.pem")
|
||||||
|
}
|
||||||
|
if len(conf.ServerKey) > 0 {
|
||||||
|
env = append(env, "DOCKER_TLSKEY=/var/run/pwd/certs/key.pem")
|
||||||
|
}
|
||||||
|
if len(conf.CACert) > 0 {
|
||||||
|
// if ca cert is specified, verify that clients that connects present a certificate signed by the CA
|
||||||
|
env = append(env, "DOCKER_TLSCACERT=/var/run/pwd/certs/ca.pem")
|
||||||
|
}
|
||||||
|
if len(conf.ServerCert) > 0 || len(conf.ServerKey) > 0 || len(conf.CACert) > 0 {
|
||||||
|
// if any of the certs is specified, enable TLS
|
||||||
|
env = append(env, "DOCKER_TLSENABLE=true")
|
||||||
|
} else {
|
||||||
|
env = append(env, "DOCKER_TLSENABLE=false")
|
||||||
|
}
|
||||||
|
|
||||||
h := &container.HostConfig{
|
h := &container.HostConfig{
|
||||||
NetworkMode: container.NetworkMode(session.Id),
|
NetworkMode: container.NetworkMode(session.Id),
|
||||||
Privileged: true,
|
Privileged: true,
|
||||||
@@ -222,42 +282,37 @@ func CreateInstance(session *Session, dindImage string) (*Instance, error) {
|
|||||||
t := true
|
t := true
|
||||||
h.Resources.OomKillDisable = &t
|
h.Resources.OomKillDisable = &t
|
||||||
|
|
||||||
var nodeName string
|
env = append(env, fmt.Sprintf("PWD_IP_ADDRESS=%s", session.PwdIpAddress))
|
||||||
var containerName string
|
cf := &container.Config{Hostname: nodeName,
|
||||||
for i := 1; ; i++ {
|
|
||||||
nodeName = fmt.Sprintf("node%d", i)
|
|
||||||
containerName = fmt.Sprintf("%s_%s", session.Id[:8], nodeName)
|
|
||||||
exists := false
|
|
||||||
for _, instance := range session.Instances {
|
|
||||||
if instance.Name == containerName {
|
|
||||||
exists = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conf := &container.Config{Hostname: nodeName,
|
|
||||||
Image: dindImage,
|
Image: dindImage,
|
||||||
Tty: true,
|
Tty: true,
|
||||||
OpenStdin: true,
|
OpenStdin: true,
|
||||||
AttachStdin: true,
|
AttachStdin: true,
|
||||||
AttachStdout: true,
|
AttachStdout: true,
|
||||||
AttachStderr: true,
|
AttachStderr: true,
|
||||||
Env: []string{fmt.Sprintf("PWD_IP_ADDRESS=%s", session.PwdIpAddress)},
|
Env: env,
|
||||||
}
|
}
|
||||||
networkConf := &network.NetworkingConfig{
|
networkConf := &network.NetworkingConfig{
|
||||||
map[string]*network.EndpointSettings{
|
map[string]*network.EndpointSettings{
|
||||||
session.Id: &network.EndpointSettings{Aliases: []string{nodeName}},
|
session.Id: &network.EndpointSettings{Aliases: []string{nodeName}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
container, err := c.ContainerCreate(context.Background(), conf, h, networkConf, containerName)
|
container, err := c.ContainerCreate(context.Background(), cf, h, networkConf, containerName)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := copyIfSet(conf.ServerCert, "cert.pem", containerCertDir, containerName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := copyIfSet(conf.ServerKey, "key.pem", containerCertDir, containerName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := copyIfSet(conf.CACert, "ca.pem", containerCertDir, containerName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
err = c.ContainerStart(context.Background(), container.ID, types.ContainerStartOptions{})
|
err = c.ContainerStart(context.Background(), container.ID, types.ContainerStartOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -271,6 +326,13 @@ func CreateInstance(session *Session, dindImage string) (*Instance, error) {
|
|||||||
return &Instance{Name: containerName, Hostname: cinfo.Config.Hostname, IP: cinfo.NetworkSettings.Networks[session.Id].IPAddress}, nil
|
return &Instance{Name: containerName, Hostname: cinfo.Config.Hostname, IP: cinfo.NetworkSettings.Networks[session.Id].IPAddress}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyIfSet(content []byte, fileName, path, containerName string) error {
|
||||||
|
if len(content) > 0 {
|
||||||
|
return CopyToContainer(containerName, path, fileName, bytes.NewReader(content))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteContainer(id string) error {
|
func DeleteContainer(id string) error {
|
||||||
return c.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{Force: true, RemoveVolumes: true})
|
return c.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{Force: true, RemoveVolumes: true})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ package services
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -42,6 +45,14 @@ type Instance struct {
|
|||||||
cert *tls.Certificate `json:"-"`
|
cert *tls.Certificate `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InstanceConfig struct {
|
||||||
|
ImageName string
|
||||||
|
Alias string
|
||||||
|
ServerCert []byte
|
||||||
|
ServerKey []byte
|
||||||
|
CACert []byte
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Instance) setUsedPort(port uint16) {
|
func (i *Instance) setUsedPort(port uint16) {
|
||||||
rw.Lock()
|
rw.Lock()
|
||||||
defer rw.Unlock()
|
defer rw.Unlock()
|
||||||
@@ -97,17 +108,17 @@ func getDindImageName() string {
|
|||||||
return dindImage
|
return dindImage
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInstance(session *Session, imageName, alias string) (*Instance, error) {
|
func NewInstance(session *Session, conf InstanceConfig) (*Instance, error) {
|
||||||
if imageName == "" {
|
if conf.ImageName == "" {
|
||||||
imageName = dindImage
|
conf.ImageName = dindImage
|
||||||
}
|
}
|
||||||
log.Printf("NewInstance - using image: [%s]\n", imageName)
|
log.Printf("NewInstance - using image: [%s]\n", conf.ImageName)
|
||||||
instance, err := CreateInstance(session, imageName)
|
instance, err := CreateInstance(session, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.Alias = alias
|
instance.Alias = conf.Alias
|
||||||
|
|
||||||
instance.session = session
|
instance.session = session
|
||||||
|
|
||||||
@@ -163,6 +174,29 @@ func (i *Instance) Attach() {
|
|||||||
case <-i.ctx.Done():
|
case <-i.ctx.Done():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Instance) UploadFromURL(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, fileName := filepath.Split(url)
|
||||||
|
|
||||||
|
copyErr := CopyToContainer(i.Name, "/var/run/pwd/uploads", fileName, resp.Body)
|
||||||
|
|
||||||
|
if copyErr != nil {
|
||||||
|
return fmt.Errorf("Error while downloading file [%s]. Error: %s\n", url, copyErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetInstance(session *Session, name string) *Instance {
|
func GetInstance(session *Session, name string) *Instance {
|
||||||
return session.Instances[name]
|
return session.Instances[name]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user