Skip to content

Commit c33a71e

Browse files
skara904vmatsibekkermahi-pas
authored
[Feature:System] Variable number of Vagrant Workers (Submitty#9537)
### Please check if the PR fulfills these requirements: * [ ] Tests for the changes have been added/updated (if possible) * [ ] Documentation has been updated/added if relevant ### What is the current behavior? <!-- List issue if it fixes/closes/implements one using the "Fixes #<number>" or "Closes #<number>" syntax --> Developers can only create one vagrant worker VM at a time, by specifying the `WORKER_PAIR` environment variable on initial vagrant up. ### What is the new behavior? A variable number of worker VMs can be created by specifying the forwarded SSH ports and private IP addresses of each in the `.vagrant/workers.json` configuration file. --------- Co-authored-by: Viane Matsibekker <[email protected]> Co-authored-by: Mahi Pasarkar <[email protected]>
1 parent 8e6731f commit c33a71e

File tree

7 files changed

+152
-77
lines changed

7 files changed

+152
-77
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ Pipfile
4444
Pipfile.lock
4545
.phpunit.result.cache
4646

47-
.env
47+
.env

.setup/CONFIGURE_SUBMITTY.py

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,6 @@ def __call__(self, parser, namespace, values, option_string=None):
6565
help="Sets up Submitty for use with the sample courses. This is a Vagrant convenience "
6666
"flag and should not be used in production!")
6767
parser.add_argument('--worker', action='store_true', default=False, help='Configure Submitty with autograding only')
68-
parser.add_argument(
69-
'--worker-pair',
70-
default=False,
71-
action=StrToBoolAction,
72-
help='Configure Submitty alongside a worker VM. This should only be used during development using Vagrant.'
73-
)
7468
parser.add_argument('--install-dir', default='/usr/local/submitty', help='Set the install directory for Submitty')
7569
parser.add_argument('--data-dir', default='/var/local/submitty', help='Set the data directory for Submitty')
7670
parser.add_argument('--websocket-port', default=8443, type=int, help='Port to use for websocket')
@@ -519,39 +513,33 @@ def write(x=''):
519513

520514
if not args.worker:
521515
if not os.path.isfile(WORKERS_JSON):
516+
capabilities = ["default"]
517+
if args.setup_for_sample_courses:
518+
capabilities.extend(["cpp", "python", "et-cetera", "notebook"])
519+
522520
worker_dict = {
523521
"primary": {
524-
"capabilities": ["default"],
522+
"capabilities": capabilities,
525523
"address": "localhost",
526524
"username": "",
527525
"num_autograding_workers": NUM_GRADING_SCHEDULER_WORKERS,
528526
"enabled" : True
529527
}
530528
}
531529

532-
if args.worker_pair:
533-
worker_dict["submitty-worker"] = {
534-
"capabilities": ['default'],
535-
"address": "192.168.56.21",
536-
"username": "submitty",
537-
"num_autograding_workers": NUM_GRADING_SCHEDULER_WORKERS,
538-
"enabled": True
539-
}
540-
if args.setup_for_sample_courses:
541-
worker_dict['submitty-worker']['capabilities'].extend([
542-
'cpp',
543-
'python',
544-
'et-cetera',
545-
'notebook',
546-
])
547-
548-
if args.setup_for_sample_courses:
549-
worker_dict['primary']['capabilities'].extend([
550-
'cpp',
551-
'python',
552-
'et-cetera',
553-
'notebook',
554-
])
530+
vagrant_workers_json = os.path.join(SUBMITTY_REPOSITORY, '.vagrant', 'workers.json')
531+
if os.path.isfile(vagrant_workers_json):
532+
with open(vagrant_workers_json) as f:
533+
vagrant_workers = json.load(f, object_hook=OrderedDict)
534+
535+
for worker, data in vagrant_workers.items():
536+
worker_dict[worker] = {
537+
"capabilities": capabilities,
538+
"address": data["ip_addr"],
539+
"username": "submitty",
540+
"num_autograding_workers": NUM_GRADING_SCHEDULER_WORKERS,
541+
"enabled": True
542+
}
555543

556544
with open(WORKERS_JSON, 'w') as workers_file:
557545
json.dump(worker_dict, workers_file, indent=4)

.setup/install_system.sh

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,31 +64,31 @@ export UTM=0
6464
export VAGRANT=0
6565
export NO_SUBMISSIONS=0
6666
export WORKER=0
67-
export WORKER_PAIR=0
6867

