Merge branch '3363-docker-add-option-to-use-auth.curoverse.com'
[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['PUBLIC_KEY_PATH'] = find_or_create_ssh_key(config['API_HOSTNAME'])
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 # find_or_create_ssh_key arvados_name
169 #   Returns the SSH public key appropriate for this Arvados instance,
170 #   generating one if necessary.
171 #
172 def find_or_create_ssh_key arvados_name
173   ssh_key_file = "#{ENV['HOME']}/.ssh/arvados_#{arvados_name}_id_rsa"
174   unless File.exists? ssh_key_file
175     system 'ssh-keygen',
176            '-f', ssh_key_file,
177            '-C', "arvados@#{arvados_name}",
178            '-P', ''
179   end
180
181   return "#{ssh_key_file}.pub"
182 end
183
184 # install_docker
185 #   Determines which Docker package is suitable for this Linux distro
186 #   and installs it, resolving any dependencies.
187 #   NOTE: not in use yet.
188
189 def install_docker
190   linux_distro = %x(lsb_release --id).split.last
191   linux_release = %x(lsb_release --release).split.last
192   linux_version = linux_distro + " " + linux_release
193   kernel_release = `uname -r`
194
195   case linux_distro
196   when 'Ubuntu'
197     if not linux_release.match '^1[234]\.'
198       warn "Arvados requires at least Ubuntu 12.04 (Precise Pangolin)."
199       warn "Your system is Ubuntu #{linux_release}."
200       exit 3
201     end
202     if linux_release.match '^12' and kernel_release.start_with? '3.2'
203       # Ubuntu Precise ships with a 3.2 kernel and must be upgraded.
204       warn "Your kernel #{kernel_release} must be upgraded to run Docker."
205       warn "To do this:"
206       warn "  sudo apt-get update"
207       warn "  sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
208       warn "  sudo reboot"
209       exit 4
210     else
211       # install AUFS
212       sudo 'apt-get', 'update'
213       sudo 'apt-get', 'install', "linux-image-extra-#{kernel_release}"
214     end
215
216     # add Docker repository
217     sudo %w(/usr/bin/apt-key adv
218               --keyserver keyserver.ubuntu.com
219               --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9)
220     source_file = Tempfile.new('arv')
221     source_file.write("deb http://get.docker.io/ubuntu docker main\n")
222     source_file.close
223     sudo '/bin/mv', source_file.path, '/etc/apt/sources.list.d/docker.list'
224     sudo %w(/usr/bin/apt-get update)
225     sudo %w(/usr/bin/apt-get install lxc-docker)
226
227     # Set up for non-root access
228     sudo %w(/usr/sbin/groupadd docker)
229     sudo '/usr/bin/gpasswd', '-a', ENV['USER'], 'docker'
230     sudo %w(/usr/sbin/service docker restart)
231   when 'Debian'
232   else
233     warn "Must be running a Debian or Ubuntu release in order to run Docker."
234     exit 5
235   end
236 end
237
238
239 if __FILE__ == $PROGRAM_NAME
240   options = { :makefile => File.join(File.dirname(__FILE__), 'Makefile') }
241   OptionParser.new do |opts|
242     opts.on('-m', '--makefile MAKEFILE-PATH',
243             'Path to the Makefile used to build Arvados Docker images') do |mk|
244       options[:makefile] = mk
245     end
246   end
247   main options
248 end