Skip to content

Commit

Permalink
avoid touching user home while restoring backup
Browse files Browse the repository at this point in the history
- move configuration and log files internal to JuliaBox out of user home directory
    - `/home/juser/.juliabox` is now relocated to `/opt/juliabox`
    - `/opt/juliabox` is mounted as a separate volume managed by JuliaBox
    - new volume plugin for configuration files handles the mount point
- do not update `.bashrc` anymore
    - this was required for Julia v0.3 package precompilation, which is now excluded.

The following files are still created in user home, only for a new user (or when they are missing):
- file link to Julia tutorials
- git config
- ssh keys

With this, JuliaBox will not need to touch the user home after restoring the backup unless there's a real need.

ref JuliaCloud#390
  • Loading branch information
tanmaykm committed Jul 16, 2016
1 parent 8847169 commit 441553b
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 120 deletions.
3 changes: 2 additions & 1 deletion container/interactive/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ RUN echo 'push!(LOAD_PATH, "/opt/julia_packages/.julia/v0.3/"); try using JuliaB

# Data volumes shall be mounted at /mnt/data
RUN mkdir /mnt/data
RUN mkdir /opt/juliabox

RUN echo "ulimit -u 2048 -n 256" >> /etc/bash.bashrc

Expand All @@ -59,4 +60,4 @@ WORKDIR /home/juser
# 8050-8052: user specified applications
EXPOSE 4200 8000 8998 8050 8051 8052

ENTRYPOINT ["/usr/bin/supervisord", "-n", "-c", "/home/juser/.juliabox/supervisord.conf", "-l", "/home/juser/.juliabox/supervisord.log", "-j", "/home/juser/.juliabox/supervisord.pid", "-q", "/home/juser/.juliabox"]
ENTRYPOINT ["/usr/bin/supervisord", "-n", "-c", "/opt/juliabox/.juliabox/supervisord.conf", "-l", "/opt/juliabox/.juliabox/supervisord.log", "-j", "/opt/juliabox/.juliabox/supervisord.pid", "-q", "/opt/juliabox/.juliabox"]
18 changes: 10 additions & 8 deletions container/interactive/IJulia/supervisord.conf
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,32 @@ logfile_backups = 2

[program:shellinabox]
command=shellinaboxd -t -s /:juser:juser:/home/juser:/bin/bash --user-css Light:-/usr/local/share/doc/shellinabox/black-on-white.css,Dark:+/usr/local/share/doc/shellinabox/white-on-black.css
stdout_logfile = /home/juser/.juliabox/shellinabox.log
stdout_logfile = /opt/juliabox/.juliabox/shellinabox.log
stdout_logfile_backups = 2
stdout_logfile_maxbytes = 1MB
stderr_logfile = /home/juser/.juliabox/shellinabox_err.log
stderr_logfile = /opt/juliabox/.juliabox/shellinabox_err.log
stderr_logfile_backups = 2
stderr_logfile_maxbytes = 1MB
environment = CMDSTAN_HOME="/usr/share/cmdstan"
environment = IPYTHONDIR="/opt/juliabox/.juliabox/.ipython"

[program:ijulia]
command=ipython notebook
stdout_logfile = /home/juser/.juliabox/ijulia.log
stdout_logfile = /opt/juliabox/.juliabox/ijulia.log
stdout_logfile_backups = 2
stdout_logfile_maxbytes = 1MB
stderr_logfile = /home/juser/.juliabox/ijulia_err.log
stderr_logfile = /opt/juliabox/.juliabox/ijulia_err.log
stderr_logfile_backups = 2
stderr_logfile_maxbytes = 1MB
environment = CMDSTAN_HOME="/usr/share/cmdstan"
environment = IPYTHONDIR="/opt/juliabox/.ipython"