6968
# Read through the flags passed to the script reading them in and setting
7069
# appropriate bash variables, breaking out of this once we hit something we
7170
# don't recognize as a flag
71+
echo ""
72+
echo "Running setup:"
7273
while :; do
7374
case $1 in
7475
--utm)
7576
export UTM=1
7677
export DEV_VM=1
78+
echo "utm"
7779
;;
7880
--vagrant)
7981
export VAGRANT=1
8082
export DEV_VM=1
83+
echo "vagrant"
8184
;;
8285
--worker)
8386
export WORKER=1
87+
echo "worker"
8488
;;
8589
--no_submissions)
8690
export NO_SUBMISSIONS=1
87-
echo 'no_submissions'
88-
;;
89-
--worker-pair)
90-
export WORKER_PAIR=1
91-
echo "worker_pair"
91+
echo "no_submissions"
9292
;;
9393
*) # No more options, so break out of the loop.
9494
break
@@ -319,12 +319,17 @@ fi
319319

320320
if ! cut -d ':' -f 1 /etc/passwd | grep -q ${DAEMON_USER} ; then
321321
useradd -m -c "First Last,RoomNumber,WorkPhone,HomePhone" "${DAEMON_USER}"
322-
if [ ${WORKER_PAIR} == 1 ]; then
322+
if [ ${WORKER} == 0 ] && [ ${DEV_VM} == 1 ] && [ -f ${SUBMITTY_REPOSITORY}/.vagrant/workers.json ]; then
323323
echo -e "attempting to create ssh key for submitty_daemon..."
324324
su submitty_daemon -c "cd ~/"
325325
su submitty_daemon -c "ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N ''"
326326
su submitty_daemon -c "echo 'successfully created ssh key'"
327-
su submitty_daemon -c "sshpass -p 'submitty' ssh-copy-id -i ~/.ssh/id_rsa.pub -o StrictHostKeyChecking=no [email protected]"
327+
328+
while read -r IP
329+
do
330+
su submitty_daemon -c "sshpass -p 'submitty' ssh-copy-id -i ~/.ssh/id_rsa.pub -o StrictHostKeyChecking=no submitty@${IP}"
331+
done <<< "$(jq -r ".[].ip_addr" "${SUBMITTY_REPOSITORY}/.vagrant/workers.json")"
332+
echo "DONE"
328333
fi
329334
fi
330335

@@ -687,7 +692,7 @@ submitty@vagrant
687692
do-not-reply@vagrant
688693
localhost
689694
25
690-
" | python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py --debug --setup-for-sample-courses --websocket-port ${WEBSOCKET_PORT} --worker-pair ${WORKER_PAIR}
695+
" | python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py --debug --setup-for-sample-courses --websocket-port ${WEBSOCKET_PORT}
691696

692697
# Set these manually as they're not asked about during CONFIGURE_SUBMITTY.py
693698
sed -i -e 's/"url": ""/"url": "ldap:\/\/localhost"/g' ${SUBMITTY_INSTALL_DIR}/config/authentication.json

.setup/install_worker.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# install_worker.sh [<extra> <extra> ...]
55

66
# This script is used to set up the worker machine in a vagrant worker pair
7-
# made by running WORKER_PAIR=1 vagrant up
7+
# made by running WORKERS=n vagrant up
88

99
GIT_PATH=/usr/local/submitty/GIT_CHECKOUT/Submitty
1010
SUPERVISOR_USER=submitty
@@ -15,11 +15,11 @@ echo "checking ${SUPERVISOR_USER} user"
1515
if ! cut -d ':' -f 1 /etc/passwd | grep -q "${SUPERVISOR_USER}" ; then
1616
echo "attempting to add ${SUPERVISOR_USER} user"
1717
#set up submitty user with password 'submitty'
18-
useradd -m -p "$(openssl passwd -crypt submitty)" -c "First Last,RoomNumber,WorkPhone,HomePhone" "${SUPERVISOR_USER}"
18+
useradd -m -p "$(openssl passwd -1 submitty)" -c "First Last,RoomNumber,WorkPhone,HomePhone" "${SUPERVISOR_USER}"
1919
[ -d "/home/${SUPERVISOR_USER}" ] && echo "Directory /home/${SUPERVISOR_USER} exists." || echo "Error: Directory /home/${SUPERVISOR_USER} does not exists."
2020
fi
2121

22-
bash "${GIT_PATH}/.setup/install_system.sh" --worker --vagrant "${@}" 2>&1 | tee "${GIT_PATH}/.vagrant/install_worker_system.log"
22+
bash "${GIT_PATH}/.setup/install_system.sh" --worker --vagrant "${@}" 2>&1
2323
echo "--- FINISHED INSTALLING SYSTEM ---"
2424
echo "installing worker..."
2525

