Skip to content

Commit 429b367

Browse files
committed
Added vagrant vm spawning provider
Ticket: ENT-5725 Signed-off-by: Victor Moene <victor.moene@northern.tech>
1 parent 0617c6d commit 429b367

File tree

4 files changed

+337
-35
lines changed

4 files changed

+337
-35
lines changed

cf_remote/Vagrantfile

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# -*- mode: ruby -*-
2+
# vi: set ft=ruby :
3+
require 'json'
4+
5+
config_path = File.expand_path("../config.json", __FILE__)
6+
if !File.file?(config_path)
7+
abort "Need a config file for this host at " + config_path
8+
end
9+
config = JSON.parse(File.read(config_path))
10+
11+
VM_BOX = config['box']
12+
VM_COUNT = config['count']
13+
VM_MEMORY = config['memory']
14+
VM_CPUS = config['cpus']
15+
VM_PROVISION = File.expand_path(config['provision'], __FILE__)
16+
VM_NAME = config['name']
17+
SYNC_FOLDER = config['sync_folder']
18+
19+
Vagrant.configure("2") do |config|
20+
21+
(1..VM_COUNT).each do |i|
22+
23+
config.vm.define "#{VM_NAME}-#{i}" do |node|
24+
node.vm.hostname = "#{VM_NAME}-#{i}"
25+
node.vm.network "private_network", ip: "192.168.56.#{9 + i}"
26+
node.vm.box = VM_BOX
27+
28+
node.vm.provision "shell" do |s|
29+
ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub").first.strip
30+
s.inline = <<-SHELL
31+
echo #{ssh_pub_key} >> /home/vagrant/.ssh/authorized_keys
32+
echo #{ssh_pub_key} >> /root/.ssh/authorized_keys
33+
SHELL
34+
end
35+
node.vm.provision "bootstrap", type: "shell", path: VM_PROVISION
36+
node.vm.synced_folder ".", "/vagrant",
37+
rsync__args: ["--verbose", "--archive", "--delete", "-z", "--links"]
38+
node.vm.synced_folder "#{SYNC_FOLDER}", "/northern.tech",
39+
rsync__args: ["--verbose", "--archive", "--delete", "-z", "--links"]
40+
41+
# https://bugs.launchpad.net/cloud-images/+bug/1874453
42+
NOW = Time.now.strftime("%d.%m.%Y.%H:%M:%S")
43+
FILENAME = "serial-debug-%s.log" % NOW
44+
node.vm.provider "virtualbox" do |vb|
45+
vb.memory = VM_MEMORY
46+
vb.cpus = VM_CPUS
47+
vb.customize [ "guestproperty", "set", :id, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold", 1000 ]
48+
vb.customize [ "modifyvm", :id, "--uart1", "0x3F8", "4" ]
49+
vb.customize [ "modifyvm", :id, "--uartmode1", "file",
50+
File.join(Dir.pwd, FILENAME) ]
51+
end
52+
53+
node.vm.provider :libvirt do |v, override|
54+
v.memory = VM_MEMORY
55+
v.cpus = VM_CPUS
56+
# Fedora 30+ uses QEMU sessions by default, breaking pretty much all
57+
# previously working Vagrantfiles:
58+
# https://fedoraproject.org/wiki/Changes/Vagrant_2.2_with_QEMU_Session#Upgrade.2Fcompatibility_impact
59+
v.qemu_use_session = false
60+
override.vm.synced_folder "./", "/vagrant", type: :rsync
61+
override.vm.synced_folder "#{SYNC_FOLDER}", "/northern.tech", type: :rsync
62+
end
63+
end
64+
end
65+
end

cf_remote/commands.py

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,14 @@
3737
CFRChecksumError,
3838
CFRUserError,
3939
)
40-
from cf_remote.spawn import VM, VMRequest, Providers, AWSCredentials, GCPCredentials
40+
from cf_remote.spawn import (
41+
VM,
42+
VMRequest,
43+
Providers,
44+
AWSCredentials,
45+
GCPCredentials,
46+
VagrantVM,
47+
)
4148
from cf_remote.spawn import spawn_vms, destroy_vms, dump_vms_info, get_cloud_driver
4249
from cf_remote import log
4350
from cf_remote import cloud_data
@@ -393,6 +400,9 @@ def spawn(
393400
network=None,
394401
public_ip=True,
395402
extend_group=False,
403+
vagrant_cpus=None,
404+
vagrant_sync=None,
405+
vagrant_provision=None,
396406
):
397407
creds_data = None
398408
if os.path.exists(CLOUD_CONFIG_FPATH):
@@ -469,13 +479,20 @@ def spawn(
469479
network=network,
470480
role=role,
471481
spawned_cb=print_progress_dot,
482+
vagrant_cpus=vagrant_cpus,
483+
vagrant_sync=vagrant_sync,
484+
vagrant_provision=vagrant_provision,
472485
)
473486
except ValueError as e:
474487
print("\nError: Failed to spawn VMs - " + str(e))
475488
return 1
476489
print("DONE")
477490