[program:tornado]
command=/home/juser/.juliabox/tornado/src/fmanage.py
directory=/home/juser/.juliabox/tornado
stdout_logfile = /home/juser/.juliabox/tornado/tornado.log
command=/opt/juliabox/.juliabox/tornado/src/fmanage.py
directory=/opt/juliabox/.juliabox/tornado
stdout_logfile = /opt/juliabox/.juliabox/tornado/tornado.log
stdout_logfile_backups = 2
stdout_logfile_maxbytes = 1MB
stderr_logfile = /home/juser/.juliabox/tornado/tornado_err.log
stderr_logfile = /opt/juliabox/.juliabox/tornado/tornado_err.log
stderr_logfile_backups = 2
stderr_logfile_maxbytes = 1MB
35 changes: 18 additions & 17 deletions container/interactive/mk_user_home.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ then
fi

DATA_LOC=$1
JUSER_HOME=/tmp/juser
CFG_DIR=/tmp/juliabox
CFG_MOUNT=/opt/juliabox
PKG_DIR=/tmp/jpkg
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
#SUDO_JUSER="sudo -u#1000 -g#1000"
Expand All @@ -19,40 +20,40 @@ function error_exit {
exit 1
}

sudo rm -rf ${JUSER_HOME}
sudo rm -rf ${CFG_DIR}
sudo rm -rf ${PKG_DIR}
mkdir -p ${JUSER_HOME}
mkdir -p ${CFG_DIR}
mkdir -p ${PKG_DIR}
mkdir -p ${JUSER_HOME}/.juliabox
mkdir -p ${CFG_DIR}/.juliabox

cp ${DIR}/setup_julia.sh ${JUSER_HOME}
cp ${DIR}/setup_julia.sh ${CFG_DIR}

sudo chown -R ${IDS} ${JUSER_HOME}
sudo chown -R ${IDS} ${CFG_DIR}
sudo chown -R ${IDS} ${PKG_DIR}
docker run -i -v ${JUSER_HOME}:/home/juser -v ${PKG_DIR}:/opt/julia_packages -e "JULIA_PKGDIR=/opt/julia_packages/.julia" --entrypoint="/home/juser/setup_julia.sh" juliabox/juliabox:latest || error_exit "Could not run juliabox image"
docker run -i -v ${CFG_DIR}:${CFG_MOUNT} -v ${PKG_DIR}:/opt/julia_packages -e "JULIA_PKGDIR=/opt/julia_packages/.julia" -e "IPYTHONDIR=${CFG_MOUNT}/.ipython" --entrypoint="${CFG_MOUNT}/setup_julia.sh" juliabox/juliabox:latest || error_exit "Could not run juliabox image"

sudo chown -R ${IDS} ${JUSER_HOME}
sudo chown -R ${IDS} ${CFG_DIR}
sudo chown -R ${IDS} ${PKG_DIR}
${SUDO_JUSER} rm ${JUSER_HOME}/setup_julia.sh
${SUDO_JUSER} rm ${CFG_DIR}/setup_julia.sh

${SUDO_JUSER} cp ${DIR}/IJulia/ipython_notebook_config.py ${JUSER_HOME}/.ipython/profile_default/ipython_notebook_config.py
${SUDO_JUSER} cp ${DIR}/IJulia/custom.css ${JUSER_HOME}/.ipython/profile_default/static/custom/custom.css
${SUDO_JUSER} cp ${DIR}/IJulia/custom.js ${JUSER_HOME}/.ipython/profile_default/static/custom/custom.js
${SUDO_JUSER} cp ${DIR}/IJulia/ipython_notebook_config.py ${CFG_DIR}/.ipython/profile_default/ipython_notebook_config.py
${SUDO_JUSER} cp ${DIR}/IJulia/custom.css ${CFG_DIR}/.ipython/profile_default/static/custom/custom.css
${SUDO_JUSER} cp ${DIR}/IJulia/custom.js ${CFG_DIR}/.ipython/profile_default/static/custom/custom.js

# install RISE (slideshow plugin)
#${SUDO_JUSER} cd /home/juser && git clone https://github.com/damianavila/RISE.git && cd RISE && git checkout -b 3.x 3.x && python setup.py install && cd .. && rm -rf RISE