.setup/vagrant/setup_vagrant.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ if ! sudo bash ${SUBMITTY_REPOSITORY}/.setup/install_system.sh --vagrant ${@}; t
2727
For whatever reason, Vagrant has failed to build. If reporting
2828
an error to the developers, please be sure to also send the
2929
build log of Vagrant located at:
30-
.vagrant/install_${DISTRO}_${VERSION}.log.
30+
.vagrant/logs/${DISTRO}-${VERSION}.log.
3131
"
3232
fi

Vagrantfile

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
# NO_SUBMISSIONS=1 vagrant up
88
# or
99
# EXTRA=rpi vagrant up
10-
# or
11-
# WORKER_PAIR=1 vagrant up
1210
#
1311
#
1412
# If you want to override the default image used for the virtual machines, you can set the
@@ -32,33 +30,37 @@
3230
$stdout.sync = true
3331
$stderr.sync = true
3432

35-
extra_command = ''
36-
autostart_worker = false
37-
if ENV.has_key?('NO_SUBMISSIONS')
38-
extra_command << '--no_submissions '
39-
end
40-
if ENV.has_key?('EXTRA')
41-
extra_command << ENV['EXTRA']
42-
end
43-
if ENV.has_key?('WORKER_PAIR')
44-
autostart_worker = true
45-
extra_command << '--worker-pair '
46-
end
33+
require 'json'
4734

48-
$script = <<SCRIPT
49-
GIT_PATH=/usr/local/submitty/GIT_CHECKOUT/Submitty
50-
DISTRO=$(lsb_release -si | tr '[:upper:]' '[:lower:]')
51-
VERSION=$(lsb_release -sr | tr '[:upper:]' '[:lower:]')
52-
bash ${GIT_PATH}/.setup/vagrant/setup_vagrant.sh #{extra_command} 2>&1 | tee ${GIT_PATH}/.vagrant/install_${DISTRO}_${VERSION}.log
53-
SCRIPT
35+
def gen_script(machine_name, worker: false)
36+
no_submissions = !ENV.fetch('NO_SUBMISSIONS', '').empty?
37+
extra = ENV.fetch('EXTRA', '')
5438

55-
$worker_script = <<SCRIPT
56-
GIT_PATH=/usr/local/submitty/GIT_CHECKOUT/Submitty
57-
DISTRO=$(lsb_release -si | tr '[:upper:]' '[:lower:]')
58-
VERSION=$(lsb_release -sr | tr '[:upper:]' '[:lower:]')
59-
bash ${GIT_PATH}/.setup/install_worker.sh #{extra_command} 2>&1 | tee ${GIT_PATH}/.vagrant/install_worker.log
39+
setup_cmd = 'bash ${GIT_PATH}/.setup/'
40+
if worker
41+
setup_cmd += 'install_worker.sh'
42+
else
43+
setup_cmd += 'vagrant/setup_vagrant.sh'
44+
if no_submissions
45+
setup_cmd += ' --no_submissions'
46+
end
47+
end
48+
unless extra.empty?
49+
setup_cmd += " #{extra}"
50+
end
51+
setup_cmd += " 2>&1 | tee ${GIT_PATH}/.vagrant/logs/#{machine_name}.log"
52+
53+
script = <<SCRIPT
54+
GIT_PATH=/usr/local/submitty/GIT_CHECKOUT/Submitty
55+
DISTRO=$(lsb_release -si | tr '[:upper:]' '[:lower:]')
56+
VERSION=$(lsb_release -sr | tr '[:upper:]' '[:lower:]')
57+
mkdir -p ${GIT_PATH}/.vagrant/logs
58+
#{setup_cmd}
6059
SCRIPT
6160

61+
return script
62+
end
63+
6264
base_boxes = Hash[]
6365

6466
# Should all be base Ubuntu boxes that use the same version
@@ -85,6 +87,15 @@ def mount_folders(config, mount_options)
8587
}
8688
end
8789

90+
def get_workers()
91+
worker_file = File.join(__dir__, '.vagrant', 'workers.json')
92+
if File.file?(worker_file)
93+
return JSON.parse(File.read(worker_file), symbolize_names: true)
94+
else
95+
return Hash[]
96+
end
97+
end
98+
8899
Vagrant.configure(2) do |config|
89100
if Vagrant.has_plugin?('vagrant-env')
90101
config.env.enable
@@ -108,21 +119,22 @@ Vagrant.configure(2) do |config|
108119
# that one) as well as making sure all non-primary ones have "autostart: false" set
109120
# so that when we do "vagrant up", it doesn't spin up those machines.
110121