478-
if public_ip and (not all(vm.public_ips for vm in vms)):
491+
if (
492+
provider != Providers.VAGRANT
493+
and public_ip
494+
and (not all(vm.public_ips for vm in vms))
495+
):
479496
print("Waiting for VMs to get IP addresses...", end="")
480497
sys.stdout.flush() # STDOUT is line-buffered
481498
while not all(vm.public_ips for vm in vms):
@@ -565,35 +582,47 @@ def destroy(group_name=None):
565582

566583
region = vms_info[group_name]["meta"]["region"]
567584
provider = vms_info[group_name]["meta"]["provider"]
568-
if provider not in ["aws", "gcp"]:
585+
if provider not in ["aws", "gcp", "vagrant"]:
569586
raise CFRUserError(
570-
"Unsupported provider '{}' encountered in '{}', only aws / gcp is supported".format(
587+
"Unsupported provider '{}' encountered in '{}', only aws, gcp or vagrant are supported".format(
571588
provider, CLOUD_STATE_FPATH
572589
)
573590
)
574591

575592
driver = None
576-
if provider == "aws":
577-
if aws_creds is None:
578-
raise CFRExitError("Missing/incomplete AWS credentials")
579-
driver = get_cloud_driver(Providers.AWS, aws_creds, region)
580-
if provider == "gcp":
581-
if gcp_creds is None:
582-
raise CFRExitError("Missing/incomplete GCP credentials")
583-
driver = get_cloud_driver(Providers.GCP, gcp_creds, region)
584-
assert driver is not None
585-
586-
nodes = driver.list_nodes()
587-
for name, vm_info in vms_info[group_name].items():
588-
if name == "meta":
589-
continue
590-
vm_uuid = vm_info["uuid"]
591-
vm = VM.get_by_uuid(vm_uuid, nodes=nodes)
592-
if vm is not None:
593-
to_destroy.append(vm)
594-
else:
595-
print("VM '%s' not found in the clouds" % vm_uuid)
596-
del vms_info[group_name]
593+
if not provider == "vagrant":
594+
if provider == "aws":
595+
if aws_creds is None:
596+
raise CFRExitError("Missing/incomplete AWS credentials")
597+
driver = get_cloud_driver(Providers.AWS, aws_creds, region)
598+
if provider == "gcp":
599+
if gcp_creds is None:
600+
raise CFRExitError("Missing/incomplete GCP credentials")
601+
driver = get_cloud_driver(Providers.GCP, gcp_creds, region)
602+
603+
assert driver is not None
604+
605+
nodes = driver.list_nodes()
606+
for name, vm_info in vms_info[group_name].items():
607+
if name == "meta":
608+
continue
609+
vm_uuid = vm_info["uuid"]
610+
vm = VM.get_by_uuid(vm_uuid, nodes=nodes)
611+
if vm is not None:
612+
to_destroy.append(vm)
613+
else:
614+
print("VM '%s' not found in the clouds" % vm_uuid)
615+
del vms_info[group_name]
616+
else:
617+
for name, vm_info in vms_info[group_name].items():
618+
if name == "meta":
619+
continue
620+
vm = VagrantVM.get_by_info(name, vm_info)
621+
if vm is not None:
622+
to_destroy.append(vm)
623+
else:
624+
print("VM '%s' not found in the clouds" % name)
625+
del vms_info[group_name]
597626
else:
598627
print("Destroying all hosts")
599628
for group_name in [key for key in vms_info.keys() if key.startswith("@")]:
@@ -603,7 +632,7 @@ def destroy(group_name=None):
603632

604633
region = vms_info[group_name]["meta"]["region"]
605634
provider = vms_info[group_name]["meta"]["provider"]
606-
if provider not in ["aws", "gcp"]:
635+
if provider not in ["aws", "gcp", "vagrant"]:
607636
raise CFRUserError(
608637
"Unsupported provider '{}' encountered in '{}', only aws / gcp is supported".format(
609638
provider, CLOUD_STATE_FPATH
@@ -655,6 +684,10 @@ def list_platforms():
655684
return 0
656685

657686

687+
def list_boxes():
688+
return 0
689+
690+
658691
def init_cloud_config():
659692
if os.path.exists(CLOUD_CONFIG_FPATH):
660693
print("File %s already exists" % CLOUD_CONFIG_FPATH)

cf_remote/main.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ def _get_arg_parser():
224224
sp.add_argument(
225225
"--list-platforms", help="List supported platforms", action="store_true"
226226
)
227+
sp.add_argument("--list-boxes", help="List installed boxes", action="store_true")
227228
sp.add_argument(
228229
"--init-config",
229230
help="Initialize configuration file for spawn functionality",
@@ -242,8 +243,22 @@ def _get_arg_parser():
242243
help="Append the new VMs to a pre-existing group",
243244
action="store_true",
244245
)
245-
sp.add_argument("--aws", help="Spawn VMs in AWS (default)", action="store_true")
246-
sp.add_argument("--gcp", help="Spawn VMs in GCP", action="store_true")
246+
sp.add_argument(
247+
"--provider",
248+
help="VM provider",
249+
type=str,
250+
default="aws",
251+
choices=["aws", "gcp", "vagrant"],
252+
)
253+
sp.add_argument("--cpus", help="Number of cpus of the vagrant instances", type=str)
254+
sp.add_argument(
255+
"--sync-root",
256+
help="Root folder of synchronized folders of vagrant instance",
257+
type=str,
258+
)
259+
sp.add_argument(
260+
"--provision", help="full path to provision shell script for Vagrant VM", type=str
261+
)
247262
sp.add_argument("--size", help="Size/type of the instances", type=str)
248263
sp.add_argument(
249264
"--network", help="network/subnet to assign the VMs to (GCP only)", type=str
@@ -360,24 +375,38 @@ def run_command_with_args(command, args) -> int:
360375
elif command == "spawn":
361376
if args.list_platforms:
362377
return commands.list_platforms()
378+
if args.list_boxes:
379+
return commands.list_boxes()
363380
if args.init_config:
364381
return commands.init_cloud_config()
365382
if args.name and "," in args.name:
366383
raise CFRExitError("Group --name may not contain commas")
367-
if args.aws and args.gcp:
368-
raise CFRExitError("--aws and --gcp cannot be used at the same time")
369384
if args.role.endswith("s"):
370385
# role should be singular
371386
args.role = args.role[:-1]
372-
if args.gcp:
387+
if args.provider == "gcp":
373388
provider = Providers.GCP
374-
else:
375-
# AWS is currently also the default
389+
elif args.provider == "aws":
376390
provider = Providers.AWS
377391
if args.network:
378392
raise CFRExitError("--network not supported for AWS")
379393
if args.no_public_ip:
380394
raise CFRExitError("--no-public-ip not supported for AWS")
395+
else:
396+
provider = Providers.VAGRANT
397+
398+
if provider != Providers.VAGRANT:
399+
if args.cpus:
400+
raise CFRExitError("--cpus not supported for {}".format(args.provider))
401+
if args.sync_root:
402+
raise CFRExitError(
403+
"--sync-root not supported for {}".format(args.provider)
404+
)
405+
if args.provision:
406+
raise CFRExitError(
407+
"--provision not supported for {}".format(args.provider)
408+
)
409+
381410
if args.network and (args.network.count("/") != 1):
382411
raise CFRExitError(
383412
"Invalid network specified, needs to be in the network/subnet format"
@@ -393,6 +422,9 @@ def run_command_with_args(command, args) -> int:
393422
network=args.network,
394423
public_ip=not args.no_public_ip,
395424
extend_group=args.append,
425+
vagrant_cpus=args.cpus,
426+
vagrant_sync=args.sync_root,
427+
vagrant_provision=args.provision,
396428
)
397429
elif command == "show":
398430
return commands.show(args.ansible_inventory)

0 commit comments

Comments
 (0)