${SUDO_JUSER} cp -R ${DIR}/IJulia/tornado ${JUSER_HOME}/.juliabox/tornado
${SUDO_JUSER} cp ${DIR}/IJulia/supervisord.conf ${JUSER_HOME}/.juliabox/supervisord.conf
${SUDO_JUSER} cp -R ${DIR}/IJulia/tutorial ${JUSER_HOME}/.juliabox/tutorial
${SUDO_JUSER} cp -R ${DIR}/IJulia/tornado ${CFG_DIR}/.juliabox/tornado
${SUDO_JUSER} cp ${DIR}/IJulia/supervisord.conf ${CFG_DIR}/.juliabox/supervisord.conf
${SUDO_JUSER} cp -R ${DIR}/IJulia/tutorial ${CFG_DIR}/.juliabox/tutorial

sudo rm ${DATA_LOC}/julia_packages.tar.gz
sudo tar -czf ${DATA_LOC}/julia_packages.tar.gz -C ${PKG_DIR} .

sudo rm ${DATA_LOC}/user_home.tar.gz
sudo tar -czf ${DATA_LOC}/user_home.tar.gz -C ${JUSER_HOME} .
sudo tar -czf ${DATA_LOC}/user_home.tar.gz -C ${CFG_DIR} .

sudo rm -rf ${JUSER_HOME}
sudo rm -rf ${CFG_DIR}
sudo rm -rf ${PKG_DIR}
for id in `docker ps -a | grep Exited | cut -d" " -f1 | grep -v CONTAINER`
do
Expand Down
2 changes: 2 additions & 0 deletions docs/Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ Provides storage volumes that are mounted on to containers. All volume providers
User home folders are overlaid with some configuration files required for working of JuliaBox.
- `JBoxVol.JBP_DATA`, `JBoxVol.JBP_DATA_EBS`:
Provide larger volumes for storing data.
- `JBoxVol.JBP_CONFIG`:
Provide read-write volumes that contain JuliaBox configuration and log files and are mounted on to containers.
- `JBoxVol.JBP_PKGBUNDLE`:
Provide read-only volumes that contain Julia packages and can be mounted on to containers.

Expand Down
2 changes: 2 additions & 0 deletions engine/conf/tornado.conf.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Welcome to JuliaBox. We hope you will like it and also share with your friends.
"env_type" : "prod",
"backup_location" : "/jboxengine/data/backups",
"pkg_location": "/jboxengine/data/packages",
"cfg_location": "/jboxengine/data/configs",
"mnt_location" : "/jboxengine/data/disks/loop/mnt",
"user_home_image" : "/jboxengine/data/user_home.tar.gz",
"pkg_image": "/jboxengine/data/julia_packages.tar.gz",
Expand All @@ -127,6 +128,7 @@ Welcome to JuliaBox. We hope you will like it and also share with your friends.
"juliabox.plugins.vol_loopback",
"juliabox.plugins.vol_ebs",
"juliabox.plugins.vol_defpkg",
"juliabox.plugins.vol_defcfg",
"juliabox.plugins.course_homework",
"juliabox.plugins.parallel",
"juliabox.plugins.auth_google",
Expand Down
11 changes: 8 additions & 3 deletions engine/src/juliabox/interactive/sess_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class SessContainer(BaseContainer):
PORTS_INTERNAL = [4200, 8000, 8998]
PORTS_USER = range(8050, 8053)
PORTS = PORTS_INTERNAL + PORTS_USER
VOLUMES = ['/home/juser', JBoxVol.PKG_MOUNT_POINT]
VOLUMES = ['/home/juser', JBoxVol.CONFIG_MOUNT_POINT, JBoxVol.PKG_MOUNT_POINT]
MAX_CONTAINERS = 0
VALID_CONTAINERS = {}
INITIAL_DISK_USED_PCT = None
Expand Down Expand Up @@ -62,15 +62,20 @@ def configure():
@staticmethod
def _create_new(name, email):
home_disk = VolMgr.get_disk_for_user(email)
cfg_disk = VolMgr.get_cfg_mount_for_user(email)
pkgs_disk = VolMgr.get_pkg_mount_for_user(email)

