Add 'apps/arv-web/' from commit 'f9732ad8460d013c2f28363655d0d1b91894dca5'
[arvados.git] / docker / build_tools / build.rb
1 #! /usr/bin/env ruby
2
3 require 'optparse'
4 require 'tempfile'
5 require 'yaml'
6
7 def main options
8   if not ip_forwarding_enabled?
9     warn "NOTE: IP forwarding must be enabled in the kernel."
10     warn "Turning IP forwarding on now."
11     sudo %w(/sbin/sysctl net.ipv4.ip_forward=1)
12   end
13
14   # Check that:
15   #   * Docker is installed and can be found in the user's path
16   #   * Docker can be run as a non-root user
17   #      - TODO: put the user in the docker group if necessary
18   #      - TODO: mount cgroup automatically
19   #      - TODO: start the docker service if not started
20
21   docker_path = %x(which docker.io).chomp
22
23   if docker_path.empty?
24     docker_path = %x(which docker).chomp
25   end
26
27   if docker_path.empty?
28     warn "Docker not found."
29     warn ""
30     warn "Please make sure that Docker has been installed and"
31     warn "can be found in your PATH."
32     warn ""
33     warn "Installation instructions for a variety of platforms can be found at"
34     warn "http://docs.docker.io/en/latest/installation/"
35     exit 1
36   elsif not docker_ok? docker_path
37     warn "WARNING: docker could not be run."
38     warn "Please make sure that:"
39     warn "  * You have permission to read and write /var/run/docker.sock"
40     warn "  * a 'cgroup' volume is mounted on your machine"
41     warn "  * the docker daemon is running"
42     exit 2
43   end
44
45   # Check that debootstrap is installed.
46   if not debootstrap_ok?
47     warn "Installing debootstrap."
48     sudo '/usr/bin/apt-get', 'install', 'debootstrap'
49   end
50
51   # Generate a config.yml if it does not exist or is empty
52   if not File.size? 'config.yml'
53     print "Generating config.yml.\n"
54     print "Arvados needs to know the email address of the administrative user,\n"
55     print "so that when that user logs in they are automatically made an admin.\n"
56     print "This should be an email address associated with a Google account.\n"
57     print "\n"
58     admin_email_address = ""
59     until is_valid_email? admin_email_address
60       print "Enter your Google ID email address here: "
61       admin_email_address = gets.strip
62       if not is_valid_email? admin_email_address
63         print "That doesn't look like a valid email address. Please try again.\n"
64       end
65     end
66
67     print "Arvados needs to know the shell login name for the administrative user.\n"
68     print "This will also be used as the name for your git repository.\n"
69     print "\n"
70     user_name = ""
71     until is_valid_user_name? user_name
72       print "Enter a shell login name here: "
73       user_name = gets.strip
74       if not is_valid_user_name? user_name
75         print "That doesn't look like a valid shell login name. Please try again.\n"
76       end
77     end
78
79     File.open 'config.yml', 'w' do |config_out|
80       config_out.write "# If a _PW or _SECRET variable is set to an empty string, a password\n"
81       config_out.write "# will be chosen randomly at build time. This is the\n"
82       config_out.write "# recommended setting.\n\n"
83       config = YAML.load_file 'config.yml.example'
84       config['API_AUTO_ADMIN_USER'] = admin_email_address
85       config['ARVADOS_USER_NAME'] = user_name
86       config['API_HOSTNAME'] = generate_api_hostname
87       config['API_WORKBENCH_ADDRESS'] = 'false'
88       config.each_key do |var|
89         config_out.write "#{var}: #{config[var]}\n"
90       end
91     end
92   end
93
94   # If all prerequisites are met, go ahead and build.
95   if ip_forwarding_enabled? and
96       docker_ok? docker_path and
97       debootstrap_ok? and
98       File.exists? 'config.yml'
99     exit 0
100   else
101     exit 6
102   end
103 end
104
105 # sudo
106 #   Execute the arg list 'cmd' under sudo.
107 #   cmd can be passed either as a series of arguments or as a
108 #   single argument consisting of a list, e.g.:
109 #     sudo 'apt-get', 'update'
110 #     sudo(['/usr/bin/gpasswd', '-a', ENV['USER'], 'docker'])
111 #     sudo %w(/usr/bin/apt-get install lxc-docker)
112 #
113 def sudo(*cmd)
114   # user can pass a single list in as an argument
115   # to allow usage like: sudo %w(apt-get install foo)
116   warn "You may need to enter your password here."
117   if cmd.length == 1 and cmd[0].class == Array
118     cmd = cmd[0]
119   end
120   system '/usr/bin/sudo', *cmd
121 end
122
123 # is_valid_email?
124 #   Returns true if its arg looks like a valid email address.
125 #   This is a very very loose sanity check.
126 #
127 def is_valid_email? str
128   str.match /^\S+@\S+\.\S+$/
129 end
130
131 # is_valid_user_name?
132 #   Returns true if its arg looks like a valid unix username.
133 #   This is a very very loose sanity check.
134 #
135 def is_valid_user_name? str
136   # borrowed from Debian's adduser (version 3.110)
137   str.match /^[_.A-Za-z0-9][-\@_.A-Za-z0-9]*\$?$/
138 end
139
140 # generate_api_hostname
141 #   Generates a 5-character randomly chosen API hostname.
142 #
143 def generate_api_hostname
144   rand(2**256).to_s(36)[0...5]
145 end
146
147 # ip_forwarding_enabled?
148 #   Returns 'true' if IP forwarding is enabled in the kernel
149 #
150 def ip_forwarding_enabled?
151   %x(/sbin/sysctl -n net.ipv4.ip_forward) == "1\n"
152 end
153
154 # debootstrap_ok?
155 #   Returns 'true' if debootstrap is installed and working.
156 #
157 def debootstrap_ok?
158   return system '/usr/sbin/debootstrap --version > /dev/null 2>&1'
159 end
160
161 # docker_ok?
162 #   Returns 'true' if docker can be run as the current user.
163 #
164 def docker_ok?(docker_path)
165   return system "#{docker_path} images > /dev/null 2>&1"
166 end
167
168 # install_docker
169 #   Determines which Docker package is suitable for this Linux distro
170 #   and installs it, resolving any dependencies.
171 #   NOTE: not in use yet.
172
173 def install_docker
174   linux_distro = %x(lsb_release --id).split.last
175   linux_release = %x(lsb_release --release).split.last
176   linux_version = linux_distro + " " + linux_release
177   kernel_release = `uname -r`
178
179   case linux_distro
180   when 'Ubuntu'
181     if not linux_release.match '^1[234]\.'
182       warn "Arvados requires at least Ubuntu 12.04 (Precise Pangolin)."
183       warn "Your system is Ubuntu #{linux_release}."
184       exit 3
185     end
186     if linux_release.match '^12' and kernel_release.start_with? '3.2'
187       # Ubuntu Precise ships with a 3.2 kernel and must be upgraded.
188       warn "Your kernel #{kernel_release} must be upgraded to run Docker."
189       warn "To do this:"
190       warn "  sudo apt-get update"
191       warn "  sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
192       warn "  sudo reboot"
193       exit 4
194     else
195       # install AUFS
196       sudo 'apt-get', 'update'
197       sudo 'apt-get', 'install', "linux-image-extra-#{kernel_release}"
198     end
199
200     # add Docker repository
201     sudo %w(/usr/bin/apt-key adv
202               --keyserver keyserver.ubuntu.com
203               --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9)
204     source_file = Tempfile.new('arv')
205     source_file.write("deb http://get.docker.io/ubuntu docker main\n")
206     source_file.close
207     sudo '/bin/mv', source_file.path, '/etc/apt/sources.list.d/docker.list'
208     sudo %w(/usr/bin/apt-get update)
209     sudo %w(/usr/bin/apt-get install lxc-docker)
210
211     # Set up for non-root access
212     sudo %w(/usr/sbin/groupadd docker)
213     sudo '/usr/bin/gpasswd', '-a', ENV['USER'], 'docker'
214     sudo %w(/usr/sbin/service docker restart)
215   when 'Debian'
216   else
217     warn "Must be running a Debian or Ubuntu release in order to run Docker."
218     exit 5
219   end
220 end
221
222
223 if __FILE__ == $PROGRAM_NAME
224   options = { :makefile => File.join(File.dirname(__FILE__), 'Makefile') }
225   OptionParser.new do |opts|
226     opts.on('-m', '--makefile MAKEFILE-PATH',
227             'Path to the Makefile used to build Arvados Docker images') do |mk|
228       options[:makefile] = mk
229     end
230   end
231   main options
232 end