Produce random-but-stable MAC addresses, take 2
[discourse_docker.git] / launcher
1 #!/bin/bash
2
3 usage () {
4 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs] [--skip-discourse-prereqs] [--docker-args STRING]"
5 echo "Commands:"
6 echo " start: Start/initialize a container"
7 echo " stop: Stop a running container"
8 echo " restart: Restart a container"
9 echo " destroy: Stop and remove a container"
10 echo " enter: Use nsenter to enter a container"
11 echo " logs: Docker logs for container"
12 echo " bootstrap: Bootstrap a container for the config based on a template"
13 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
14 echo " cleanup: Remove all containers that have stopped for > 24 hours"
15 echo " setup: Create a new configuration file and bootstrap"
16 echo
17 echo "Options:"
18 echo " --skip-prereqs Don't check launcher prerequisites"
19 echo " --skip-discourse-prereqs Don't check prerequisites specifiy to Discourse"
20 echo " --docker-args Extra arguments to pass when running docker"
21 exit 1
22 }
23
24 command=$1
25 config=$2
26
27 user_args=""
28
29 while [ ${#} -gt 0 ]; do
30 case "${1}" in
31
32 --skip-prereqs)
33 SKIP_PREREQ="1"
34 ;;
35 --skip-discourse-prereqs)
36 SKIP_DISCOURSE_PREREQS="1"
37 ;;
38 --docker-args)
39 user_args="$2"
40 shift
41 ;;
42 esac
43
44 shift 1
45 done
46
47 # Docker doesn't like uppercase characters, spaces or special characters, catch it now before we build everything out and then find out
48 re='[A-Z/ !@#$%^&*()+~`=]'
49 if [[ $config =~ $re ]];
50 then
51 echo
52 echo "ERROR: Config name must not contain upper case characters, spaces or special characters. Correct config name and rerun $0."
53 echo
54 exit 1
55 fi
56
57 cd "$(dirname "$0")"
58
59 docker_min_version='1.6.0'
60 docker_rec_version='1.6.0'
61 git_min_version='1.8.0'
62 git_rec_version='1.8.0'
63
64 config_file=containers/"$config".yml
65 cidbootstrap=cids/"$config"_bootstrap.cid
66 local_discourse=local_discourse
67 image=discourse/discourse:1.0.17
68 docker_path=`which docker.io || which docker`
69 git_path=`which git`
70 template_path=samples/standalone.yml
71 changelog=/tmp/changelog # used to test whether sed did anything
72
73 if [ "${SUPERVISED}" = "true" ]; then
74 restart_policy="--restart=no"
75 attach_on_start="-a"
76 attach_on_run="-a stdout -a stderr"
77 else
78 attach_on_run="-d"
79 fi
80
81 if [ -n "$DOCKER_HOST" ]; then
82 docker_ip=`sed -e 's/^tcp:\/\/\(.*\):.*$/\1/' <<< "$DOCKER_HOST"`
83 elif [ -x "$(which ip 2>/dev/null)" ]; then
84 docker_ip=`ip addr show docker0 | \
85 grep 'inet ' | \
86 awk '{ split($2,a,"/"); print a[1] }';`
87 else
88 docker_ip=`ifconfig | \
89 grep -B1 "inet addr" | \
90 awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
91 grep docker0 | \
92 awk -F: '{ print $3 }';`
93 fi
94
95 compare_version() {
96 declare -a ver_a
97 declare -a ver_b
98 IFS=. read -a ver_a <<< "$1"
99 IFS=. read -a ver_b <<< "$2"
100
101 while [[ -n $ver_a ]]; do
102 if (( ver_a > ver_b )); then
103 return 0
104 elif (( ver_b > ver_a )); then
105 return 1
106 else
107 unset ver_a[0]
108 ver_a=("${ver_a[@]}")
109 unset ver_b[0]
110 ver_b=("${ver_b[@]}")
111 fi
112 done
113 return 1 # They are equal
114 }
115
116
117 install_docker() {
118
119 echo "Docker is not installed, you will need to install Docker in order to run Discourse"
120 echo "Please visit https://docs.docker.com/installation/ for instructions on how to do this for your system"
121 echo
122 echo "If you are running a recent Ubuntu Server, try the following:"
123 echo "sudo apt-get install docker-engine"
124
125 exit 1
126 }
127
128 check_prereqs() {
129
130 if [ -z $docker_path ]; then
131 install_docker
132 fi
133
134 # 1. docker daemon running?
135 # we send stderr to /dev/null cause we don't care about warnings,
136 # it usually complains about swap which does not matter
137 test=`$docker_path info 2> /dev/null`
138 if [[ $? -ne 0 ]] ; then
139 echo "Cannot connect to the docker daemon - verify it is running and you have access"
140 exit 1
141 fi
142
143 # 2. running aufs or btrfs
144 test=`$docker_path info 2> /dev/null | grep 'Driver: '`
145 if [[ "$test" =~ [aufs|btrfs|zfs|overlay] ]] ; then : ; else
146 echo "Your Docker installation is not using a supported filesystem if we were to proceed you may have a broken install."
147 echo "aufs is the recommended filesystem you should be using (zfs/btrfs and overlay may work as well)"
148 echo "You can tell what filesystem you are using by running \"docker info\" and looking at the driver"
149 echo
150 echo "If you wish to continue anyway using your existing unsupported filesystem, "
151 echo "read the source code of launcher and figure out how to bypass this."
152 exit 1
153 fi
154
155 # 3. running recommended docker version
156 test=($($docker_path --version)) # Get docker version string
157 test=${test[2]//,/} # Get version alone and strip comma if exists
158
159 # At least minimum docker version
160 if compare_version "${docker_min_version}" "${test}"; then
161 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
162 exit 1
163 fi
164
165 # Recommend newer docker version
166 if compare_version "${docker_rec_version}" "${test}"; then
167 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
168 fi
169
170 # 4. discourse docker image is downloaded
171 test=`$docker_path images | awk '{print $1 ":" $2 }' | grep "$image"`
172
173 if [ -z "$test" ]; then
174 echo
175 echo "WARNING: We are about to start downloading the Discourse base image"
176 echo "This process may take anywhere between a few minutes to an hour, depending on your network speed"
177 echo
178 echo "Please be patient"
179 echo
180
181 fi
182
183 # 5. running recommended git version
184 test=($($git_path --version)) # Get git version string
185 test=${test[2]//,/} # Get version alone and strip comma if exists
186
187 # At least minimum version
188 if compare_version "${git_min_version}" "${test}"; then
189 echo "ERROR: Git version ${test} not supported, please upgrade to at least ${git_min_version}, or recommended ${git_rec_version}"
190 exit 1
191 fi
192
193 # Recommend best version
194 if compare_version "${git_rec_version}" "${test}"; then
195 echo "WARNING: Git version ${test} deprecated, recommend upgrade to ${git_rec_version} or newer."
196 fi
197
198 # 6. able to attach stderr / out / tty
199 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
200 if [[ "$test" =~ "working" ]] ; then : ; else
201 echo "Your Docker installation is not working correctly"
202 echo
203 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
204 exit 1
205 fi
206
207 }
208
209
210
211 check_resources() {
212 # Memory
213 resources="ok"
214 avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
215 if [ "$avail_mem" -lt 900 ]; then
216 resources="insufficient"
217 echo "WARNING: You do not appear to have sufficient memory to run Discourse."
218 echo
219 echo "Your system may not work properly, or future upgrades of Discourse may"
220 echo "not complete successfully."
221 echo
222 echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#create-new-cloud-server"
223 elif [ "$avail_mem" -lt 1800 ]; then
224 total_swap="$(LANG=C free -m | grep ^Swap: | awk '{print $2}')"
225 if [ "$total_swap" -lt 1000 ]; then
226 resources="insufficient"
227 echo "WARNING: You must have at least 1GB of swap when running with less"
228 echo "than 2GB of RAM."
229 echo
230 echo "Your system may not work properly, or future upgrades of Discourse may"
231 echo "not complete successfully."
232 echo
233 echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#set-up-swap-if-needed"
234 fi
235 fi
236
237 # Disk space
238 free_disk="$(df /var | tail -n 1 | awk '{print $4}')"
239 if [ "$free_disk" -lt 5000 ]; then
240 resources="insufficient"
241 echo "WARNING: You must have at least 5GB of *free* disk space to run Discourse."
242 echo
243 echo "Insufficient disk space may result in problems running your site, and may"
244 echo "not even allow Discourse installation to complete successfully."
245 echo
246 echo "Please free up some space, or expand your disk, before continuing."
247 echo
248 echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused packages and \`./launcher cleanup\` to remove stale Docker containers."
249 exit 1
250 fi
251
252 if [ -t 0 ] && [ "$resources" != "ok" ]; then
253 echo
254 read -p "Press ENTER to continue, or Ctrl-C to exit and give your system more resources"
255 fi
256 }
257
258 check_ports() {
259 local valid=$(netstat -tln | awk '{print $4}' | grep ":${1}\$")
260
261 if [ -n "$valid" ]; then
262 echo "Launcher has detected that port ${1} is in use."
263 echo
264 echo "If you are trying to run Discourse simultaneously with another web server like Apache or nginx, you will need to bind to a different port."
265 echo "See https://meta.discourse.org/t/17247 for help."
266 echo "To continue anyway, re-run Launcher with --skip-prereqs"
267 exit 1
268 fi
269 }
270
271 if [ -z "$SKIP_PREREQS" ] ; then
272 check_prereqs
273 fi
274
275 host_run() {
276 read -r -d '' env_ruby << 'RUBY'
277 require 'yaml'
278
279 input = STDIN.readlines.join
280 yaml = YAML.load(input)
281
282 if host_run = yaml['host_run']
283 params = yaml['params'] || {}
284 host_run.each do |run|
285 params.each do |k,v|
286 run = run.gsub("$#{k}", v)
287 end
288 STDOUT.write "#{run}--SEP--"
289 end
290 end
291 RUBY
292
293 host_run=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
294
295 while [ "$host_run" ] ; do
296 iter=${host_run%%--SEP--*}
297 echo
298 echo "Host run: $iter"
299 $iter || exit 1
300 echo
301 host_run="${host_run#*--SEP--}"
302 done
303 }
304
305
306 set_volumes() {
307 volumes=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
308 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
309 }
310
311 set_links() {
312 links=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
313 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
314 }
315
316 set_template_info() {
317
318 templates=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
319 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
320
321 arrTemplates=(${templates// / })
322 config_data=$(cat $config_file)
323
324 input="hack: true"
325
326 for template in "${arrTemplates[@]}"
327 do
328 [ ! -z $template ] && {
329 input="$input _FILE_SEPERATOR_ $(cat $template)"
330 }
331 done
332
333 # we always want our config file last so it takes priority
334 input="$input _FILE_SEPERATOR_ $config_data"
335
336 read -r -d '' env_ruby << 'RUBY'
337 require 'yaml'
338
339 input=STDIN.readlines.join
340 # default to UTF-8 for the dbs sake
341 env = {'LANG' => 'en_US.UTF-8'}
342 input.split('_FILE_SEPERATOR_').each do |yml|
343 yml.strip!
344 begin
345 env.merge!(YAML.load(yml)['env'] || {})
346 rescue Psych::SyntaxError => e
347 puts e
348 puts "*ERROR."
349 rescue => e
350 puts yml
351 p e
352 end
353 end
354 puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
355 RUBY
356
357 raw=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
358
359 env=()
360 ok=1
361 while read i; do
362 if [ "$i" == "*ERROR." ]; then
363 ok=0
364 elif [ -n "$i" ]; then
365 env[${#env[@]}]=$i
366 fi
367 done <<< "$raw"
368
369 if [ "$ok" -ne 1 ]; then
370 echo "${env[@]}"
371 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
372 exit 1
373 fi
374 }
375
376 if [ -z $docker_path ]; then
377 install_docker
378 fi
379
380 [ "$command" == "cleanup" ] && {
381 echo
382 echo "The following command will"
383 echo "- Delete all docker images for old containers"
384 echo "- Delete all stopped and orphan containers"
385 echo
386 read -p "Are you sure (Y/n): " -n 1 -r && echo
387 if [[ $REPLY =~ ^[Yy]$ || ! $REPLY ]]
388 then
389 space=$(df /var/lib/docker | awk '{ print $4 }' | grep -v Available)
390 echo "Starting Cleanup (bytes free $space)"
391
392 STATE_DIR=./.gc-state scripts/docker-gc
393
394 space=$(df /var/lib/docker | awk '{ print $4 }' | grep -v Available)
395 echo "Finished Cleanup (bytes free $space)"
396
397 else
398 exit 1
399 fi
400 exit 0
401 }
402
403 if [ -z "$command" -a -z "$config" ]; then
404 usage
405 fi
406
407 if [ ! "$command" == "setup" ]; then
408 if [[ ! -e $config_file ]]; then
409 echo "Config file was not found, ensure $config_file exists"
410 echo
411 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
412 exit 1
413 fi
414 fi
415
416 docker_version=($($docker_path --version))
417 docker_version=${test[2]//,/}
418 restart_policy=${restart_policy:---restart=always}
419
420 set_existing_container(){
421 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
422 }
423
424 run_stop() {
425
426 set_existing_container
427
428 if [ ! -z $existing ]
429 then
430 (
431 set -x
432 $docker_path stop -t 10 $config
433 )
434 else
435 echo "$config was not started !"
436 exit 1
437 fi
438 }
439
440 set_run_image() {
441 run_image=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
442 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['run_image']"`
443
444 if [ -z "$run_image" ]; then
445 run_image="$local_discourse/$config"
446 fi
447 }
448
449 set_boot_command() {
450 boot_command=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
451 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['boot_command']"`
452
453 if [ -z "$boot_command" ]; then
454
455 no_boot_command=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
456 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['no_boot_command']"`
457
458 if [ -z "$no_boot_command" ]; then
459 boot_command="/sbin/boot"
460 fi
461 fi
462 }
463
464 scale_ram_and_cpu() {
465
466 # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
467 avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
468 avail_gb=$(( $avail_mem / 950 ))
469 avail_cores=`cat /proc/cpuinfo | grep "cpu cores" | uniq | awk '{print $4}'`
470 echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"
471
472 # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB
473 if [ "$avail_gb" -eq "1" ]
474 then
475 db_shared_buffers=128
476 else
477 if [ "$avail_gb" -eq "2" ]
478 then
479 db_shared_buffers=256
480 else
481 db_shared_buffers=$(( 256 * $avail_gb ))
482 fi
483 fi
484 db_shared_buffers=$(( db_shared_buffers < 4096 ? db_shared_buffers : 4096 ))
485
486 sed -i -e "s/^ #db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $config_file
487 if [ -s $changelog ]
488 then
489 echo "setting db_shared_buffers = ${db_shared_buffers}MB based on detected CPU/RAM"
490 rm $changelog
491 fi
492
493
494 # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8
495 if [ "$avail_gb" -le "2" ]
496 then
497 unicorn_workers=$(( 2 * $avail_gb ))
498 else
499 unicorn_workers=$(( 2 * $avail_cores ))
500 fi
501 unicorn_workers=$(( unicorn_workers < 8 ? unicorn_workers : 8 ))
502
503 sed -i -e "s/^ #UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $config_file
504 if [ -s $changelog ]
505 then
506 echo "setting UNICORN_WORKERS = ${unicorn_workers} based on detected CPU/RAM"
507 rm $changelog
508 fi
509
510 }
511
512 set_config() {
513 if [ -f $config_file ]
514 then
515 echo $config_file exists already.
516 echo To remove it use: rm $config_file
517 exit 1
518 fi
519 cp ./samples/standalone.yml $config_file
520 if [ ! -f $config_file ]
521 then
522 echo Unable to copy $config_file. Are you root?
523 exit 1
524 fi
525
526 local hostname="discourse.example.com"
527 local developer_emails="me@example.com"
528 local smtp_address="smtp.example.com"
529 local smtp_user_name="user@example.com"
530 local smtp_password="pa\$\$word"
531 local letsencrypt_account_email="your.email@example.com"
532 local letsencrypt_status="change to enable"
533
534 local new_value=""
535 local letsencrypt_status="change to enable"
536 local config_sane="n"
537 local config_ok="n"
538 local update_ok="y"
539
540 while [[ "$config_ok" == "n" || "$config_sane" == "n" ]]
541 do
542 if [ ! -z $hostname ]
543 then
544 read -p "hostname: [$hostname]: " new_value
545 if [ ! -z $new_value ]
546 then
547 hostname=$new_value
548 else
549 echo "Unchanged."
550 fi
551 fi
552 if [ ! -z $developer_emails ]
553 then
554 read -p "developer_emails [$developer_emails]: " new_value
555 if [ ! -z $new_value ]
556 then
557 developer_emails=$new_value
558 fi
559 fi
560 if [ ! -z $smtp_address ]
561 then
562 read -p "smtp_address [$smtp_address]: " new_value
563 if [ ! -z $new_value ]
564 then
565 smtp_address=$new_value
566 fi
567 fi
568 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
569 then
570 smtp_user_name="SMTP_Injection"
571
572 fi
573 if [ "$smtp_address" == "smtp.sendgrid.net" ]
574 then
575 smtp_user_name="apikey"
576 fi
577 if [ ! -z $smtp_user_name ]
578 then
579 read -p "smtp_user_name [$smtp_user_name]: " new_value
580 if [ ! -z $new_value ]
581 then
582 smtp_user_name=$new_value
583 fi
584 fi
585 if [ ! -z $smtp_password ]
586 then
587 read -p "smtp_password [$smtp_password]: " new_value
588 if [ ! -z $new_value ]
589 then
590 smtp_password=$new_value
591 fi
592 fi
593 if [ ! -z $letsencrypt_account_email ]
594 then
595 read -p "letsencrypt_account_email ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
596 if [ ! -z $new_value ]
597 then
598 letsencrypt_account_email=$new_value
599 if [ "$new_value" == "off" ]
600 then
601 letsencrypt_status="change to enable"
602 else
603 letsencrypt_status="Enter 'OFF' to disable."
604 echo "Letsencrypt enabled."
605 fi
606 else
607 echo "letsencrypt unchanged"
608 fi
609 fi
610
611 #TODO sanity check these values. For now we trust the user's input.
612 config_sane="y"
613
614 if [ "$config_sane" == "y" ]
615 then
616 echo -e "\nThat's it! Everything is set. Read carefully before continuing.\n"
617 else
618 echo "Errors found in settings"
619 fi
620
621 echo "DISCOURSE_HOSTNAME: $hostname"
622 echo "DISCOURSE_DEVELOPER_EMAILS: $developer_emails"
623 echo "DISCOURSE_SMTP_ADDRESS: $smtp_address"
624 echo "DISCOURSE_SMTP_USER_NAME: $smtp_user_name"
625 echo "DISCOURSE_SMTP_PASSWORD: $smtp_password"
626 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
627 then
628 echo "LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email"
629 echo "LETSENCRYPT will be enabled."
630 else
631 echo "LETSENCRYPT will not be enabled."
632 fi
633 echo
634 read -p "Enter to write these settings to $config_file, 'N' to retry, or ^C to start again: " config_ok
635 done
636
637 echo -e "\nWriting changes to $config_file:"
638 sed -i -e "s/^ DISCOURSE_HOSTNAME: 'discourse.example.com'/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $config_file
639 if [ -s $changelog ]
640 then
641 cat $changelog
642 rm $changelog
643 else
644 echo DISCOURSE_HOSTNAME change failed.
645 update_ok="n"
646 fi
647
648 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $config_file
649 if [ -s $changelog ]
650 then
651 cat $changelog
652 rm $changelog
653 else
654 echo DISCOURSE_DEVELOPER_EMAILS change failed.
655 update_ok="n"
656 fi
657
658 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS: smtp.example.com.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $config_file
659 if [ -s $changelog ]
660 then
661 cat $changelog
662 rm $changelog
663 else
664 echo DISCOURSE_SMTP_ADDRESS change failed.
665 update_ok="n"
666 fi
667
668 sed -i -e "s/^ #DISCOURSE_SMTP_USER_NAME: user@example.com.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $config_file
669 if [ -s $changelog ]
670 then
671 cat $changelog
672 rm $changelog
673 else
674 echo DISCOURSE_SMTP_USER_NAME change failed.
675 update_ok="n"
676 fi
677
678 sed -i -e "s/^ #DISCOURSE_SMTP_PASSWORD: pa\$\$word.*/ DISCOURSE_SMTP_PASSWORD: $smtp_password/w $changelog" $config_file
679 if [ -s $changelog ]
680 then
681 cat $changelog
682 rm $changelog
683 else
684 echo DISCOURSE_SMTP_PASSWORD change failed.
685 update_ok="n"
686 fi
687
688 if [ "$letsencrypt_status" != "change to enable" ]
689 then
690 sed -i -e "s/^ #LETSENCRYPT_ACCOUNT_EMAIL: your.email@example.com/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $config_file
691 if [ -s $changelog ]
692 then
693 cat $changelog
694 rm $changelog
695 else
696 echo LETSENCRYPT_ACCOUNT_EMAIL change failed.
697 update_ok="n"
698 fi
699 local src='^ #- "templates\/web.ssl.template.yml"'
700 local dst=' \- "templates\/web.ssl.template.yml"'
701 sed -i -e "s/$src/$dst/w $changelog" $config_file
702 if [ -s $changelog ]
703 then
704 echo " web.ssl.template.yml enabled"
705 else
706 update_ok="n"
707 echo " web.ssl.template.yml NOT ENABLED--was it on already?"
708 fi
709 local src='^ #- "templates\/web.letsencrypt.ssl.template.yml"'
710 local dst=' - "templates\/web.letsencrypt.ssl.template.yml"'
711
712 sed -i -e "s/$src/$dst/w $changelog" $config_file
713 if [ -s $changelog ]
714 then
715 echo " letsencrypt.ssl.template.yml enabled"
716 else
717 update_ok="n"
718 echo "letsencrypt.ssl.template.yml NOT ENABLED--was it on already?"
719 fi
720 fi # enable letsencrypt
721
722 if [ "$update_ok" == "y" ]
723 then
724 echo -e "\n$config_file updated successfully."
725 else
726 echo -e "There was an error changing the configuration.\n"
727 fi
728 }
729
730 run_start() {
731
732 existing=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
733 echo $existing
734 if [ ! -z $existing ]
735 then
736 echo "Nothing to do, your container has already started!"
737 exit 0
738 fi
739
740 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
741 if [ ! -z $existing ]
742 then
743 echo "starting up existing container"
744 (
745 set -x
746 $docker_path start $config
747 )
748 exit 0
749 fi
750
751 host_run
752
753 if [ -z "$SKIP_DISCOURSE_PREREQS" ] ; then
754 ports=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
755 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| \"-p #{p}\"}.join(' ')"`
756
757 IFS='-p ' read -a array <<< "$ports"
758 for element in "${array[@]}"
759 do
760 IFS=':' read -a args <<< "$element"
761 if [ "${#args[@]}" == "2" ]; then
762 check_ports "${args[0]}"
763 elif [ "${#args[@]}" == "3" ]; then
764 check_ports "${args[1]}"
765 fi
766 done
767 fi
768
769 docker_args=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
770 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['docker_args']"`
771
772 set_template_info
773 set_volumes
774 set_links
775 set_run_image
776 set_boot_command
777
778 # get hostname and settings from container configuration
779 for envar in "${env[@]}"
780 do
781 if [[ $envar == DOCKER_USE_HOSTNAME* ]] || [[ $envar == DISCOURSE_HOSTNAME* ]]
782 then
783 # use as environment variable
784 eval $envar
785 fi
786 done
787
788 (
789 hostname=`hostname -s`
790 # overwrite hostname
791 if [ "$DOCKER_USE_HOSTNAME" = "true" ]
792 then
793 hostname=$DISCOURSE_HOSTNAME
794 else
795 hostname=$hostname-$config
796 fi
797
798 # we got to normalize so we only have allowed strings, this is more comprehensive but lets see how bash does first
799 # hostname=`$docker_path run $user_args --rm $image ruby -e 'print ARGV[0].gsub(/[^a-zA-Z-]/, "-")' $hostname`
800 # docker added more hostname rules
801 hostname=${hostname/_/-}
802
803 mac_address="--mac-address $($docker_path run $user_args -i --rm -a stdout -a stderr $image /bin/sh -c "echo $hostname | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'")"
804
805 set -x
806 $docker_path run $user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname" \
807 -e DOCKER_HOST_IP=$docker_ip --name $config -t $ports $volumes $mac_address $docker_args \
808 $run_image $boot_command
809
810 )
811 exit 0
812
813 }
814
815 valid_config_check() {
816
817 valid_config="y"
818 for x in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
819 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
820 do
821 mail_var=`grep "^ $x:" $config_file`
822 local result=$?
823 local default="example.com"
824 if (( result == 0 ))
825 then
826 if [[ $mail_var = *"$default"* ]]
827 then
828 echo "Warning: $x left at incorrect default of example.com"
829 valid_config="n"
830 fi
831 else
832 echo "Warning: $x not configured"
833 valid_config="n"
834 fi
835 done
836 if [ -t 0 ] && [ "$valid_config" != "y" ]; then
837 echo
838 read -p "Press Ctrl-C to exit and edit $config_file or ENTER to continue"
839 fi
840 }
841
842 run_bootstrap() {
843 if [ -z "$SKIP_DISCOURSE_PREREQS" ] ; then
844 # Does your system meet the minimum requirements?
845 check_resources
846
847 # is our configuration file valid?
848 valid_config_check
849
850 # make minor scaling adjustments for RAM and CPU
851 scale_ram_and_cpu
852 fi
853
854 # I got no frigging clue what this does, ask Sam Saffron. It RUNS STUFF ON THE HOST I GUESS?
855 host_run
856
857 # Is the image available?
858 # If not, pull it here so the user is aware what's happening.
859 $docker_path history $image >/dev/null 2>&1 || $docker_path pull $image
860
861 set_template_info
862
863 base_image=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
864 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
865
866 update_pups=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
867 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
868
869 if [[ ! X"" = X"$base_image" ]]; then
870 image=$base_image
871 fi
872
873 set_volumes
874 set_links
875
876 rm -f $cidbootstrap
877
878 run_command="cd /pups &&"
879 if [[ ! "false" = $update_pups ]]; then
880 run_command="$run_command git pull &&"
881 fi
882 run_command="$run_command /pups/bin/pups --stdin"
883
884 echo $run_command
885
886 (exec echo "$input" | $docker_path run $user_args $links "${env[@]}" -e DOCKER_HOST_IP=$docker_ip --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \
887 /bin/bash -c "$run_command") \
888 || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
889
890 [ ! -e $cidbootstrap ] && echo "** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one" && exit 1
891
892 sleep 5
893
894 $docker_path commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
895 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
896 }
897
898
899
900 case "$command" in
901 bootstrap)
902 run_bootstrap
903 echo "Successfully bootstrapped, to startup use ./launcher start $config"
904 exit 0
905 ;;
906
907 setup)
908 set_config
909 read -p "Press ENTER to continue, or Ctrl-C to exit to check $config_file"
910 run_bootstrap
911 exit 0
912 ;;
913
914 enter)
915 exec $docker_path exec -it $config /bin/bash --login
916 ;;
917
918 stop)
919 run_stop
920 exit 0
921 ;;
922
923 logs)
924
925 $docker_path logs $config
926 exit 0
927 ;;
928
929 restart)
930 run_stop
931 run_start
932 exit 0
933 ;;
934
935 start)
936 run_start
937 exit 0
938 ;;
939
940 rebuild)
941 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
942 echo "Ensuring discourse docker is up to date"
943
944 git remote update
945
946 LOCAL=$(git rev-parse @)
947 REMOTE=$(git rev-parse @{u})
948 BASE=$(git merge-base @ @{u})
949
950 if [ $LOCAL = $REMOTE ]; then
951 echo "Discourse Docker is up-to-date"
952
953 elif [ $LOCAL = $BASE ]; then
954 echo "Updating Discourse Docker"
955 git pull || (echo 'failed to update' && exit 1)
956 exec /bin/bash $0 $@
957
958 elif [ $REMOTE = $BASE ]; then
959 echo "Your version of Discourse Docker is ahead of origin"
960
961 else
962 echo "Discourse Docker has diverged source, this is only expected in Dev mode"
963 fi
964
965 fi
966
967 set_existing_container
968
969 if [ ! -z $existing ]
970 then
971 echo "Stopping old container"
972 (
973 set -x
974 $docker_path stop -t 10 $config
975 )
976 fi
977
978 run_bootstrap
979
980 if [ ! -z $existing ]
981 then
982 echo "Removing old container"
983 (
984 set -x
985 $docker_path rm $config
986 )
987 fi
988
989 run_start
990 exit 0
991 ;;
992
993
994 destroy)
995 (set -x; $docker_path stop -t 10 $config && $docker_path rm $config) || (echo "$config was not found" && exit 0)
996 exit 0
997 ;;
998 esac
999
1000 usage