vols = {
home_disk.disk_path: {
'bind': SessContainer.VOLUMES[0],
'ro': False
},
pkgs_disk.disk_path: {
cfg_disk.disk_path: {
'bind': SessContainer.VOLUMES[1],
'ro': False
},
pkgs_disk.disk_path: {
'bind': SessContainer.VOLUMES[2],
'ro': True
}
}
Expand Down Expand Up @@ -279,7 +284,7 @@ def on_kill(self):
self.on_stop()

def before_delete(self, cname, backup):
for disktype in (JBoxVol.JBP_USERHOME, JBoxVol.JBP_PKGBUNDLE, JBoxVol.JBP_DATA):
for disktype in (JBoxVol.JBP_USERHOME, JBoxVol.JBP_PKGBUNDLE, JBoxVol.JBP_DATA, JBoxVol.JBP_CONFIG):
disk = VolMgr.get_disk_from_container(self.dockid, disktype)
if disk is not None:
disk.release(backup=backup)
Expand Down
3 changes: 3 additions & 0 deletions engine/src/juliabox/plugins/vol_defcfg/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from defcfg import JBoxDefaultConfigVol

__author__ = 'tan'
86 changes: 86 additions & 0 deletions engine/src/juliabox/plugins/vol_defcfg/defcfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import os

from juliabox.jbox_util import ensure_delete, make_sure_path_exists, unique_sessname, JBoxCfg
from juliabox.vol import JBoxVol


class JBoxDefaultConfigVol(JBoxVol):
provides = [JBoxVol.JBP_CONFIG]

FS_LOC = None

@staticmethod
def configure():
cfg_location = os.path.expanduser(JBoxCfg.get('cfg_location'))
make_sure_path_exists(cfg_location)
JBoxDefaultConfigVol.FS_LOC = cfg_location

@staticmethod
def _get_config_mounts_used(cid):
used = []
props = JBoxDefaultConfigVol.dckr().inspect_container(cid)
try:
for _cpath, hpath in JBoxVol.extract_mounts(props):
if hpath.startswith(JBoxDefaultConfigVol.FS_LOC):
used.append(hpath.split('/')[-1])
except:
JBoxDefaultConfigVol.log_error("error finding config mount points used in " + cid)
return []
return used

@staticmethod
def refresh_disk_use_status(container_id_list=None):
pass

@staticmethod
def get_disk_for_user(user_email):
JBoxDefaultConfigVol.log_debug("creating configs disk for %s", user_email)
if JBoxDefaultConfigVol.FS_LOC is None:
JBoxDefaultConfigVol.configure()

disk_path = os.path.join(JBoxDefaultConfigVol.FS_LOC, unique_sessname(user_email))
cfgvol = JBoxDefaultConfigVol(disk_path, user_email=user_email)
cfgvol._unpack_config()
return cfgvol

@staticmethod
def is_mount_path(fs_path):
return fs_path.startswith(JBoxDefaultConfigVol.FS_LOC)

@staticmethod
def get_disk_from_container(cid):
mounts_used = JBoxDefaultConfigVol._get_config_mounts_used(cid)
if len(mounts_used) == 0:
return None

mount_used = mounts_used[0]
disk_path = os.path.join(JBoxDefaultConfigVol.FS_LOC, str(mount_used))
container_name = JBoxVol.get_cname(cid)
sessname = container_name[1:]
return JBoxDefaultConfigVol(disk_path, sessname=sessname)

@staticmethod
def refresh_user_home_image():
pass

def release(self, backup=False):
ensure_delete(self.disk_path, include_itself=True)

@staticmethod
def disk_ids_used_pct():
return 0

def _unpack_config(self):
if os.path.exists(self.disk_path):
JBoxDefaultConfigVol.log_debug("Config folder exists %s. Deleting...", self.disk_path)
ensure_delete(self.disk_path, include_itself=True)
JBoxDefaultConfigVol.log_debug("Config folder deleted %s", self.disk_path)

JBoxDefaultConfigVol.log_debug("Will unpack config to %s", self.disk_path)
os.mkdir(self.disk_path)
JBoxDefaultConfigVol.log_debug("Created config folder %s", self.disk_path)

self.restore_user_home(True)
JBoxDefaultConfigVol.log_debug("Restored config files to %s", self.disk_path)
self.setup_instance_config()
JBoxDefaultConfigVol.log_debug("Setup instance config at %s", self.disk_path)
2 changes: 1 addition & 1 deletion engine/src/juliabox/plugins/vol_ebs/ebs.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def get_disk_for_user(user_email):
try:
existing_disk = JBoxDiskState(cluster_id=CompEC2.INSTALL_ID, region_id=CompEC2.REGION,
user_id=user_email)
except Exception, ex:
except Exception as ex:
JBoxEBSVol.log_debug("No existing disk for %s. Exception %r", user_email, ex)
existing_disk = None

Expand Down
15 changes: 3 additions & 12 deletions engine/src/juliabox/plugins/vol_hostdisk/hostdisk.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def get_disk_for_user(user_email):
if not os.path.exists(disk_path):
os.mkdir(disk_path)
hostvol = JBoxHostDiskVol(disk_path, user_email=user_email)
hostvol.refresh_disk(mark_refreshed=False)
hostvol.refresh_disk()

if JBoxVol.BACKUP_LOC is not None:
JBoxHostDiskVol.log_debug("restoring data for %s", user_email)
Expand Down Expand Up @@ -87,19 +87,10 @@ def _backup(self, clear_volume=True):
if JBoxVol.BACKUP_LOC is not None:
super(JBoxHostDiskVol, self)._backup(clear_volume=clear_volume)

def refresh_disk(self, mark_refreshed=True):
if JBoxVol.BACKUP_LOC is None:
self.log_debug("restoring common data on disk at %s", self.disk_path)
self.restore_user_home(False)
else:
def refresh_disk(self):
if JBoxVol.BACKUP_LOC is not None:
self.log_debug("blanking out disk at %s", self.disk_path)
ensure_delete(self.disk_path)
self.log_debug("restoring common data on disk at %s", self.disk_path)
self.restore_user_home(True)

self.setup_instance_config()
if mark_refreshed:
self.mark_refreshed()
self.log_debug("refreshed disk at %s", self.disk_path)

def release(self, backup=False):
Expand Down
27 changes: 3 additions & 24 deletions engine/src/juliabox/plugins/vol_loopback/loopback.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,7 @@ def get_disk_for_user(user_email):
raise Exception("No free disk available")
disk_path = os.path.join(JBoxLoopbackVol.FS_LOC, str(disk_id))
loopvol = JBoxLoopbackVol(disk_path, user_email=user_email)

if not loopvol.is_refreshed():
loopvol.refresh_disk(mark_refreshed=False)
else:
JBoxLoopbackVol.log_debug("disk already refreshed for %s", user_email)
loopvol.unmark_refreshed()

loopvol.refresh_disk()
JBoxLoopbackVol.log_debug("restoring data for %s", user_email)
loopvol.restore()
return loopvol
Expand All @@ -149,29 +143,14 @@ def get_disk_from_container(cid):

@staticmethod
def refresh_user_home_image():
disk_id = 0
while disk_id < JBoxLoopbackVol.MAX_DISKS:
disk_id = JBoxLoopbackVol._reserve_disk_id(begin_idx=disk_id)
if disk_id < 0:
break

disk_path = os.path.join(JBoxLoopbackVol.FS_LOC, str(disk_id))
loopvol = JBoxLoopbackVol(disk_path)
loopvol.refresh_disk()
JBoxLoopbackVol._unreserve_disk_id(disk_id)
disk_id += 1
pass

def _backup(self, clear_volume=True):
super(JBoxLoopbackVol, self)._backup(clear_volume=clear_volume)

def refresh_disk(self, mark_refreshed=True):
def refresh_disk(self):
self.log_debug("blanking out disk at %s", self.disk_path)
ensure_delete(self.disk_path)
self.log_debug("restoring common data on disk at %s", self.disk_path)
self.restore_user_home(True)
self.setup_instance_config()
if mark_refreshed:
self.mark_refreshed()
self.log_debug("refreshed disk at %s", self.disk_path)

def release(self, backup=False):
Expand Down
Loading

0 comments on commit 441553b

Please sign in to comment.