oswitch 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/README.mkd +295 -0
- data/bin/oswitch +45 -0
- data/lib/oswitch.rb +293 -0
- data/oswitch.gemspec +27 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f17f0c2bd70cf5a969f60d6a000ebacaba1940dc
|
4
|
+
data.tar.gz: 4fc93cdb2580315e7a49032c2ff32797566144a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 08362f1f00b5d02531c2df28c59ccb9107f13e9ffcce5efef32cf2378e922f83c8db512dd098af6c081237b6db7920bf64c1d258c0f916762de5bd5a3eb0e883
|
7
|
+
data.tar.gz: 73b363b026815f51c84b110dd59a857a168fc9c8e9aaee6b22b14cb96341b4d83c7eba06b02a92d9fd18ef2cbb4d4f2fd91307cf1ba52f27aa4b6909da0f8e3e
|
data/Gemfile
ADDED
data/README.mkd
ADDED
@@ -0,0 +1,295 @@
|
|
1
|
+
# OSwitch
|
2
|
+
|
3
|
+
One-line access to other operating systems.
|
4
|
+
|
5
|
+
* [Usage](#example-usage)
|
6
|
+
* [Installation](#installation)
|
7
|
+
* [FAQ](#faq)
|
8
|
+
* [Roadmap](#roadmap)
|
9
|
+
* [Contributors & Funding](#contributors-&-funding)
|
10
|
+
|
11
|
+
## Background
|
12
|
+
|
13
|
+
Genomic analyses require using many bioinformatics tools starting from assessing
|
14
|
+
the quality of sequenced data and assembly, to annotation, comparison and
|
15
|
+
analysis. The data types are young, thus the tools are too and many
|
16
|
+
genomicists lack computational training. Thus tools are frequently
|
17
|
+
updated yet often challenging to install. Furthermore, software updates often
|
18
|
+
involve changes in algorithms or input/output formats, making analyses
|
19
|
+
difficult to reproduce. To make matters worse, genomicists often lack
|
20
|
+
the skills necessary to setup complex bioinformatics software, and
|
21
|
+
systems administrators can be overwhelmed by large numbers of software
|
22
|
+
installation requests & the challenges of managing multiple versions.
|
23
|
+
|
24
|
+
## Aim & Features
|
25
|
+
|
26
|
+
We are developing `oswitch` to enable **seamless switching
|
27
|
+
from one operating system to another** - providing access to diverse
|
28
|
+
ranges of tools. This project grew from our own need to rapidly access
|
29
|
+
software distributed as part of
|
30
|
+
[BioLinux](http://environmentalomics.org/bio-linux/) on our MacBooks
|
31
|
+
and our university HPC system.
|
32
|
+
|
33
|
+
For this we take advantage of the [docker](http://docker.io/)
|
34
|
+
technology. Docker works by creating "to the specification" image from
|
35
|
+
a Dockerfile, which is then run in an isolated
|
36
|
+
"container". Dockerfiles or the resulting images can persist forever,
|
37
|
+
are easily [shared or published](https://hub.docker.com/), and make
|
38
|
+
it possible for anybody to recreate the exact same setup at
|
39
|
+
any point of time in the future. This is similar to using virtual
|
40
|
+
machine images - but much more
|
41
|
+
[flexible and light-weight](http://stackoverflow.com/questions/16047306/how-is-docker-io-different-from-a-normal-virtual-machine).
|
42
|
+
|
43
|
+
`oswitch` is thus a wrapper facilitating access to docker images (without the
|
44
|
+
need for ssh-ing). Importantly, when switching operating systems inside a
|
45
|
+
shell, most things remain unchanged:
|
46
|
+
|
47
|
+
* Current working directory is maintained
|
48
|
+
* User name, uid and gid are maintained
|
49
|
+
* Login shell (bash/zsh/fish) is maintained
|
50
|
+
* Home directory is maintained (thus all .dotfiles and config files
|
51
|
+
are maintained).
|
52
|
+
* read/write permissions are maintained
|
53
|
+
* Paths are maintained whenever possible. Thus volumes (external drives,
|
54
|
+
NAS) mounted on the host are available in the container at the same
|
55
|
+
path.
|
56
|
+
|
57
|
+
## Example Usage
|
58
|
+
|
59
|
+
There are two broad usage scenarios: interactive use & non-interactive
|
60
|
+
use.
|
61
|
+
|
62
|
+
##### Use a package interactively in a normal command-line
|
63
|
+
|
64
|
+
Minimalist example:
|
65
|
+
|
66
|
+
```shell
|
67
|
+
Yannick@n56-169 ~/g/oswitch> uname -a
|
68
|
+
Darwin n56-169.sbcs.qmul.ac.uk 14.0.0 Darwin Kernel Version 14.0.0: Fri Sep 19 00:26:44 PDT 2014; root:xnu-2782.1.97~2/RELEASE_X86_64 x86_64
|
69
|
+
Yannick@n56-169 ~/g/oswitch> oswitch yeban/biolinux:8
|
70
|
+
### You are now running: biolinux_8, in container: biolinux_8-27182. ###
|
71
|
+
Yannick@biolinux_8-27182 ~/g/oswitch> uname -a
|
72
|
+
Linux biolinux_8-27182 3.16.4-tinycore64 #1 SMP Thu Oct 23 16:14:24 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
|
73
|
+
```
|
74
|
+
|
75
|
+
Biologically relevant example:
|
76
|
+
|
77
|
+
```shell
|
78
|
+
# Trying to run blast.
|
79
|
+
pixel:~/test/ $ ls
|
80
|
+
mygene.fasta
|
81
|
+
pixel:~/test/ $ cat mygene.fa
|
82
|
+
>myfavoritegene isthisone
|
83
|
+
MNTLWLSLWDYPGKLPLNFMVFDTKDDLQAAYWRDPYSIPLAVIFEDPQPISQRLIYEIR
|
84
|
+
TNPSYTLPPPPTKLYSAPISCRKNKTGHWMDDILSIKTGESCPVNNYLHSGFLALQMITD
|
85
|
+
ITKIKLENSDVTIPDIKLIMFPKEPYTADWMLAFRVVIPLYMVLALSQFITYLLILIVGE
|
86
|
+
KENKIKEGMKMMGLNDSVF
|
87
|
+
pixel:~/test/ $ blastp -query mygene.fa -remote -db nr -outfmt 7 > mygene_blastp_nr.tab
|
88
|
+
zsh: command not found: blastp
|
89
|
+
# Indeed... blastp is not installed on my MacBook.
|
90
|
+
|
91
|
+
# Switch to BioLinux and run blastp.
|
92
|
+
pixel:~/test/ $ oswitch biolinux
|
93
|
+
###### You are now running: biolinux in container biolinux-7187. ######
|
94
|
+
biolinux-7187:~/test/ $ blastp -query mygene.fa -remote -db nr -outfmt 7 > mygene_blastp_nr.tab
|
95
|
+
# BioLinux includes blastp, thus the command ran smoothly.
|
96
|
+
|
97
|
+
# View the result.
|
98
|
+
biolinux-7187:~/test/ $ head mygene_blastp_nr.tab
|
99
|
+
# BLASTP 2.2.28+
|
100
|
+
# Query: myfavoritegene isthisone
|
101
|
+
# RID: BJAHAHU9015
|
102
|
+
# Database: nr
|
103
|
+
# Fields: query id, subject id, % identity, alignment length, mismatches, gap opens, q. start, q. end, s. start, s. end, evalue, bit score
|
104
|
+
# 501 hits found
|
105
|
+
myfavoritegene gi|322796550|gb|EFZ19024.1| 100.00 199 0 0 1 199 1 199 2e-142 407
|
106
|
+
myfavoritegene gi|307183032|gb|EFN69988.1| 86.07 201 25 2 1 199 80 279 6e-115 361
|
107
|
+
myfavoritegene gi|572260155|ref|XP_006608402.1| 80.60 201 36 2 1 199 95 294 4e-108 350
|
108
|
+
myfavoritegene gi|328778864|ref|XP_397465.4| 80.60 201 36 2 1 199 95 294 5e-108 350
|
109
|
+
|
110
|
+
|
111
|
+
# [... potentially run other analyses that require biolinux things...]
|
112
|
+
|
113
|
+
# Return to normal operating system
|
114
|
+
biolinux-7187:~/test/ $ exit
|
115
|
+
pixel:~/test/ $ ls
|
116
|
+
mygene.fasta mygene_blastp_nr.txt
|
117
|
+
```
|
118
|
+
|
119
|
+
##### Use a package non-interactively
|
120
|
+
|
121
|
+
Alternatively, single commands can be run directly in a container
|
122
|
+
(e.g. BioLinux) without entering it interactively. This can
|
123
|
+
be useful to test new tools, or to run a single piece of
|
124
|
+
not-locally-installed software as part of a single command. The
|
125
|
+
container terminates automatically once the command has been
|
126
|
+
executed, output is printed to the terminal and can be redirected, and
|
127
|
+
the exit status of the command run within container is returned.
|
128
|
+
|
129
|
+
```shell
|
130
|
+
# Run command directly in BioLinux and view results if success.
|
131
|
+
pixel:~/test/ $ oswitch biolinux blastp -remote -query mygene.fa -db nr > mygene_blastp_nr.txt
|
132
|
+
```
|
133
|
+
|
134
|
+
##### Listing available operating system containers
|
135
|
+
|
136
|
+
OSwitch can pull any image from docker hub. You can see the images you pulled
|
137
|
+
from docker hub using oswitch as:
|
138
|
+
|
139
|
+
```shell
|
140
|
+
pixel:~ $ oswitch -l
|
141
|
+
yeban/biolinux:8
|
142
|
+
ubuntu:14.04
|
143
|
+
```
|
144
|
+
|
145
|
+
##### Availability
|
146
|
+
|
147
|
+
OSwitch has been tested on:
|
148
|
+
|
149
|
+
* Mac OS X Yosemite
|
150
|
+
* Ubuntu 14.04.1
|
151
|
+
* CentOS 7
|
152
|
+
|
153
|
+
##### Caveats
|
154
|
+
|
155
|
+
* Works only for Debian, Ubuntu, CentOS based docker images.
|
156
|
+
* Host directories/volumes with paths conflicting with container paths are
|
157
|
+
skipped.
|
158
|
+
* SELinux must be disabled on CentOS for mounting volumes to work.
|
159
|
+
|
160
|
+
## Installation
|
161
|
+
|
162
|
+
OSwitch first requires a working docker install.
|
163
|
+
|
164
|
+
#### Install and setup docker
|
165
|
+
|
166
|
+
##### Mac OS X
|
167
|
+
|
168
|
+
Installing docker - https://docs.docker.com/installation/mac/
|
169
|
+
|
170
|
+
##### Ubuntu
|
171
|
+
|
172
|
+
Installing docker - https://docs.docker.com/installation/ubuntulinux/
|
173
|
+
|
174
|
+
Add yourself to docker group so you can run docker client without sudo:
|
175
|
+
|
176
|
+
```shell
|
177
|
+
$ sudo usermod -aG docker `whoami`
|
178
|
+
|
179
|
+
# then logout and login again for the above command to take effect
|
180
|
+
```
|
181
|
+
|
182
|
+
##### CentOS
|
183
|
+
|
184
|
+
Installing docker - https://docs.docker.com/installation/centos/
|
185
|
+
|
186
|
+
Add yourself to docker group so you can run docker client without sudo:
|
187
|
+
|
188
|
+
```shell
|
189
|
+
$ sudo usermod -aG docker `whoami`
|
190
|
+
|
191
|
+
# then logout and login again for the above command to take effect
|
192
|
+
```
|
193
|
+
|
194
|
+
Disable SELinux as it gets in the way of mounting volumes within the container:
|
195
|
+
|
196
|
+
```shell
|
197
|
+
$ sed -i .bak 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
|
198
|
+
|
199
|
+
# then reboot your system
|
200
|
+
```
|
201
|
+
|
202
|
+
The above command backs up the original file to `/etc/selinux/config.bak`. If
|
203
|
+
you are concerned about disabling SELinux, do note that we are trying to work
|
204
|
+
out a better solution.
|
205
|
+
|
206
|
+
#### Test that docker is correctly installed
|
207
|
+
|
208
|
+
The following should give an encouraging message:
|
209
|
+
|
210
|
+
$ docker run hello-world
|
211
|
+
|
212
|
+
#### Install oswitch
|
213
|
+
|
214
|
+
Requirements: Ruby 2.0 or higher.
|
215
|
+
|
216
|
+
$ gem install oswitch
|
217
|
+
|
218
|
+
#### Testing oswitch
|
219
|
+
|
220
|
+
$ oswitch ubuntu:14.04
|
221
|
+
|
222
|
+
## FAQ
|
223
|
+
|
224
|
+
##### Q. Directories mounted within container on Mac host are empty.
|
225
|
+
The problem is, on Mac `boot2docker` is the _real_ host, not OS X. `oswitch`
|
226
|
+
can mount only what's available to it from `boot2docker`. For example,
|
227
|
+
`/Applications`.
|
228
|
+
|
229
|
+
Run `boot2docker ssh ls /Applications` and you will find it empty as well.
|
230
|
+
|
231
|
+
The workaround is to correctly mount the directories you want in `boot2docker`
|
232
|
+
first.
|
233
|
+
|
234
|
+
```
|
235
|
+
boot2docker down
|
236
|
+
VBoxManage sharedfolder remove boot2docker-vm --name Applications
|
237
|
+
VBoxManage sharedfolder add boot2docker-vm --name Applications --hostpath /Applications
|
238
|
+
boot2docker up
|
239
|
+
boot2docker ssh "sudo mkdir -p /Applications && sudo mount -t vboxsf -o uid=1000,gid=50 Applications /Applications"
|
240
|
+
```
|
241
|
+
|
242
|
+
##### Q. cwd is empty in the container
|
243
|
+
This means the said directory was not mounted by oswitch, or was incorrectly
|
244
|
+
mounted. On Linux host, directories that can conflict with paths within
|
245
|
+
container are not mounted. On Mac, `boot2docker` can get in the way.
|
246
|
+
|
247
|
+
Please [report](https://github.com/yeban/oswitch/issues/new) this on our [issue
|
248
|
+
tracker](https://github.com/yeban/oswitch/issues). To help us debug, please
|
249
|
+
include:
|
250
|
+
|
251
|
+
1. the directory in question
|
252
|
+
2. the operating system you are running
|
253
|
+
|
254
|
+
##### Q. oswitch does not work with my docker image
|
255
|
+
Please [report](https://github.com/yeban/oswitch/issues/new) this on our [issue
|
256
|
+
tracker](https://github.com/yeban/oswitch/issues) with oswitch's output. If the
|
257
|
+
image you are using is not available via docker hub or another public
|
258
|
+
repository, please include the Dockerfile as well.
|
259
|
+
|
260
|
+
## Roadmap
|
261
|
+
|
262
|
+
1. ~~make it possible to use docker containers without inheriting our
|
263
|
+
current baseimage~~
|
264
|
+
2. ~~gem distribution for easier installation~~
|
265
|
+
3. brew recipe for Mac
|
266
|
+
4. test on QMUL's compute cluster
|
267
|
+
5. create an SELinux policy to run oswitch on CentOS without having to disable
|
268
|
+
SELinux entirely
|
269
|
+
6. rpm and deb packages
|
270
|
+
7. make available images for common bioinformatics software
|
271
|
+
8. deploy at [RAL/JASMIN](http://www.jasmin.ac.uk)
|
272
|
+
|
273
|
+
## Contribute
|
274
|
+
|
275
|
+
$ git clone https://github.com/yeban/oswitch
|
276
|
+
$ cd oswitch
|
277
|
+
$ gem install bundler && bundle
|
278
|
+
$ bundle exec bin/oswitch biolinux
|
279
|
+
|
280
|
+
## Contributors & Funding
|
281
|
+
|
282
|
+
* Anurag Priyam - [a.priyam@qmul.ac.uk](mailto:a.priyam@qmul.ac.uk) | [@yeban](//twitter.com/yeban)
|
283
|
+
* [Bruno Vieira](https://github.com/bmpvieira) ([@bmpvieira](//twitter.com/bmpvieira))
|
284
|
+
* [Saurabh Kumar](https://github.com/sa1)
|
285
|
+
* Richard Nichols - [http://www.sbcs.qmul.ac.uk/staff/richardnichols.html](http://www.sbcs.qmul.ac.uk/staff/richardnichols.html) | [@qmwugbt112](//twitter.com/qmwugbt112)
|
286
|
+
* Yannick Wurm - [http://wurmlab.github.io](http://wurmlab.github.io) |
|
287
|
+
[@yannick__](//twitter.com/yannick__)
|
288
|
+
|
289
|
+
---
|
290
|
+
|
291
|
+
<p align="center">
|
292
|
+
Development funded as part of NERC EOS Cloud at
|
293
|
+
<a href="http://wurmlab.github.io/">Wurm Lab</a>,
|
294
|
+
<a href="http://sbcs.mul.ac.uk/">Queen Mary University of London</a>.
|
295
|
+
</p>
|
data/bin/oswitch
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'oswitch'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
collect = lambda do |key|
|
8
|
+
lambda do |value|
|
9
|
+
options[key] = value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
optspec = OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: oswitch [options] <package> [cmd]"
|
15
|
+
|
16
|
+
opts.on '-l', '--list',
|
17
|
+
'List packages.',
|
18
|
+
&collect[:list]
|
19
|
+
|
20
|
+
opts.on '-h', '--help',
|
21
|
+
'Print help.',
|
22
|
+
&collect[:help]
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
optspec.order!
|
27
|
+
rescue OptionParser::InvalidOption,
|
28
|
+
OptionParser::MissingArgument => e
|
29
|
+
puts e
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
if options[:help] || (options.empty? && ARGV.empty?)
|
34
|
+
puts optspec
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
38
|
+
if options[:list]
|
39
|
+
puts OSwitch.packages
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
package = ARGV[0]
|
44
|
+
command = ARGV[1..-1]
|
45
|
+
OSwitch.to(package, command)
|
data/lib/oswitch.rb
ADDED
@@ -0,0 +1,293 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'colorize'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'shellwords'
|
5
|
+
|
6
|
+
# OSwitch leverages docker to provide access to complex Bioinformatics software
|
7
|
+
# (even Biolinux!) in just one command.
|
8
|
+
#
|
9
|
+
# Images are built on the user's system on demand and executed in a container.
|
10
|
+
# Containers are removed after execution.
|
11
|
+
#
|
12
|
+
# Volumes from host OS are mounted in the container just the same, including
|
13
|
+
# home directory. USER, HOME, SHELL, and PWD are preserved.
|
14
|
+
class OSwitch
|
15
|
+
|
16
|
+
class ENOPKG < StandardError
|
17
|
+
|
18
|
+
def initialize(name)
|
19
|
+
@name = name
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"Recipe to run #@name not available."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ENODKR < StandardError
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
"***** Docker not installed / correctly setup / running.
|
31
|
+
Are you able to run 'docker info'?"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
include Timeout
|
36
|
+
|
37
|
+
# Captures a docker image's metadata.
|
38
|
+
Image = Struct.new :repository, :tag, :id, :created, :size
|
39
|
+
|
40
|
+
class Image
|
41
|
+
|
42
|
+
# Model Image's eigenclass as a collection of Image objects.
|
43
|
+
class << self
|
44
|
+
|
45
|
+
include Enumerable
|
46
|
+
|
47
|
+
def all
|
48
|
+
`docker images`.split("\n").drop(1).
|
49
|
+
map{|l| Image.new(*l.split(/\s{2,}/))}
|
50
|
+
end
|
51
|
+
|
52
|
+
def each(&block)
|
53
|
+
all.each(&block)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get(imgname)
|
57
|
+
repository, tag = imgname.split(':')
|
58
|
+
return if not repository or repository.empty?
|
59
|
+
tag = 'latest' if not tag or tag.empty?
|
60
|
+
find {|img| img.repository == repository and img.tag == tag}
|
61
|
+
end
|
62
|
+
|
63
|
+
def exists?(imgname)
|
64
|
+
!!get(imgname)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Linux specific code.
|
70
|
+
module Linux
|
71
|
+
|
72
|
+
def uid
|
73
|
+
Process.uid
|
74
|
+
end
|
75
|
+
|
76
|
+
def gid
|
77
|
+
Process.gid
|
78
|
+
end
|
79
|
+
|
80
|
+
# Parse /proc/mounts for mountpoints.
|
81
|
+
def mountpoints
|
82
|
+
mtab = IO.readlines '/proc/mounts'
|
83
|
+
mountpoints = mtab.map{ |line| line.split(/\s+/)[1]}
|
84
|
+
mountpoints.map!{ |mount| unescape(mount) }
|
85
|
+
# Ignore common system mountpoints.
|
86
|
+
mountpoints.reject!{ |mount| mount =~ /^\/$/ }
|
87
|
+
mountpoints.reject!{ |mount| mount =~ /^\/(proc|sys|usr|boot|tmp|dev|var|bin|etc|lib).*/ }
|
88
|
+
# Mount /run/media/* but ignore other /run/ mountpoints.
|
89
|
+
mountpoints.reject!{ |mount| mount =~ /^\/run.*/ unless mount =~ /^\/run\/(media.*)/ }
|
90
|
+
|
91
|
+
# Add home dir.
|
92
|
+
mountpoints << home
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def unescape(mount)
|
98
|
+
mount.gsub(/\\040/, " ").gsub(/\\012/, "\n").gsub(/\\134/, "\\").gsub(/\\011/, "\t")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Mac OS X specific code.
|
103
|
+
module Darwin
|
104
|
+
|
105
|
+
BLACKLIST =
|
106
|
+
%r{
|
107
|
+
^/$|
|
108
|
+
^/(bin|cores|dev|etc|home|Incompatible\ Software|
|
109
|
+
installer\.failurerequests|lost\+found|net|
|
110
|
+
Network|opt|private|sbin|System|Users|tmp|
|
111
|
+
usr|var|Volumes$)
|
112
|
+
}x
|
113
|
+
|
114
|
+
def uid
|
115
|
+
`boot2docker ssh id -u`.chomp
|
116
|
+
end
|
117
|
+
|
118
|
+
def gid
|
119
|
+
`boot2docker ssh id -g`.chomp
|
120
|
+
end
|
121
|
+
|
122
|
+
def mountpoints
|
123
|
+
volumes = Dir['/Volumes/*'].map {|v| File.symlink?(v) ? File.readlink(v) : v}
|
124
|
+
volumes = volumes | Dir['/*']
|
125
|
+
volumes.reject! { |mount| mount =~ BLACKLIST }
|
126
|
+
volumes << home
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# NOTE:
|
131
|
+
# This won't work on JRuby, as it sets RUBY_PLATFORM to 'java'.
|
132
|
+
case RUBY_PLATFORM
|
133
|
+
when /linux/
|
134
|
+
include Linux
|
135
|
+
when /darwin/
|
136
|
+
include Darwin
|
137
|
+
end
|
138
|
+
|
139
|
+
DOTDIR = File.expand_path('~/.oswitch')
|
140
|
+
|
141
|
+
class << self
|
142
|
+
# Invoke as `OSwitch.to` instead of `OSwitch.new`.
|
143
|
+
alias_method :to, :new
|
144
|
+
private :new
|
145
|
+
|
146
|
+
def packages
|
147
|
+
Dir["#{DOTDIR}/*"].
|
148
|
+
select {|entry| File.directory? entry}.
|
149
|
+
map {|pkg|
|
150
|
+
pkg.gsub("#{DOTDIR}/", '')
|
151
|
+
}
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def initialize(package, command = [])
|
156
|
+
@package = package.strip
|
157
|
+
@command = command.join(' ')
|
158
|
+
@imgname = "oswitch_#{@package}"
|
159
|
+
@cntname = "#{@package.gsub(%r{/|:}, '_')}-#{Process.pid}"
|
160
|
+
exec
|
161
|
+
end
|
162
|
+
|
163
|
+
attr_reader :package, :command, :imgname, :cntname
|
164
|
+
|
165
|
+
def exec
|
166
|
+
ping and build and switch
|
167
|
+
rescue ENODKR, ENOPKG => e
|
168
|
+
puts e
|
169
|
+
exit
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def switch
|
175
|
+
cmdline = "docker run --name #{cntname} --hostname #{cntname} -it --rm" \
|
176
|
+
" -w #{cwd} #{mountargs} #{imgname} "
|
177
|
+
if command.empty?
|
178
|
+
# Display motd and run interactive shell.
|
179
|
+
cmdline << "#{shell} -c \"echo #{motd}; #{shell} -i\""
|
180
|
+
else
|
181
|
+
cmdline << "#{shell} -c \"#{command}\""
|
182
|
+
end
|
183
|
+
Kernel.exec cmdline
|
184
|
+
end
|
185
|
+
|
186
|
+
def build
|
187
|
+
return true if Image.exists? imgname
|
188
|
+
write_context && system("docker build -t #{imgname} #{context_dir}")
|
189
|
+
end
|
190
|
+
|
191
|
+
# Ping docker daemon. Raise error if no response within 10s.
|
192
|
+
def ping
|
193
|
+
pong = timeout 5, ENODKR do
|
194
|
+
system 'docker info > /dev/null 2>&1'
|
195
|
+
end
|
196
|
+
pong or raise ENODKR
|
197
|
+
end
|
198
|
+
|
199
|
+
## Code to generate context dir that will be built into a docker image. ##
|
200
|
+
|
201
|
+
# Write data to context dir.
|
202
|
+
def write_context
|
203
|
+
create_context_dir
|
204
|
+
write_dockerfile
|
205
|
+
end
|
206
|
+
|
207
|
+
# Create context dir.
|
208
|
+
def create_context_dir
|
209
|
+
FileUtils.mkdir_p context_dir
|
210
|
+
FileUtils.cp_r(template_files, context_dir)
|
211
|
+
end
|
212
|
+
|
213
|
+
# Write Dockerfile.
|
214
|
+
def write_dockerfile
|
215
|
+
dockerfile = File.join(context_dir, 'Dockerfile')
|
216
|
+
File.write(dockerfile, dockerfile_data)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Generate String that get written to Dockerfile.
|
220
|
+
def dockerfile_data
|
221
|
+
data = ["FROM #{package}"]
|
222
|
+
data << 'COPY _switch /'
|
223
|
+
data << 'COPY wheel /etc/sudoers.d/'
|
224
|
+
data << "RUN /_switch #{userargs} 2>&1 | tee /tmp/oswitch.log"
|
225
|
+
data << 'ENV LC_ALL en_US.UTF-8'
|
226
|
+
data << "USER #{username}"
|
227
|
+
data << "ENTRYPOINT [\"#{shell}\", \"-c\"]"
|
228
|
+
data.join("\n")
|
229
|
+
end
|
230
|
+
|
231
|
+
# Location of context dir.
|
232
|
+
def context_dir
|
233
|
+
File.join(DOTDIR, package)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Location of template dir.
|
237
|
+
def template_dir
|
238
|
+
File.expand_path('../context/', File.dirname(__FILE__))
|
239
|
+
end
|
240
|
+
|
241
|
+
# Template files.
|
242
|
+
def template_files
|
243
|
+
Dir[File.join(template_dir, '*')]
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
## Data required to switchify a container. ##
|
248
|
+
def username
|
249
|
+
ENV['USER']
|
250
|
+
end
|
251
|
+
|
252
|
+
def home
|
253
|
+
ENV['HOME']
|
254
|
+
end
|
255
|
+
|
256
|
+
def shell
|
257
|
+
File.basename ENV['SHELL']
|
258
|
+
end
|
259
|
+
|
260
|
+
def cwd
|
261
|
+
Dir.pwd
|
262
|
+
end
|
263
|
+
|
264
|
+
def motd
|
265
|
+
str =<<MOTD
|
266
|
+
################################################################################
|
267
|
+
You are now running: #{package}, in container: #{cntname}.
|
268
|
+
|
269
|
+
Container is distinct from the shell your launched this container from. Changes
|
270
|
+
you make here will be lost unless it's made to one of the directories below:
|
271
|
+
|
272
|
+
- #{mountpoints.join("\n - ")}
|
273
|
+
|
274
|
+
It's possible you may not be able to write to one or more directories above,
|
275
|
+
but it should be possible to read data from all. Home directory is often the
|
276
|
+
safest to write to.
|
277
|
+
|
278
|
+
Press Ctrl-D or type 'exit' to go back.
|
279
|
+
################################################################################
|
280
|
+
MOTD
|
281
|
+
str.blue.shellescape
|
282
|
+
end
|
283
|
+
|
284
|
+
def mountargs
|
285
|
+
mountpoints.map do |mountpoint|
|
286
|
+
"-v '#{mountpoint}':'#{mountpoint}'"
|
287
|
+
end.join(' ')
|
288
|
+
end
|
289
|
+
|
290
|
+
def userargs
|
291
|
+
[uid, gid, username, home, shell].join(' ')
|
292
|
+
end
|
293
|
+
end
|
data/oswitch.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
# meta
|
3
|
+
s.name = 'oswitch'
|
4
|
+
s.version = '0.1.0'
|
5
|
+
s.authors = ['Anurag Priyam']
|
6
|
+
s.email = ['anurag08priyam@gmail.com']
|
7
|
+
s.homepage = 'https://github.com/yeban/oswitch'
|
8
|
+
s.license = 'MIT'
|
9
|
+
|
10
|
+
s.summary = "Use docker image as the host operating system's user and \
|
11
|
+
access host operating system's filesystem."
|
12
|
+
s.description = <<DESC
|
13
|
+
Use any docker image as the host operating system's user (same user name, same
|
14
|
+
uid, same gid, and even the same shell!) and access to host operating system's
|
15
|
+
filesystem.
|
16
|
+
DESC
|
17
|
+
|
18
|
+
# dependencies
|
19
|
+
s.add_dependency('colorize', '~> 0.7.5')
|
20
|
+
|
21
|
+
# gem
|
22
|
+
s.files = Dir['lib/**/*'] + Dir['Dockerfiles/**/*']
|
23
|
+
s.files = s.files + ['Gemfile', 'oswitch.gemspec']
|
24
|
+
s.files = s.files + ['README.mkd']
|
25
|
+
s.require_paths = ['lib']
|
26
|
+
s.executables = ['oswitch']
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oswitch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anurag Priyam
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.7.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.7.5
|
27
|
+
description: |
|
28
|
+
Use any docker image as the host operating system's user (same user name, same
|
29
|
+
uid, same gid, and even the same shell!) and access to host operating system's
|
30
|
+
filesystem.
|
31
|
+
email:
|
32
|
+
- anurag08priyam@gmail.com
|
33
|
+
executables:
|
34
|
+
- oswitch
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- Gemfile
|
39
|
+
- README.mkd
|
40
|
+
- bin/oswitch
|
41
|
+
- lib/oswitch.rb
|
42
|
+
- oswitch.gemspec
|
43
|
+
homepage: https://github.com/yeban/oswitch
|
44
|
+
licenses:
|
45
|
+
- MIT
|
46
|
+
metadata: {}
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 2.4.2
|
64
|
+
signing_key:
|
65
|
+
specification_version: 4
|
66
|
+
summary: Use docker image as the host operating system's user and access host operating
|
67
|
+
system's filesystem.
|
68
|
+
test_files: []
|