diff --git a/docker/docker.go b/docker/docker.go index 58494f2..196272a 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -32,27 +32,34 @@ const ( type DockerApi interface { GetClient() *client.Client - CreateNetwork(id string, opts types.NetworkCreate) error - ConnectNetwork(container, network, ip string) (string, error) + + NetworkCreate(id string, opts types.NetworkCreate) error + NetworkConnect(container, network, ip string) (string, error) NetworkInspect(id string) (types.NetworkResource, error) - GetDaemonInfo() (types.Info, error) - GetDaemonHost() string + NetworkDelete(id string) error + NetworkDisconnect(containerId, networkId string) error + + DaemonInfo() (types.Info, error) + DaemonHost() string + GetSwarmPorts() ([]string, []uint16, error) GetPorts() ([]uint16, error) - GetContainerStats(name string) (io.ReadCloser, error) + + ContainerStats(name string) (io.ReadCloser, error) ContainerResize(name string, rows, cols uint) error ContainerRename(old, new string) error + ContainerDelete(name string) error + ContainerCreate(opts CreateContainerOpts) error + ContainerIPs(id string) (map[string]string, error) + ExecAttach(instanceName string, command []string, out io.Writer) (int, error) + Exec(instanceName string, command []string) (int, error) + CreateAttachConnection(name string) (net.Conn, error) CopyToContainer(containerName, destination, fileName string, content io.Reader) error - DeleteContainer(name string) error - CreateContainer(opts CreateContainerOpts) error - GetContainerIPs(id string) (map[string]string, error) - ExecAttach(instanceName string, command []string, out io.Writer) (int, error) - DisconnectNetwork(containerId, networkId string) error - DeleteNetwork(id string) error - Exec(instanceName string, command []string) (int, error) + CopyFromContainer(containerName, filePath string) (io.Reader, error) SwarmInit(advertiseAddr string) (*SwarmTokens, error) SwarmJoin(addr, token string) error + ConfigCreate(name string, labels map[string]string, data []byte) error ConfigDelete(name string) error } @@ -82,7 +89,7 @@ func (d *docker) ConfigDelete(name string) error { return d.c.ConfigRemove(context.Background(), name) } -func (d *docker) CreateNetwork(id string, opts types.NetworkCreate) error { +func (d *docker) NetworkCreate(id string, opts types.NetworkCreate) error { _, err := d.c.NetworkCreate(context.Background(), id, opts) if err != nil { @@ -94,7 +101,7 @@ func (d *docker) CreateNetwork(id string, opts types.NetworkCreate) error { return nil } -func (d *docker) ConnectNetwork(containerId, networkId, ip string) (string, error) { +func (d *docker) NetworkConnect(containerId, networkId, ip string) (string, error) { settings := &network.EndpointSettings{} if ip != "" { settings.IPAddress = ip @@ -125,11 +132,11 @@ func (d *docker) NetworkInspect(id string) (types.NetworkResource, error) { return d.c.NetworkInspect(context.Background(), id, types.NetworkInspectOptions{}) } -func (d *docker) GetDaemonInfo() (types.Info, error) { +func (d *docker) DaemonInfo() (types.Info, error) { return d.c.Info(context.Background()) } -func (d *docker) GetDaemonHost() string { +func (d *docker) DaemonHost() string { return d.c.DaemonHost() } @@ -180,7 +187,7 @@ func (d *docker) GetPorts() ([]uint16, error) { return openPorts, nil } -func (d *docker) GetContainerStats(name string) (io.ReadCloser, error) { +func (d *docker) ContainerStats(name string) (io.ReadCloser, error) { stats, err := d.c.ContainerStats(context.Background(), name, false) return stats.Body, err @@ -222,7 +229,21 @@ func (d *docker) CopyToContainer(containerName, destination, fileName string, co return d.c.CopyToContainer(context.Background(), containerName, destination, r, types.CopyToContainerOptions{AllowOverwriteDirWithFile: true}) } -func (d *docker) DeleteContainer(name string) error { +func (d *docker) CopyFromContainer(containerName, filePath string) (io.Reader, error) { + rc, stat, err := d.c.CopyFromContainer(context.Background(), containerName, filePath) + if err != nil { + return nil, err + } + if stat.Mode.IsDir() { + return nil, fmt.Errorf("Copying directories is not supported") + } + tr := tar.NewReader(rc) + // advance to the only possible file in the tar archive + tr.Next() + return tr, nil +} + +func (d *docker) ContainerDelete(name string) error { err := d.c.ContainerRemove(context.Background(), name, types.ContainerRemoveOptions{Force: true, RemoveVolumes: true}) d.c.VolumeRemove(context.Background(), name, true) return err @@ -242,7 +263,7 @@ type CreateContainerOpts struct { Networks []string } -func (d *docker) CreateContainer(opts CreateContainerOpts) (err error) { +func (d *docker) ContainerCreate(opts CreateContainerOpts) (err error) { // Make sure directories are available for the new instance container containerDir := "/var/run/pwd" containerCertDir := fmt.Sprintf("%s/certs", containerDir) @@ -382,7 +403,7 @@ func (d *docker) CreateContainer(opts CreateContainerOpts) (err error) { return } -func (d *docker) GetContainerIPs(id string) (map[string]string, error) { +func (d *docker) ContainerIPs(id string) (map[string]string, error) { cinfo, err := d.c.ContainerInspect(context.Background(), id) if err != nil { return nil, err @@ -468,7 +489,7 @@ func (d *docker) Exec(instanceName string, command []string) (int, error) { return ins.ExitCode, nil } -func (d *docker) DisconnectNetwork(containerId, networkId string) error { +func (d *docker) NetworkDisconnect(containerId, networkId string) error { err := d.c.NetworkDisconnect(context.Background(), networkId, containerId, true) if err != nil { @@ -480,7 +501,7 @@ func (d *docker) DisconnectNetwork(containerId, networkId string) error { return nil } -func (d *docker) DeleteNetwork(id string) error { +func (d *docker) NetworkDelete(id string) error { err := d.c.NetworkRemove(context.Background(), id) if err != nil { diff --git a/docker/mock.go b/docker/mock.go index 024918d..4d0ad0c 100644 --- a/docker/mock.go +++ b/docker/mock.go @@ -5,8 +5,8 @@ import ( "net" "time" - "docker.io/go-docker/api/types" client "docker.io/go-docker" + "docker.io/go-docker/api/types" "github.com/stretchr/testify/mock" ) @@ -19,12 +19,12 @@ func (m *Mock) GetClient() *client.Client { return args.Get(0).(*client.Client) } -func (m *Mock) CreateNetwork(id string, opts types.NetworkCreate) error { +func (m *Mock) NetworkCreate(id string, opts types.NetworkCreate) error { args := m.Called(id, opts) return args.Error(0) } -func (m *Mock) ConnectNetwork(container, network, ip string) (string, error) { +func (m *Mock) NetworkConnect(container, network, ip string) (string, error) { args := m.Called(container, network, ip) return args.String(0), args.Error(1) } @@ -34,12 +34,12 @@ func (m *Mock) NetworkInspect(id string) (types.NetworkResource, error) { return args.Get(0).(types.NetworkResource), args.Error(1) } -func (m *Mock) GetDaemonInfo() (types.Info, error) { +func (m *Mock) DaemonInfo() (types.Info, error) { args := m.Called() return args.Get(0).(types.Info), args.Error(1) } -func (m *Mock) GetDaemonHost() string { +func (m *Mock) DaemonHost() string { args := m.Called() return args.String(0) } @@ -53,7 +53,7 @@ func (m *Mock) GetPorts() ([]uint16, error) { args := m.Called() return args.Get(0).([]uint16), args.Error(1) } -func (m *Mock) GetContainerStats(name string) (io.ReadCloser, error) { +func (m *Mock) ContainerStats(name string) (io.ReadCloser, error) { args := m.Called(name) return args.Get(0).(io.ReadCloser), args.Error(1) } @@ -73,15 +73,20 @@ func (m *Mock) CopyToContainer(containerName, destination, fileName string, cont args := m.Called(containerName, destination, fileName, content) return args.Error(0) } -func (m *Mock) DeleteContainer(id string) error { + +func (m *Mock) CopyFromContainer(containerName, filePath string) (io.Reader, error) { + args := m.Called(containerName, filePath) + return args.Get(0).(io.Reader), args.Error(1) +} +func (m *Mock) ContainerDelete(id string) error { args := m.Called(id) return args.Error(0) } -func (m *Mock) CreateContainer(opts CreateContainerOpts) error { +func (m *Mock) ContainerCreate(opts CreateContainerOpts) error { args := m.Called(opts) return args.Error(0) } -func (m *Mock) GetContainerIPs(id string) (map[string]string, error) { +func (m *Mock) ContainerIPs(id string) (map[string]string, error) { args := m.Called(id) return args.Get(0).(map[string]string), args.Error(1) } @@ -90,11 +95,11 @@ func (m *Mock) ExecAttach(instanceName string, command []string, out io.Writer) args := m.Called(instanceName, command, out) return args.Int(0), args.Error(1) } -func (m *Mock) DisconnectNetwork(containerId, networkId string) error { +func (m *Mock) NetworkDisconnect(containerId, networkId string) error { args := m.Called(containerId, networkId) return args.Error(0) } -func (m *Mock) DeleteNetwork(id string) error { +func (m *Mock) NetworkDelete(id string) error { args := m.Called(id) return args.Error(0) } diff --git a/dockerfiles/dind/Dockerfile.dind b/dockerfiles/dind/Dockerfile similarity index 100% rename from dockerfiles/dind/Dockerfile.dind rename to dockerfiles/dind/Dockerfile diff --git a/dockerfiles/k8s/Dockerfile.final b/dockerfiles/k8s/Dockerfile.final deleted file mode 100644 index 188696c..0000000 --- a/dockerfiles/k8s/Dockerfile.final +++ /dev/null @@ -1,8 +0,0 @@ -FROM franela/kind_builder - -COPY motd /etc/motd -RUN echo $'cat /etc/motd \n\ -export PS1="[\h \W]$ "' >> /root/.bash_profile - -CMD systemctl start docker && systemctl start kubelet \ - && while true; do bash -l; done diff --git a/dockerfiles/pwm/.gitconfig b/dockerfiles/pwm/.gitconfig new file mode 100644 index 0000000..87b8f9b --- /dev/null +++ b/dockerfiles/pwm/.gitconfig @@ -0,0 +1,2 @@ +[url "https://"] + insteadOf = git:// diff --git a/dockerfiles/pwm/.inputrc b/dockerfiles/pwm/.inputrc new file mode 100644 index 0000000..6a5b035 --- /dev/null +++ b/dockerfiles/pwm/.inputrc @@ -0,0 +1,73 @@ +# /etc/inputrc - global inputrc for libreadline +# See readline(3readline) and `info rluserman' for more information. + +# Be 8 bit clean. +set input-meta on +set output-meta on + +# To allow the use of 8bit-characters like the german umlauts, uncomment +# the line below. However this makes the meta key not work as a meta key, +# which is annoying to those which don't need to type in 8-bit characters. + +# set convert-meta off + +# try to enable the application keypad when it is called. Some systems +# need this to enable the arrow keys. +# set enable-keypad on + +# see /usr/share/doc/bash/inputrc.arrows for other codes of arrow keys + +# do not bell on tab-completion +# set bell-style none +# set bell-style visible + +# some defaults / modifications for the emacs mode +$if mode=emacs + +# allow the use of the Home/End keys +"\e[1~": beginning-of-line +"\e[4~": end-of-line + +# allow the use of the Delete/Insert keys +"\e[3~": delete-char +"\e[2~": quoted-insert + +# mappings for "page up" and "page down" to step to the beginning/end +# of the history +# "\e[5~": beginning-of-history +# "\e[6~": end-of-history + +# alternate mappings for "page up" and "page down" to search the history +# "\e[5~": history-search-backward +# "\e[6~": history-search-forward + +# mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving +"\e[1;5C": forward-word +"\e[1;5D": backward-word +"\e[5C": forward-word +"\e[5D": backward-word +"\e\e[C": forward-word +"\e\e[D": backward-word + +$if term=rxvt +"\e[7~": beginning-of-line +"\e[8~": end-of-line +"\eOc": forward-word +"\eOd": backward-word +$endif + +# for non RH/Debian xterm, can't hurt for RH/Debian xterm +# "\eOH": beginning-of-line +# "\eOF": end-of-line + +# for freebsd console +# "\e[H": beginning-of-line +# "\e[F": end-of-line + +$endif + +# faster completion +set show-all-if-ambiguous on + +"\e[A": history-search-backward +"\e[B": history-search-forward diff --git a/dockerfiles/pwm/.profile b/dockerfiles/pwm/.profile new file mode 100644 index 0000000..3269f8b --- /dev/null +++ b/dockerfiles/pwm/.profile @@ -0,0 +1,5 @@ +export PS1='\e[1m\e[31m[\h] \e[32m\e[34m\u@$(hostname -i)\e[35m \w\e[0m\n$ ' +alias vi='vim' +export PATH=$PATH:/root/go/bin +cat /etc/motd +echo $BASHPID > /var/run/cwd diff --git a/dockerfiles/pwm/.vimrc b/dockerfiles/pwm/.vimrc new file mode 100644 index 0000000..5919020 --- /dev/null +++ b/dockerfiles/pwm/.vimrc @@ -0,0 +1,6 @@ +syntax on +set autoindent +set expandtab +set number +set shiftwidth=2 +set softtabstop=2 diff --git a/dockerfiles/pwm/Dockerfile b/dockerfiles/pwm/Dockerfile new file mode 100644 index 0000000..6e34504 --- /dev/null +++ b/dockerfiles/pwm/Dockerfile @@ -0,0 +1,44 @@ +ARG VERSION=docker:stable-dind +FROM ${VERSION} + +RUN apk add --no-cache git tmux vim curl bash build-base qemu-img qemu-system-x86_64 + +ENV GOPATH /root/go +ENV PATH $PATH:$GOPATH + +# Use specific moby commit due to vendoring mismatch +ENV MOBY_COMMIT="d9d2a91780b34b92e669bbfa099f613bd9fad6bb" + +RUN mkdir /root/go && apk add --no-cache go \ + && go get -u -d github.com/moby/tool/cmd/moby && (cd $GOPATH/src/github.com/moby/tool/cmd/moby && git checkout $MOBY_COMMIT && go install) \ + && go get -u github.com/linuxkit/linuxkit/src/cmd/linuxkit \ + && rm -rf /root/go/pkg && rm -rf /root/go/src && rm -rf /usr/lib/go + + +# Add bash completion and set bash as default shell +RUN mkdir /etc/bash_completion.d \ + && curl https://raw.githubusercontent.com/docker/cli/master/contrib/completion/bash/docker -o /etc/bash_completion.d/docker \ + && sed -i "s/ash/bash/" /etc/passwd + + +# Replace modprobe with a no-op to get rid of spurious warnings +# (note: we can't just symlink to /bin/true because it might be busybox) +RUN rm /sbin/modprobe && echo '#!/bin/true' >/sbin/modprobe && chmod +x /sbin/modprobe + +# Install a nice vimrc file and prompt (by soulshake) +COPY ["sudo", "/usr/local/bin/"] +COPY [".vimrc", ".profile", ".inputrc", ".gitconfig", "./root/"] +COPY ["motd", "/etc/motd"] +COPY ["daemon.json", "/etc/docker/"] + +# Move to our home +WORKDIR /root + + +# Remove IPv6 alias for localhost and start docker in the background ... +CMD cat /etc/hosts >/etc/hosts.bak && \ + sed 's/^::1.*//' /etc/hosts.bak > /etc/hosts && \ + mount -t securityfs none /sys/kernel/security && \ + dockerd &>/docker.log & \ + while true ; do /bin/bash -l; done +# ... and then put a shell in the foreground, restarting it if it exits diff --git a/dockerfiles/pwm/daemon.json b/dockerfiles/pwm/daemon.json new file mode 100644 index 0000000..2792f93 --- /dev/null +++ b/dockerfiles/pwm/daemon.json @@ -0,0 +1,7 @@ +{ + "experimental": true, + "debug": true, + "log-level": "info", + "insecure-registries": ["127.0.0.1"], + "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"] +} diff --git a/dockerfiles/pwm/motd b/dockerfiles/pwm/motd new file mode 100644 index 0000000..2c1701b --- /dev/null +++ b/dockerfiles/pwm/motd @@ -0,0 +1,8 @@ +############################################################### +# WARNING!!!! # +# This is a sandbox environment. Using personal credentials # +# is HIGHLY! discouraged. Any consequences of doing so are # +# completely the user's responsibilites. # +# # +# The PWD team. # +############################################################### diff --git a/dockerfiles/pwm/sudo b/dockerfiles/pwm/sudo new file mode 100755 index 0000000..c832c17 --- /dev/null +++ b/dockerfiles/pwm/sudo @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +# This is shim to help with the case were pasted commands from a readme assume you are not root. Since this isto be run by root, it should effectively be a dummy command that allows the parameters to pass through. + +exec $@ diff --git a/handlers/bootstrap.go b/handlers/bootstrap.go index 6855b74..7acdf47 100644 --- a/handlers/bootstrap.go +++ b/handlers/bootstrap.go @@ -68,8 +68,10 @@ func Register(extend HandlerExtender) { corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/uploads", FileUpload).Methods("POST") corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", DeleteInstance).Methods("DELETE") corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/exec", Exec).Methods("POST") + corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/fstree", fsTree).Methods("GET") + corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/file", file).Methods("GET") - r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/editor.html", func(rw http.ResponseWriter, r *http.Request) { + r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/editor", func(rw http.ResponseWriter, r *http.Request) { http.ServeFile(rw, r, "www/editor.html") }) diff --git a/handlers/file_instance.go b/handlers/file_instance.go new file mode 100644 index 0000000..2d7e3fc --- /dev/null +++ b/handlers/file_instance.go @@ -0,0 +1,53 @@ +package handlers + +import ( + "encoding/base64" + "io" + "log" + "net/http" + + "github.com/gorilla/mux" +) + +func file(rw http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + sessionId := vars["sessionId"] + instanceName := vars["instanceName"] + + query := req.URL.Query() + + path := query.Get("path") + + if path == "" { + rw.WriteHeader(http.StatusBadRequest) + return + } + + s, _ := core.SessionGet(sessionId) + if s == nil { + rw.WriteHeader(http.StatusNotFound) + return + } + + i := core.InstanceGet(s, instanceName) + if i == nil { + rw.WriteHeader(http.StatusNotFound) + return + } + + instanceFile, err := core.InstanceFile(i, path) + + if err != nil { + log.Println(err) + rw.WriteHeader(http.StatusInternalServerError) + return + } + + encoder := base64.NewEncoder(base64.StdEncoding, rw) + + if _, err = io.Copy(encoder, instanceFile); err != nil { + log.Println(err) + rw.WriteHeader(http.StatusInternalServerError) + return + } +} diff --git a/handlers/fstree_instance.go b/handlers/fstree_instance.go new file mode 100644 index 0000000..9e1b175 --- /dev/null +++ b/handlers/fstree_instance.go @@ -0,0 +1,42 @@ +package handlers + +import ( + "io" + "log" + "net/http" + + "github.com/gorilla/mux" +) + +func fsTree(rw http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + sessionId := vars["sessionId"] + instanceName := vars["instanceName"] + + s, _ := core.SessionGet(sessionId) + if s == nil { + rw.WriteHeader(http.StatusNotFound) + return + } + + i := core.InstanceGet(s, instanceName) + if i == nil { + rw.WriteHeader(http.StatusNotFound) + return + } + + tree, err := core.InstanceFSTree(i) + + if err != nil { + log.Println(err) + rw.WriteHeader(http.StatusInternalServerError) + return + } + + rw.Header().Set("content-type", "application/json") + if _, err = io.Copy(rw, tree); err != nil { + log.Println(err) + rw.WriteHeader(http.StatusInternalServerError) + return + } +} diff --git a/provisioner/dind.go b/provisioner/dind.go index a840650..1714d9a 100644 --- a/provisioner/dind.go +++ b/provisioner/dind.go @@ -83,11 +83,11 @@ func (d *DinD) InstanceNew(session *types.Session, conf types.InstanceConfig) (* if err != nil { return nil, err } - if err := dockerClient.CreateContainer(opts); err != nil { + if err := dockerClient.ContainerCreate(opts); err != nil { return nil, err } - ips, err := dockerClient.GetContainerIPs(containerName) + ips, err := dockerClient.ContainerIPs(containerName) if err != nil { return nil, err } @@ -131,7 +131,7 @@ func (d *DinD) InstanceDelete(session *types.Session, instance *types.Instance) if err != nil { return err } - err = dockerClient.DeleteContainer(instance.Name) + err = dockerClient.ContainerDelete(instance.Name) if err != nil && !strings.Contains(err.Error(), "No such container") { return err } @@ -150,6 +150,40 @@ func (d *DinD) InstanceExec(instance *types.Instance, cmd []string) (int, error) return dockerClient.Exec(instance.Name, cmd) } +func (d *DinD) InstanceFSTree(instance *types.Instance) (io.Reader, error) { + session, err := d.getSession(instance.SessionId) + if err != nil { + return nil, err + } + dockerClient, err := d.factory.GetForSession(session) + if err != nil { + return nil, err + } + b := bytes.NewBuffer([]byte{}) + + if c, err := dockerClient.ExecAttach(instance.Name, []string{"bash", "-c", `tree --noreport -J $HOME`}, b); c > 0 { + log.Println(b.String()) + return nil, fmt.Errorf("Error %d trying list directories", c) + } else if err != nil { + return nil, err + } + + return b, nil +} + +func (d *DinD) InstanceFile(instance *types.Instance, filePath string) (io.Reader, error) { + session, err := d.getSession(instance.SessionId) + if err != nil { + return nil, err + } + dockerClient, err := d.factory.GetForSession(session) + if err != nil { + return nil, err + } + + return dockerClient.CopyFromContainer(instance.Name, filePath) +} + func (d *DinD) InstanceResizeTerminal(instance *types.Instance, rows, cols uint) error { session, err := d.getSession(instance.SessionId) if err != nil { diff --git a/provisioner/overlay.go b/provisioner/overlay.go index 9f08065..ea60888 100644 --- a/provisioner/overlay.go +++ b/provisioner/overlay.go @@ -27,7 +27,7 @@ func (p *overlaySessionProvisioner) SessionNew(ctx context.Context, s *types.Ses // We assume we are out of capacity return fmt.Errorf("Out of capacity") } - u, _ := url.Parse(dockerClient.GetDaemonHost()) + u, _ := url.Parse(dockerClient.DaemonHost()) if u.Host == "" { s.Host = "localhost" } else { @@ -36,13 +36,13 @@ func (p *overlaySessionProvisioner) SessionNew(ctx context.Context, s *types.Ses } opts := dtypes.NetworkCreate{Driver: "overlay", Attachable: true} - if err := dockerClient.CreateNetwork(s.Id, opts); err != nil { + if err := dockerClient.NetworkCreate(s.Id, opts); err != nil { log.Println("ERROR NETWORKING", err) return err } log.Printf("Network [%s] created for session [%s]\n", s.Id, s.Id) - ip, err := dockerClient.ConnectNetwork(config.L2ContainerName, s.Id, s.PwdIpAddress) + ip, err := dockerClient.NetworkConnect(config.L2ContainerName, s.Id, s.PwdIpAddress) if err != nil { log.Println(err) return err @@ -58,14 +58,14 @@ func (p *overlaySessionProvisioner) SessionClose(s *types.Session) error { log.Println(err) return err } - if err := dockerClient.DisconnectNetwork(config.L2ContainerName, s.Id); err != nil { + if err := dockerClient.NetworkDisconnect(config.L2ContainerName, s.Id); err != nil { if !strings.Contains(err.Error(), "is not connected to the network") { log.Println("ERROR NETWORKING", err) return err } } log.Printf("Disconnected l2 from network [%s]\n", s.Id) - if err := dockerClient.DeleteNetwork(s.Id); err != nil { + if err := dockerClient.NetworkDelete(s.Id); err != nil { if !strings.Contains(err.Error(), "not found") { log.Println(err) return err diff --git a/provisioner/provisioner.go b/provisioner/provisioner.go index 8167c2f..400ed11 100644 --- a/provisioner/provisioner.go +++ b/provisioner/provisioner.go @@ -19,6 +19,8 @@ type InstanceProvisionerApi interface { InstanceNew(session *types.Session, conf types.InstanceConfig) (*types.Instance, error) InstanceDelete(session *types.Session, instance *types.Instance) error InstanceExec(instance *types.Instance, cmd []string) (int, error) + InstanceFSTree(instance *types.Instance) (io.Reader, error) + InstanceFile(instance *types.Instance, filePath string) (io.Reader, error) InstanceResizeTerminal(instance *types.Instance, cols, rows uint) error InstanceGetTerminal(instance *types.Instance) (net.Conn, error) diff --git a/provisioner/windows.go b/provisioner/windows.go index a533e3e..19c1cda 100644 --- a/provisioner/windows.go +++ b/provisioner/windows.go @@ -155,6 +155,15 @@ func (d *windows) InstanceExec(instance *types.Instance, cmd []string) (int, err return ex.ExitCode, nil } +func (d *windows) InstanceFSTree(instance *types.Instance) (io.Reader, error) { + //TODO implement + return nil, nil +} +func (d *windows) InstanceFile(instance *types.Instance, filePath string) (io.Reader, error) { + //TODO implement + return nil, nil +} + func (d *windows) releaseInstance(instanceId string) error { return d.storage.WindowsInstanceDelete(instanceId) } diff --git a/pwd/client_test.go b/pwd/client_test.go index 1ba5487..af30718 100644 --- a/pwd/client_test.go +++ b/pwd/client_test.go @@ -29,9 +29,9 @@ func TestClientNew(t *testing.T) { _g.On("NewId").Return("aaaabbbbcccc") _f.On("GetForSession", mock.AnythingOfType("*types.Session")).Return(_d, nil) - _d.On("CreateNetwork", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) - _d.On("GetDaemonHost").Return("localhost") - _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) + _d.On("NetworkCreate", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) + _d.On("DaemonHost").Return("localhost") + _d.On("NetworkConnect", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionCount").Return(1, nil) _s.On("InstanceCount").Return(0, nil) @@ -72,9 +72,9 @@ func TestClientCount(t *testing.T) { _g.On("NewId").Return("aaaabbbbcccc") _f.On("GetForSession", mock.AnythingOfType("*types.Session")).Return(_d, nil) - _d.On("CreateNetwork", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) - _d.On("GetDaemonHost").Return("localhost") - _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) + _d.On("NetworkCreate", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) + _d.On("DaemonHost").Return("localhost") + _d.On("NetworkConnect", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("ClientPut", mock.AnythingOfType("*types.Client")).Return(nil) _s.On("ClientCount").Return(1, nil) @@ -113,9 +113,9 @@ func TestClientResizeViewPort(t *testing.T) { _g.On("NewId").Return("aaaabbbbcccc") _f.On("GetForSession", mock.AnythingOfType("*types.Session")).Return(_d, nil) - _d.On("CreateNetwork", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) - _d.On("GetDaemonHost").Return("localhost") - _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) + _d.On("NetworkCreate", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) + _d.On("DaemonHost").Return("localhost") + _d.On("NetworkConnect", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionCount").Return(1, nil) _s.On("InstanceCount").Return(0, nil) diff --git a/pwd/instance.go b/pwd/instance.go index e2b6d91..5880e46 100644 --- a/pwd/instance.go +++ b/pwd/instance.go @@ -149,3 +149,23 @@ func (p *pwd) InstanceExec(instance *types.Instance, cmd []string) (int, error) } return exitCode, nil } + +func (p *pwd) InstanceFSTree(instance *types.Instance) (io.Reader, error) { + defer observeAction("InstanceFSTree", time.Now()) + + prov, err := p.getProvisioner(instance.Type) + if err != nil { + return nil, err + } + return prov.InstanceFSTree(instance) +} + +func (p *pwd) InstanceFile(instance *types.Instance, filePath string) (io.Reader, error) { + defer observeAction("InstanceFile", time.Now()) + + prov, err := p.getProvisioner(instance.Type) + if err != nil { + return nil, err + } + return prov.InstanceFile(instance, filePath) +} diff --git a/pwd/instance_test.go b/pwd/instance_test.go index acd0c2d..ca93f8c 100644 --- a/pwd/instance_test.go +++ b/pwd/instance_test.go @@ -56,9 +56,9 @@ func TestInstanceNew(t *testing.T) { _g.On("NewId").Return("aaaabbbbcccc") _f.On("GetForSession", mock.AnythingOfType("*types.Session")).Return(_d, nil) - _d.On("CreateNetwork", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) - _d.On("GetDaemonHost").Return("localhost") - _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) + _d.On("NetworkCreate", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) + _d.On("DaemonHost").Return("localhost") + _d.On("NetworkConnect", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionCount").Return(1, nil) _s.On("ClientCount").Return(0, nil) @@ -101,8 +101,8 @@ func TestInstanceNew(t *testing.T) { HostFQDN: "something.play-with-docker.com", Networks: []string{session.Id}, } - _d.On("CreateContainer", expectedContainerOpts).Return(nil) - _d.On("GetContainerIPs", expectedInstance.Name).Return(map[string]string{session.Id: "10.0.0.1"}, nil) + _d.On("ContainerCreate", expectedContainerOpts).Return(nil) + _d.On("ContainerIPs", expectedInstance.Name).Return(map[string]string{session.Id: "10.0.0.1"}, nil) _s.On("InstancePut", mock.AnythingOfType("*types.Instance")).Return(nil) _e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_aaaabbbbcccc", "10.0.0.1", "node1", "ip10-0-0-1-aaaabbbbcccc"}).Return() @@ -129,9 +129,9 @@ func TestInstanceNew_WithNotAllowedImage(t *testing.T) { _g.On("NewId").Return("aaaabbbbcccc") _f.On("GetForSession", mock.AnythingOfType("*types.Session")).Return(_d, nil) - _d.On("CreateNetwork", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) - _d.On("GetDaemonHost").Return("localhost") - _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) + _d.On("NetworkCreate", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) + _d.On("DaemonHost").Return("localhost") + _d.On("NetworkConnect", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionCount").Return(1, nil) _s.On("ClientCount").Return(0, nil) @@ -171,8 +171,8 @@ func TestInstanceNew_WithNotAllowedImage(t *testing.T) { Privileged: true, Networks: []string{session.Id}, } - _d.On("CreateContainer", expectedContainerOpts).Return(nil) - _d.On("GetContainerIPs", expectedInstance.Name).Return(map[string]string{session.Id: "10.0.0.1"}, nil) + _d.On("ContainerCreate", expectedContainerOpts).Return(nil) + _d.On("ContainerIPs", expectedInstance.Name).Return(map[string]string{session.Id: "10.0.0.1"}, nil) _s.On("InstancePut", mock.AnythingOfType("*types.Instance")).Return(nil) _e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_aaaabbbbcccc", "10.0.0.1", "node1", "ip10-0-0-1-aaaabbbbcccc"}).Return() @@ -200,9 +200,9 @@ func TestInstanceNew_WithCustomHostname(t *testing.T) { _g.On("NewId").Return("aaaabbbbcccc") _f.On("GetForSession", mock.AnythingOfType("*types.Session")).Return(_d, nil) - _d.On("CreateNetwork", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) - _d.On("GetDaemonHost").Return("localhost") - _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) + _d.On("NetworkCreate", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) + _d.On("DaemonHost").Return("localhost") + _d.On("NetworkConnect", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionCount").Return(1, nil) _s.On("ClientCount").Return(0, nil) @@ -242,8 +242,8 @@ func TestInstanceNew_WithCustomHostname(t *testing.T) { Networks: []string{session.Id}, } - _d.On("CreateContainer", expectedContainerOpts).Return(nil) - _d.On("GetContainerIPs", expectedInstance.Name).Return(map[string]string{session.Id: "10.0.0.1"}, nil) + _d.On("ContainerCreate", expectedContainerOpts).Return(nil) + _d.On("ContainerIPs", expectedInstance.Name).Return(map[string]string{session.Id: "10.0.0.1"}, nil) _s.On("InstancePut", mock.AnythingOfType("*types.Instance")).Return(nil) _e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_aaaabbbbcccc", "10.0.0.1", "redis-master", "ip10-0-0-1-aaaabbbbcccc"}).Return() diff --git a/pwd/mock.go b/pwd/mock.go index bd67082..e39c901 100644 --- a/pwd/mock.go +++ b/pwd/mock.go @@ -87,6 +87,16 @@ func (m *Mock) InstanceExec(instance *types.Instance, cmd []string) (int, error) return args.Int(0), args.Error(1) } +func (m *Mock) InstanceFSTree(instance *types.Instance) (io.Reader, error) { + args := m.Called(instance) + return args.Get(0).(io.Reader), args.Error(1) +} + +func (m *Mock) InstanceFile(instance *types.Instance, filePath string) (io.Reader, error) { + args := m.Called(instance, filePath) + return args.Get(0).(io.Reader), args.Error(1) +} + func (m *Mock) ClientNew(id string, session *types.Session) *types.Client { args := m.Called(id, session) return args.Get(0).(*types.Client) diff --git a/pwd/pwd.go b/pwd/pwd.go index 6f8fcf0..3c64586 100644 --- a/pwd/pwd.go +++ b/pwd/pwd.go @@ -89,6 +89,8 @@ type PWDApi interface { InstanceFindBySession(session *types.Session) ([]*types.Instance, error) InstanceDelete(session *types.Session, instance *types.Instance) error InstanceExec(instance *types.Instance, cmd []string) (int, error) + InstanceFSTree(instance *types.Instance) (io.Reader, error) + InstanceFile(instance *types.Instance, filePath string) (io.Reader, error) ClientNew(id string, session *types.Session) *types.Client ClientResizeViewPort(client *types.Client, cols, rows uint) diff --git a/pwd/session_test.go b/pwd/session_test.go index 1d68de6..ad8895b 100644 --- a/pwd/session_test.go +++ b/pwd/session_test.go @@ -31,9 +31,9 @@ func TestSessionNew(t *testing.T) { _g.On("NewId").Return("aaaabbbbcccc") _f.On("GetForSession", mock.AnythingOfType("*types.Session")).Return(_d, nil) - _d.On("CreateNetwork", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) - _d.On("GetDaemonHost").Return("localhost") - _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) + _d.On("NetworkCreate", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) + _d.On("DaemonHost").Return("localhost") + _d.On("NetworkConnect", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("SessionCount").Return(1, nil) _s.On("InstanceCount").Return(0, nil) @@ -93,9 +93,9 @@ func TestSessionSetup(t *testing.T) { _g.On("NewId").Return("aaaabbbbcccc") _f.On("GetForSession", "aaaabbbbcccc").Return(_d, nil) - _d.On("CreateNetwork", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) - _d.On("GetDaemonHost").Return("localhost") - _d.On("ConnectNetwork", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) + _d.On("NetworkCreate", "aaaabbbbcccc", dtypes.NetworkCreate{Attachable: true, Driver: "overlay"}).Return(nil) + _d.On("DaemonHost").Return("localhost") + _d.On("NetworkConnect", config.L2ContainerName, "aaaabbbbcccc", "").Return("10.0.0.1", nil) _s.On("SessionPut", mock.AnythingOfType("*types.Session")).Return(nil) _s.On("InstancePut", mock.AnythingOfType("*types.Instance")).Return(nil) _s.On("SessionCount").Return(1, nil) @@ -104,31 +104,31 @@ func TestSessionSetup(t *testing.T) { _s.On("InstanceFindBySessionId", "aaaabbbbcccc").Return([]*types.Instance{}, nil) _d.On("CreateContainer", docker.CreateContainerOpts{Image: "franela/dind", SessionId: "aaaabbbbcccc", ContainerName: "aaaabbbb_manager1", Hostname: "manager1", Privileged: true, HostFQDN: "localhost", Networks: []string{"aaaabbbbcccc"}}).Return(nil) - _d.On("GetContainerIPs", "aaaabbbb_manager1").Return(map[string]string{"aaaabbbbcccc": "10.0.0.2"}, nil) + _d.On("ContainerIPs", "aaaabbbb_manager1").Return(map[string]string{"aaaabbbbcccc": "10.0.0.2"}, nil) _f.On("GetForInstance", mock.AnythingOfType("*types.Instance")).Return(_d, nil) _d.On("SwarmInit").Return(&docker.SwarmTokens{Manager: "managerToken", Worker: "workerToken"}, nil) _e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_manager1", "10.0.0.2", "manager1", "ip10-0-0-2-aaaabbbbcccc"}).Return() _d.On("CreateContainer", docker.CreateContainerOpts{Image: "franela/dind", SessionId: "aaaabbbbcccc", ContainerName: "aaaabbbb_manager2", Hostname: "manager2", Privileged: true, HostFQDN: "localhost", Networks: []string{"aaaabbbbcccc"}}).Return(nil) - _d.On("GetContainerIPs", "aaaabbbb_manager2").Return(map[string]string{"aaaabbbbcccc": "10.0.0.3"}, nil) + _d.On("ContainerIPs", "aaaabbbb_manager2").Return(map[string]string{"aaaabbbbcccc": "10.0.0.3"}, nil) _f.On("GetForInstance", mock.AnythingOfType("*types.Instance")).Return(_d, nil) _d.On("SwarmJoin", "10.0.0.2:2377", "managerToken").Return(nil) _e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_manager2", "10.0.0.3", "manager2", "ip10-0-0-3-aaaabbbbcccc"}).Return() _d.On("CreateContainer", docker.CreateContainerOpts{Image: "franela/dind:overlay2-dev", SessionId: "aaaabbbbcccc", ContainerName: "aaaabbbb_manager3", Hostname: "manager3", Privileged: true, HostFQDN: "localhost", Networks: []string{"aaaabbbbcccc"}}).Return(nil) - _d.On("GetContainerIPs", "aaaabbbb_manager3").Return(map[string]string{"aaaabbbbcccc": "10.0.0.4"}, nil) + _d.On("ContainerIPs", "aaaabbbb_manager3").Return(map[string]string{"aaaabbbbcccc": "10.0.0.4"}, nil) _f.On("GetForInstance", mock.AnythingOfType("*types.Instance")).Return(_d, nil) _d.On("SwarmJoin", "10.0.0.2:2377", "managerToken").Return(nil) _e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_manager3", "10.0.0.4", "manager3", "ip10-0-0-4-aaaabbbbcccc"}).Return() _d.On("CreateContainer", docker.CreateContainerOpts{Image: "franela/dind", SessionId: "aaaabbbbcccc", ContainerName: "aaaabbbb_worker1", Hostname: "worker1", Privileged: true, HostFQDN: "localhost", Networks: []string{"aaaabbbbcccc"}}).Return(nil) - _d.On("GetContainerIPs", "aaaabbbb_worker1").Return(map[string]string{"aaaabbbbcccc": "10.0.0.5"}, nil) + _d.On("ContainerIPs", "aaaabbbb_worker1").Return(map[string]string{"aaaabbbbcccc": "10.0.0.5"}, nil) _f.On("GetForInstance", mock.AnythingOfType("*types.Instance")).Return(_d, nil) _d.On("SwarmJoin", "10.0.0.2:2377", "workerToken").Return(nil) _e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_worker1", "10.0.0.5", "worker1", "ip10-0-0-5-aaaabbbbcccc"}).Return() _d.On("CreateContainer", docker.CreateContainerOpts{Image: "franela/dind", SessionId: "aaaabbbbcccc", ContainerName: "aaaabbbb_other", Hostname: "other", Privileged: true, HostFQDN: "localhost", Networks: []string{"aaaabbbbcccc"}}).Return(nil) - _d.On("GetContainerIPs", "aaaabbbb_other").Return(map[string]string{"aaaabbbbcccc": "10.0.0.6"}, nil) + _d.On("ContainerIPs", "aaaabbbb_other").Return(map[string]string{"aaaabbbbcccc": "10.0.0.6"}, nil) _e.M.On("Emit", event.INSTANCE_NEW, "aaaabbbbcccc", []interface{}{"aaaabbbb_other", "10.0.0.6", "other", "ip10-0-0-6-aaaabbbbcccc"}).Return() var nilArgs []interface{} diff --git a/scheduler/task/check_swarm_ports_test.go b/scheduler/task/check_swarm_ports_test.go index ca871f2..53c3113 100644 --- a/scheduler/task/check_swarm_ports_test.go +++ b/scheduler/task/check_swarm_ports_test.go @@ -42,7 +42,7 @@ func TestCheckSwarmPorts_RunWhenManager(t *testing.T) { } f.On("GetForInstance", i).Return(d, nil) - d.On("GetDaemonInfo").Return(info, nil) + d.On("DaemonInfo").Return(info, nil) d.On("GetSwarmPorts").Return([]string{"aaaabbbb_node1", "aaaabbbb_node2"}, []uint16{8080, 9090}, nil) e.M.On("Emit", CheckSwarmPortsEvent, "aaaabbbbcccc", []interface{}{ClusterPorts{Manager: i.Name, Instances: []string{i.Name, "aaaabbbb_node2"}, Ports: []int{8080, 9090}}}).Return() diff --git a/scheduler/task/check_swarm_status.go b/scheduler/task/check_swarm_status.go index 7cd9396..b507108 100644 --- a/scheduler/task/check_swarm_status.go +++ b/scheduler/task/check_swarm_status.go @@ -49,7 +49,7 @@ func NewCheckSwarmStatus(e event.EventApi, f docker.FactoryApi) *checkSwarmStatu func getDockerSwarmStatus(ctx context.Context, client docker.DockerApi) (ClusterStatus, error) { status := ClusterStatus{} - info, err := client.GetDaemonInfo() + info, err := client.DaemonInfo() if err != nil { return status, err } diff --git a/scheduler/task/check_swarm_status_test.go b/scheduler/task/check_swarm_status_test.go index 5077a0e..0cb14d8 100644 --- a/scheduler/task/check_swarm_status_test.go +++ b/scheduler/task/check_swarm_status_test.go @@ -40,7 +40,7 @@ func TestCheckSwarmStatus_RunWhenInactive(t *testing.T) { } f.On("GetForInstance", i).Return(d, nil) - d.On("GetDaemonInfo").Return(infoInactive, nil) + d.On("DaemonInfo").Return(infoInactive, nil) e.M.On("Emit", CheckSwarmStatusEvent, "aaabbbccc", []interface{}{ClusterStatus{IsManager: false, IsWorker: false, Instance: "node1"}}).Return() task := NewCheckSwarmStatus(e, f) @@ -71,7 +71,7 @@ func TestCheckSwarmStatus_RunWhenLocked(t *testing.T) { } f.On("GetForInstance", i).Return(d, nil) - d.On("GetDaemonInfo").Return(infoLocked, nil) + d.On("DaemonInfo").Return(infoLocked, nil) e.M.On("Emit", CheckSwarmStatusEvent, "aaabbbccc", []interface{}{ClusterStatus{IsManager: false, IsWorker: false, Instance: "node1"}}).Return() task := NewCheckSwarmStatus(e, f) @@ -103,7 +103,7 @@ func TestCheckSwarmStatus_RunWhenManager(t *testing.T) { } f.On("GetForInstance", i).Return(d, nil) - d.On("GetDaemonInfo").Return(infoLocked, nil) + d.On("DaemonInfo").Return(infoLocked, nil) e.M.On("Emit", CheckSwarmStatusEvent, "aaabbbccc", []interface{}{ClusterStatus{IsManager: true, IsWorker: false, Instance: "node1"}}).Return() task := NewCheckSwarmStatus(e, f) @@ -135,7 +135,7 @@ func TestCheckSwarmStatus_RunWhenWorker(t *testing.T) { } f.On("GetForInstance", i).Return(d, nil) - d.On("GetDaemonInfo").Return(infoLocked, nil) + d.On("DaemonInfo").Return(infoLocked, nil) e.M.On("Emit", CheckSwarmStatusEvent, "aaabbbccc", []interface{}{ClusterStatus{IsManager: false, IsWorker: true, Instance: "node1"}}).Return() task := NewCheckSwarmStatus(e, f) diff --git a/scheduler/task/collect_stats.go b/scheduler/task/collect_stats.go index 5ed012a..0ad1cad 100644 --- a/scheduler/task/collect_stats.go +++ b/scheduler/task/collect_stats.go @@ -91,7 +91,7 @@ func (t *collectStats) Run(ctx context.Context, instance *types.Instance) error log.Println(err) return err } - reader, err := dockerClient.GetContainerStats(instance.Name) + reader, err := dockerClient.ContainerStats(instance.Name) if err != nil { log.Println("Error while trying to collect instance stats", err) return err diff --git a/scheduler/task/collect_stats_test.go b/scheduler/task/collect_stats_test.go index 17df434..121bcba 100644 --- a/scheduler/task/collect_stats_test.go +++ b/scheduler/task/collect_stats_test.go @@ -65,7 +65,7 @@ func TestCollectStats_Run(t *testing.T) { s.On("SessionGet", i.SessionId).Return(sess, nil) f.On("GetForSession", sess).Return(d, nil) - d.On("GetContainerStats", i.Name).Return(nopCloser{bytes.NewReader(b)}, nil) + d.On("ContainerStats", i.Name).Return(nopCloser{bytes.NewReader(b)}, nil) e.M.On("Emit", CollectStatsEvent, "aaaabbbbcccc", []interface{}{InstanceStats{Instance: i.Name, Mem: "0.00% (0B / 0B)", Cpu: "0.00%"}}).Return() task := NewCollectStats(e, f, s) diff --git a/www/assets/editor.css b/www/assets/editor.css new file mode 100644 index 0000000..063f536 --- /dev/null +++ b/www/assets/editor.css @@ -0,0 +1,15 @@ +.alert-top { + position: absolute; + top: 0; + right: 0; + width:100px; + display:none; + text-align: center; + padding: 3px; + height: 30px; + margin-bottom: 0px; +} + +.col-md-3 { + overflow-x: auto; +} diff --git a/www/editor.html b/www/editor.html new file mode 100644 index 0000000..9e825ae --- /dev/null +++ b/www/editor.html @@ -0,0 +1,171 @@ + + + + Editor + + + + + + + +
+ +
+
+
+ +
+
+
+
+ + + +
+
+
+
+
+ + + + + + + + +