111-
config.vm.define 'submitty-worker', autostart: autostart_worker do |ubuntu|
112-
# If this IP address changes, it must be changed in install_system.sh and
113-
# CONFIGURE_SUBMITTY.py to allow the ssh connection
114-
ubuntu.vm.network "private_network", ip: "192.168.56.21"
115-
ubuntu.vm.network 'forwarded_port', guest: 22, host: 2220, id: 'ssh'
116-
ubuntu.vm.provision 'shell', inline: $worker_script
122+
get_workers.map do |worker_name, data|
123+
config.vm.define worker_name do |ubuntu|
124+
ubuntu.vm.network 'private_network', ip: data[:ip_addr]
125+
ubuntu.vm.network 'forwarded_port', guest: 22, host: data[:ssh_port], id: 'ssh'
126+
ubuntu.vm.provision 'shell', inline: gen_script(worker_name, worker: true)
127+
end
117128
end
118129

119-
config.vm.define 'ubuntu-22.04', primary: true do |ubuntu|
130+
vm_name = 'ubuntu-22.04'
131+
config.vm.define vm_name, primary: true do |ubuntu|
120132
ubuntu.vm.network 'forwarded_port', guest: 1511, host: ENV.fetch('VM_PORT_SITE', 1511)
121133
ubuntu.vm.network 'forwarded_port', guest: 8443, host: ENV.fetch('VM_PORT_WS', 8443)
122134
ubuntu.vm.network 'forwarded_port', guest: 5432, host: ENV.fetch('VM_PORT_DB', 16442)
123135
ubuntu.vm.network 'forwarded_port', guest: 7000, host: ENV.fetch('VM_PORT_SAML', 7000)
124136
ubuntu.vm.network 'forwarded_port', guest: 22, host: ENV.fetch('VM_PORT_SSH', 2222), id: 'ssh'
125-
ubuntu.vm.provision 'shell', inline: $script
137+
ubuntu.vm.provision 'shell', inline: gen_script(vm_name)
126138
end
127139

128140
config.vm.provider 'virtualbox' do |vb, override|

generate_workers.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/usr/bin/python3
2+
3+
import argparse
4+
import glob
5+
import ipaddress
6+
import json
7+
import os
8+
from collections import OrderedDict
9+
from typing import Union, cast
10+
11+
12+
def get_args():
13+
parser = argparse.ArgumentParser(
14+
description='Script to generate configuration for '
15+
'development worker machines')
16+
17+
parser.add_argument('-n', '--num', default=1, type=int,
18+
help='Number of worker machines to configure')
19+
parser.add_argument('--ip-range', default='192.168.56.0/24', type=ipaddress.ip_network,
20+
help='IP address range for workers')
21+
parser.add_argument('--base-port', default=2240, type=int,
22+
help='Base ssh port (ports will be assigned incrementally)')
23+
24+
return parser.parse_args()
25+
26+
27+
def main():
28+
args = get_args()
29+
workers = OrderedDict()
30+
rootdir = os.path.dirname(os.path.realpath(__file__))
31+
workerfile = os.path.join(rootdir, '.vagrant', 'workers.json')
32+
33+
if len(glob.glob(os.path.join(rootdir, '.vagrant', 'machines', '*', '*', 'action_provision'))):
34+
if input("Warning: There are existing vagrant machines in this project that may conflict"
35+
" with new configuration. Are you sure you would like to proceed? [y/N] "
36+
)[0].lower() != 'y':
37+
return
38+
39+
if os.path.isfile(workerfile):
40+
if input('Overwrite existing worker configuration? [y/N] ')[0].lower() != 'y':
41+
return
42+
43+
ips = cast(Union[ipaddress.IPv4Network, ipaddress.IPv6Network], args.ip_range).hosts()
44+
if isinstance(ips, list):
45+
ips = iter(ips)
46+
47+
for i in range(1, args.num+1):
48+
ip = next(ips, None)
49+
while (ip is not None
50+
and (ip.is_reserved or isinstance(ip, ipaddress.IPv4Address)
51+
and str(ip).endswith('.1'))):
52+
ip = next(ips, None)
53+
54+
if ip is None:
55+
raise IndexError("IP range insufficient for requested number of workers")
56+
57+
data = OrderedDict()
58+
data['ip_addr'] = str(ip)
59+
data['ssh_port'] = args.base_port + i
60+
workers[f'worker-{i}'] = data
61+
62+
os.makedirs(os.path.dirname(workerfile), exist_ok=True)
63+
with open(workerfile, 'w') as file:
64+
json.dump(workers, file, indent=4)
65+
66+
print('Wrote new configuration to ' + workerfile)
67+
68+
69+
if __name__ == '__main__':
70+
main()

0 commit comments

Comments
 (0)