class OSwitch

OSwitch leverages docker to provide access to complex Bioinformatics software (even Biolinux!) in just one command.

Images are built on the user's system on demand and executed in a container. Containers are removed after execution.

Volumes from host OS are mounted in the container just the same, including home directory. USER, HOME, SHELL, and PWD are preserved.

Constants

DOTDIR
Image

Captures a docker image's metadata.

PREFIX

Attributes

cntname[R]
command[R]
imgname[R]
package[R]

Public Class Methods

new(package, command = []) click to toggle source
# File lib/oswitch.rb, line 42
def initialize(package, command = [])
  @package = package
  @command = command
  @imgname = "oswitch_#{@package}"
  @cntname = "#{@package.gsub(%r{/|:}, '_')}-#{Process.pid}"
  exec
end
packages() click to toggle source
# File lib/oswitch.rb, line 29
def packages
  Dir["#{DOTDIR}/*"].map do |rep|
    pkgs = Dir["#{rep}/*"].select {|pkg| File.directory?(pkg)}
    pkgs = [rep] if pkgs.empty?
    pkgs
  end.
  flatten.
  map {|pkg|
    pkg.gsub("#{DOTDIR}/", '')
  }
end

Public Instance Methods

exec() click to toggle source
# File lib/oswitch.rb, line 52
  def exec
    ping and build and switch
  rescue ENODKR => e
    puts e
    exit
  rescue => e
    puts <<MSG

Ouch! Looks like you have hit a bug. Please could you post the error report
below to our issue tracker (https://github.com/wurmlab/oswitch/issues):

#{e}\n#{e.backtrace.join("\n")}

MSG
    exit
  end

Private Instance Methods

build() click to toggle source
# File lib/oswitch.rb, line 83
def build
  return true if Image.exists? imgname
  create_context_dir &&
    system("docker build -t #{imgname} #{context_dir}") || !remove_context_dir
end
context_dir() click to toggle source

Location of context dir.

# File lib/oswitch.rb, line 130
def context_dir
  File.join(DOTDIR, package)
end
create_context_dir() click to toggle source

Create context dir.

# File lib/oswitch.rb, line 100
def create_context_dir
  FileUtils.mkdir_p context_dir
  FileUtils.cp_r(template_files, context_dir)
  write_dockerfile
end
dockerfile_data() click to toggle source

Generate String that get written to Dockerfile.

# File lib/oswitch.rb, line 118
def dockerfile_data
  data = ["FROM #{package}"]
  data << 'COPY _switch /'
  data << 'COPY wheel /etc/sudoers.d/'
  data << "RUN /_switch #{userargs} 2>&1"
  data << 'ENV LC_ALL en_US.UTF-8'
  data << "USER #{username}"
  data << "ENTRYPOINT [\"#{shell}\", \"-c\"]"
  data.join("\n")
end
motd() click to toggle source
# File lib/oswitch.rb, line 144
  def motd
    str =<<MOTD
################################################################################
You are now running: #{package}, in container: #{cntname}.

Container is distinct from the shell your launched this container from. Changes
you make here will be lost unless it's made to one of the directories below:

 - #{mountpoints.join("\n - ")}

It's possible you may not be able to write to one or more directories above,
but it should be possible to read data from all. Home directory is often the
safest to write to.

Press Ctrl-D or type 'exit' to go back.
################################################################################
MOTD
    str.shellescape
  end
mountargs() click to toggle source
# File lib/oswitch.rb, line 164
def mountargs
  mountpoints.map do |mountpoint|
    "-v '#{mountpoint}':'#{mountpoint}'"
  end.join(' ')
end
ping() click to toggle source

Ping docker daemon. Raise error if no response within 10s.

# File lib/oswitch.rb, line 90
def ping
  pong = timeout 5, ENODKR do
    system 'docker info > /dev/null 2>&1'
  end
  pong or raise ENODKR
end
remove_context_dir() click to toggle source

Remove context dir.

# File lib/oswitch.rb, line 107
def remove_context_dir
  FileUtils.rm_r(context_dir)
end
switch() click to toggle source
# File lib/oswitch.rb, line 71
def switch
  cmdline = "docker run --name #{cntname} --hostname #{cntname} -it --rm" \
    " -w #{cwd} #{mountargs} #{imgname} "
  if command.empty?
    # Display motd and run interactive shell.
    cmdline << "\"echo #{motd}; #{shell} -i\""
  else
    cmdline << "\"#{command}\""
  end
  Kernel.exec cmdline
end
template_dir() click to toggle source

Location of template dir.

# File lib/oswitch.rb, line 135
def template_dir
  File.join(PREFIX, 'share', 'oswitch', 'context')
end
template_files() click to toggle source

Template files.

# File lib/oswitch.rb, line 140
def template_files
  Dir[File.join(template_dir, '*')]
end
userargs() click to toggle source
# File lib/oswitch.rb, line 170
def userargs
  [uid, gid, username, home, shell].join(' ')
end
write_dockerfile() click to toggle source

Write Dockerfile.

# File lib/oswitch.rb, line 112
def write_dockerfile
  dockerfile = File.join(context_dir, 'Dockerfile')
  File.write(dockerfile, dockerfile_data)
end