Add support for file editor
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
2
dockerfiles/pwm/.gitconfig
Normal file
2
dockerfiles/pwm/.gitconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
[url "https://"]
|
||||
insteadOf = git://
|
||||
73
dockerfiles/pwm/.inputrc
Normal file
73
dockerfiles/pwm/.inputrc
Normal file
@@ -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
|
||||
5
dockerfiles/pwm/.profile
Normal file
5
dockerfiles/pwm/.profile
Normal file
@@ -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
|
||||
6
dockerfiles/pwm/.vimrc
Normal file
6
dockerfiles/pwm/.vimrc
Normal file
@@ -0,0 +1,6 @@
|
||||
syntax on
|
||||
set autoindent
|
||||
set expandtab
|
||||
set number
|
||||
set shiftwidth=2
|
||||
set softtabstop=2
|
||||
44
dockerfiles/pwm/Dockerfile
Normal file
44
dockerfiles/pwm/Dockerfile
Normal file
@@ -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
|
||||
7
dockerfiles/pwm/daemon.json
Normal file
7
dockerfiles/pwm/daemon.json
Normal file
@@ -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"]
|
||||
}
|
||||
8
dockerfiles/pwm/motd
Normal file
8
dockerfiles/pwm/motd
Normal file
@@ -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. #
|
||||
###############################################################
|
||||
5
dockerfiles/pwm/sudo
Executable file
5
dockerfiles/pwm/sudo
Executable file
@@ -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 $@
|
||||
@@ -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")
|
||||
})
|
||||
|
||||
|
||||
53
handlers/file_instance.go
Normal file
53
handlers/file_instance.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
42
handlers/fstree_instance.go
Normal file
42
handlers/fstree_instance.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
10
pwd/mock.go
10
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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
15
www/assets/editor.css
Normal file
15
www/assets/editor.css
Normal file
@@ -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;
|
||||
}
|
||||
171
www/editor.html
Normal file
171
www/editor.html
Normal file
@@ -0,0 +1,171 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>Editor</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/zTree.v3/3.5.29/css/metroStyle/metroStyle.min.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic-bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/assets/editor.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid ">
|
||||
<div id="alert-info" class="alert alert-info alert-top" role="alert">
|
||||
<span class="alert-msg"></span>
|
||||
</div>
|
||||
<div class="row" style="height: 100%">
|
||||
<div class="col-md-3">
|
||||
<div>
|
||||
<button type="button" id='treeReloadBtn' class="btn btn-sm">
|
||||
<span class="oi oi-reload" title="Refresh" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="fileTree" class="ztree"></div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<!-- Nav tabs -->
|
||||
<ul class="nav nav-tabs" id="tabs" role="tablist">
|
||||
</ul>
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.rawgit.com/zTree/zTree_v3/4f2717d4/js/jquery.ztree.core.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js"></script>
|
||||
<script src="https://cdn.rawgit.com/beatgammit/base64-js/be644dec/base64js.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
$(document).ready(function() {
|
||||
var alertShow = function(msg) {
|
||||
$div = $('.alert-info');
|
||||
$div.find('.alert-msg').text(msg);
|
||||
if ($div.css('display') === 'none') {
|
||||
// fadein, fadeout.
|
||||
$div.fadeIn(1000).delay(1000).fadeOut(1000);
|
||||
}
|
||||
};
|
||||
//regiters events for newly created tab elements
|
||||
var registerEvents = function(tabId) {
|
||||
//this method will register event on close icon on the tab..
|
||||
$(".closeTab").click(function () {
|
||||
//there are multiple elements which has .closeTab icon so close the tab whose close icon is clicked
|
||||
var tabContentId = $(this).parent().attr("href");
|
||||
$(this).parent().parent().remove(); //remove li of tab
|
||||
$('#tabs a:last').tab('show'); // Select first tab
|
||||
$(tabContentId).remove(); //remove respective tab content
|
||||
});
|
||||
|
||||
$('#fileReload_'+tabId+'Btn').click(function() {
|
||||
var path = $(this).attr('data-file-path');
|
||||
loadFile(path, tabId);
|
||||
});
|
||||
|
||||
$('#fileSave_'+tabId+'Btn').click(function() {
|
||||
var path = $(this).attr('data-file-path');
|
||||
saveFile(path, tabId);
|
||||
});
|
||||
};
|
||||
|
||||
var getFilePath = function(treeNode) {
|
||||
var parent = treeNode.getParentNode();
|
||||
var path = '';
|
||||
if (parent) {
|
||||
return getFilePath(parent) + '/' + treeNode.name;
|
||||
} else {
|
||||
return treeNode.name;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var treeClick = function(event, treeId, treeNode, clickFlag) {
|
||||
if (!treeNode.isParent) {
|
||||
var tabId = treeNode.tId;
|
||||
if ($('#tab_' + tabId + '').length == 0) {
|
||||
var filePath = getFilePath(treeNode);
|
||||
$('.nav-tabs').append('<li class="nav-item"><a class="nav-link" data-toggle="tab" role="tab" href="#tab_' + tabId + '"><button class="close closeTab" type="button" >×</button>'+treeNode.name+'</a></li>');
|
||||
$('.tab-content').append(' \
|
||||
<div class="tab-pane" id="tab_' + tabId + '"> \
|
||||
<div style="height: 40px; width: 100%; padding-top: 5px; background-color: #F0F0F0"> \
|
||||
<button type="button" id="fileSave_'+tabId+'Btn" data-file-path="'+filePath+'" class="btn btn-info btn-sm"> \
|
||||
<span class="oi oi-data-transfer-upload" title="Save" aria-hidden="true"></span> Save \
|
||||
</button> \
|
||||
<button type="button" id="fileReload_'+tabId+'Btn" data-file-path="'+filePath+'" class="btn btn-info btn-sm"> \
|
||||
<span class="oi oi-reload" title="Save" aria-hidden="true"></span> Reload \
|
||||
</button> \
|
||||
</div> \
|
||||
<div id="editor_'+ tabId +'" style="height: calc(100vh - 82px); width: 100%;"></div> \
|
||||
</div> \
|
||||
');
|
||||
loadFile(filePath, tabId);
|
||||
registerEvents(tabId);
|
||||
}
|
||||
$('#tabs a[href="#tab_'+ tabId +'"]').tab('show');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var loadFile = function(filePath, tabId) {
|
||||
var editor = ace.edit('editor_'+tabId);
|
||||
$.get('./file?path='+filePath)
|
||||
.done(function( fileBase64 ) {
|
||||
var bytes = base64js.toByteArray(fileBase64);
|
||||
editor.setValue((new TextDecoder("utf-8")).decode(bytes), -1);
|
||||
editor.focus();
|
||||
alertShow('file loaded')
|
||||
});
|
||||
}
|
||||
|
||||
var saveFile = function(filePath, tabId) {
|
||||
var editor = ace.edit('editor_'+tabId);
|
||||
var fileData = new Blob([editor.getValue()], { type: 'text/plain' });
|
||||
var data = new FormData();
|
||||
var fileName = filePath.substr(filePath.lastIndexOf('/'))
|
||||
data.append(fileName,fileData, fileName);
|
||||
$.ajax({
|
||||
url: 'uploads?path='+filePath.substr(0, filePath.lastIndexOf('/')),
|
||||
data: data,
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
method: 'POST',
|
||||
type: 'POST', // For jQuery < 1.9
|
||||
success: function(data) {
|
||||
alertShow('file saved')
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var setting = {
|
||||
data: {
|
||||
key: {
|
||||
children: "contents"
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
onClick: treeClick
|
||||
}
|
||||
};
|
||||
|
||||
var populateTree = function() {
|
||||
$.getJSON('./fstree')
|
||||
.done(function( treeData ) {
|
||||
treeData[0].open = true;
|
||||
$.fn.zTree.init($("#fileTree"), setting, treeData);
|
||||
});
|
||||
}
|
||||
|
||||
// Attach handlers to tree reload btn
|
||||
$('#treeReloadBtn').click(populateTree);
|
||||
|
||||
// populate tree whenever the page starts
|
||||
populateTree();
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user