Skip to content

Commit

Permalink
Add a wrapper for Vm.assemble to create Sshable for the service vms
Browse files Browse the repository at this point in the history
We manage customer virtual machines, as well as the virtual machines
utilized by our other services. The latter are managed by the control
plane, which connects these virtual machines via SSH. Or services like
GitHub runners, PostgreSQL, MinIO, and E2E tests all rely on these
virtual machines. Each service generates an Sshable, assembles the
virtual machine, and then updates the host of the Sshable once the
machine is ready. To streamline this process, I've consolidated the
generation and testing from 4 different locations into one.

`Vm::Nexus.assemble_with_sshable` serves as a wrapper for
`Vm::Nexus.assemble`, sharing the same signature with one key
difference: it receives a `unix_user` as the first argument instead of a
`public_key`.  Consequently, it generates an Sshable for the virtual
machine
  • Loading branch information
enescakir committed Nov 2, 2023
1 parent 98ae59c commit 987c976
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 93 deletions.
14 changes: 2 additions & 12 deletions prog/minio/minio_server_nexus.rb
Expand Up @@ -18,10 +18,8 @@ def self.assemble(minio_pool_id, index)
end

DB.transaction do
ssh_key = SshKey.generate

vm_st = Prog::Vm::Nexus.assemble(
ssh_key.public_key,
vm_st = Prog::Vm::Nexus.assemble_with_sshable(
"minio-user",
Config.minio_service_project_id,
location: minio_pool.cluster.location,
size: minio_pool.cluster.target_vm_size,
Expand All @@ -30,16 +28,9 @@ def self.assemble(minio_pool_id, index)
] + Array.new(minio_pool.per_server_driver_count) { {encrypted: false, size_gib: (minio_pool.per_server_storage_size / minio_pool.per_server_driver_count).floor} },
boot_image: "ubuntu-jammy",
enable_ip4: true,
unix_user: "minio-user",
private_subnet_id: minio_pool.cluster.private_subnet.id
)

Sshable.create(
unix_user: "minio-user",
host: "temp_#{vm_st.id}",
raw_private_key_1: ssh_key.keypair
) { _1.id = vm_st.id }

minio_server = MinioServer.create_with_id(minio_pool_id: minio_pool_id, vm_id: vm_st.id, index: index)

Strand.create(prog: "Minio::MinioServerNexus", label: "start") { _1.id = minio_server.id }
Expand All @@ -61,7 +52,6 @@ def cluster
label def start
nap 5 unless vm.strand.label == "wait"
register_deadline(:wait, 10 * 60)
vm.sshable.update(host: vm.ephemeral_net4)
bud Prog::BootstrapRhizome, {"target_folder" => "minio", "subject_id" => vm.id, "user" => "minio-user"}

hop_wait_bootstrap_rhizome
Expand Down
12 changes: 2 additions & 10 deletions prog/postgres/postgres_nexus.rb
Expand Up @@ -24,9 +24,8 @@ def self.assemble(project_id, location, server_name, vm_size, storage_size_gib)
DB.transaction do
ubid = PostgresResource.generate_ubid

ssh_key = SshKey.generate
vm_st = Prog::Vm::Nexus.assemble(
ssh_key.public_key,
vm_st = Prog::Vm::Nexus.assemble_with_sshable(
"ubi",
Config.postgres_service_project_id,
location: location,
name: ubid.to_s,
Expand All @@ -39,12 +38,6 @@ def self.assemble(project_id, location, server_name, vm_size, storage_size_gib)
enable_ip4: true
)

Sshable.create(
unix_user: "ubi",
host: "temp_#{vm_st.id}",
raw_private_key_1: ssh_key.keypair
) { _1.id = vm_st.id }

postgres_resource = PostgresResource.create(
project_id: project_id, location: location, server_name: server_name,
target_vm_size: vm_size, target_storage_size_gib: storage_size_gib,
Expand All @@ -68,7 +61,6 @@ def before_run

label def start
nap 5 unless vm.strand.label == "wait"
vm.sshable.update(host: vm.ephemeral_net4)

postgres_resource.incr_initial_provisioning
hop_bootstrap_rhizome
Expand Down
34 changes: 6 additions & 28 deletions prog/test/vm_group.rb
Expand Up @@ -30,14 +30,10 @@ def self.assemble(storage_encrypted: true, test_reboot: true)
strand.add_child(subnet1_s)
strand.add_child(subnet2_s)

keypair_1 = SshKey.generate
keypair_2 = SshKey.generate
keypair_3 = SshKey.generate

storage_encrypted = frame.fetch("storage_encrypted", true)

vm1_s = Prog::Vm::Nexus.assemble(
keypair_1.public_key, project.id,
vm1_s = Prog::Vm::Nexus.assemble_with_sshable(
"ubi", project.id,
private_subnet_id: subnet1_s.id,
storage_volumes: [
{encrypted: storage_encrypted},
Expand All @@ -46,38 +42,20 @@ def self.assemble(storage_encrypted: true, test_reboot: true)
enable_ip4: true
)

vm2_s = Prog::Vm::Nexus.assemble(
keypair_2.public_key, project.id,
vm2_s = Prog::Vm::Nexus.assemble_with_sshable(
"ubi", project.id,
private_subnet_id: subnet1_s.id,
storage_volumes: [{encrypted: storage_encrypted}],
enable_ip4: true
)

vm3_s = Prog::Vm::Nexus.assemble(
keypair_3.public_key, project.id,
vm3_s = Prog::Vm::Nexus.assemble_with_sshable(
"ubi", project.id,
private_subnet_id: subnet2_s.id,
storage_volumes: [{encrypted: storage_encrypted}],
enable_ip4: true
)

Sshable.create(
unix_user: "ubi",
host: "temp_#{vm1_s.id}",
raw_private_key_1: keypair_1.keypair
) { _1.id = vm1_s.id }

Sshable.create(
unix_user: "ubi",
host: "temp_#{vm2_s.id}",
raw_private_key_1: keypair_2.keypair
) { _1.id = vm2_s.id }

Sshable.create(
unix_user: "ubi",
host: "temp_#{vm3_s.id}",
raw_private_key_1: keypair_3.keypair
) { _1.id = vm3_s.id }

strand.add_child(vm1_s)
strand.add_child(vm2_s)
strand.add_child(vm3_s)
Expand Down
15 changes: 2 additions & 13 deletions prog/vm/github_runner.rb
Expand Up @@ -57,29 +57,20 @@ def pick_vm
return picked_vm
end

ssh_key = SshKey.generate
# We use unencrypted storage for now, because provisioning 86G encrypted
# storage takes ~8 minutes. Unencrypted disk uses `cp` command instead
# of `spdk_dd` and takes ~3 minutes. If btrfs disk mounted, it decreases to
# ~10 seconds.
vm_st = Prog::Vm::Nexus.assemble(
ssh_key.public_key,
vm_st = Prog::Vm::Nexus.assemble_with_sshable(
"runner",
project.id,
name: github_runner.ubid.to_s,
size: label_data["vm_size"],
unix_user: "runner",
location: label_data["location"],
boot_image: label_data["boot_image"],
storage_volumes: [{size_gib: label_data["storage_size_gib"], encrypted: false}],
enable_ip4: true
)

Sshable.create(
unix_user: "runner",
host: "temp_#{vm_st.id}",
raw_private_key_1: ssh_key.keypair
) { _1.id = vm_st.id }

Clog.emit("Pool is empty") { {github_runner: {label: github_runner.label, repository_name: github_runner.repository_name, cores: vm_st.subject.cores}} }
vm_st.subject
end
Expand Down Expand Up @@ -112,9 +103,7 @@ def before_run

label def wait_vm
nap 5 unless vm.strand.label == "wait"
vm.sshable.update(host: vm.ephemeral_net4)
register_deadline(:wait, 10 * 60)

hop_install_nftables_rules
end

Expand Down
13 changes: 11 additions & 2 deletions prog/vm/nexus.rb
Expand Up @@ -111,6 +111,16 @@ def self.assemble(public_key, project_id, name: nil, size: "standard-2",
end
end

def self.assemble_with_sshable(unix_user, *, **kwargs)
ssh_key = SshKey.generate
kwargs[:unix_user] = unix_user
st = assemble(ssh_key.public_key, *, **kwargs)
Sshable.create(unix_user: unix_user, host: "temp_#{st.id}", raw_private_key_1: ssh_key.keypair) {
_1.id = st.id
}
st
end

def vm_name
@vm_name ||= vm.inhost_name
end
Expand Down Expand Up @@ -228,9 +238,8 @@ def before_run

AssignedVmAddress.create_with_id(dst_vm_id: vm.id, ip: ip4.to_s, address_id: address.id) if ip4
end

vm.sshable&.update(host: vm.ephemeral_net4 || vm.ephemeral_net6.nth(2))
register_deadline(:wait, 10 * 60)

hop_create_unix_user
end

Expand Down
29 changes: 10 additions & 19 deletions prog/vm/vm_pool.rb
Expand Up @@ -29,25 +29,16 @@ def before_run
end

label def create_new_vm
ssh_key = SshKey.generate
DB.transaction do
vm_st = Prog::Vm::Nexus.assemble(
ssh_key.public_key,
Config.vm_pool_project_id,
size: vm_pool.vm_size,
unix_user: "runner",
location: vm_pool.location,
boot_image: vm_pool.boot_image,
storage_volumes: [{size_gib: vm_pool.storage_size_gib, encrypted: false}],
enable_ip4: true,
pool_id: vm_pool.id
)
Sshable.create(
unix_user: "runner",
host: "temp_#{vm_st.id}",
raw_private_key_1: ssh_key.keypair
) { _1.id = vm_st.id }
end
Prog::Vm::Nexus.assemble_with_sshable(
"runner",
Config.vm_pool_project_id,
size: vm_pool.vm_size,
location: vm_pool.location,
boot_image: vm_pool.boot_image,
storage_volumes: [{size_gib: vm_pool.storage_size_gib, encrypted: false}],
enable_ip4: true,
pool_id: vm_pool.id
)

hop_wait
end
Expand Down
2 changes: 0 additions & 2 deletions spec/prog/minio/minio_server_nexus_spec.rb
Expand Up @@ -71,10 +71,8 @@
vm = nx.minio_server.vm
vm.strand.update(label: "wait")
expect(nx).to receive(:register_deadline)
expect(vm).to receive(:ephemeral_net4).and_return("1.1.1.1")
expect(nx).to receive(:bud).with(Prog::BootstrapRhizome, {"target_folder" => "minio", "subject_id" => vm.id, "user" => "minio-user"})
expect { nx.start }.to hop("wait_bootstrap_rhizome")
expect(vm.sshable.host).to eq "1.1.1.1"
end
end

Expand Down
4 changes: 1 addition & 3 deletions spec/prog/postgres/postgres_nexus_spec.rb
Expand Up @@ -74,11 +74,9 @@
expect { nx.start }.to nap(5)
end

it "update sshable host and hops" do
it "hops if vm is ready" do
expect(postgres_resource).to receive(:incr_initial_provisioning)
expect(vm).to receive(:strand).and_return(Strand.new(label: "wait"))
expect(vm).to receive(:ephemeral_net4).and_return("1.1.1.1")
expect(sshable).to receive(:update).with(host: "1.1.1.1")
expect { nx.start }.to hop("bootstrap_rhizome")
end
end
Expand Down
4 changes: 1 addition & 3 deletions spec/prog/vm/github_runner_spec.rb
Expand Up @@ -150,11 +150,9 @@
expect { nx.wait_vm }.to nap(5)
end

it "update sshable host and hops" do
it "hops if vm is ready" do
expect(nx).to receive(:vm).and_return(vm).at_least(:once)
expect(vm).to receive(:strand).and_return(Strand.new(label: "wait"))
expect(vm).to receive(:ephemeral_net4).and_return("1.1.1.1")
expect(sshable).to receive(:update).with(host: "1.1.1.1")
expect { nx.wait_vm }.to hop("install_nftables_rules")
end
end
Expand Down
23 changes: 22 additions & 1 deletion spec/prog/vm/nexus_spec.rb
Expand Up @@ -166,6 +166,23 @@
end
end

describe ".assemble_with_sshable" do
it "calls .assemble with generated ssh key" do
st_id = "eb3dbcb3-2c90-8b74-8fb4-d62a244d7ae5"
expect(SshKey).to receive(:generate).and_return(instance_double(SshKey, public_key: "public", keypair: "pair"))
expect(described_class).to receive(:assemble) do |public_key, project_id, **kwargs|
expect(public_key).to eq("public")
expect(project_id).to eq(prj.id)
expect(kwargs[:name]).to be_nil
expect(kwargs[:size]).to eq("new_size")
expect(kwargs[:unix_user]).to eq("test_user")
end.and_return(Strand.new(id: st_id))
expect(Sshable).to receive(:create).with({unix_user: "test_user", host: "temp_#{st_id}", raw_private_key_1: "pair"})

described_class.assemble_with_sshable("test_user", prj.id, size: "new_size")
end
end

describe "#create_unix_user" do
it "runs adduser" do
sshable = instance_double(Sshable)
Expand Down Expand Up @@ -242,6 +259,7 @@
expect(args[:ephemeral_net6]).to match(/2a01:4f9:2b:35a:.*/)
expect(args[:vm_host_id]).to match vmh_id
end
expect(vm).to receive(:sshable).and_return(nil)

expect { nx.start }.to hop("create_unix_user")
end
Expand All @@ -253,14 +271,17 @@
ip6: NetAddr.parse_ip("2a01:4f9:2b:35a::2")
) { _1.id = vmh_id }
address = Address.new(cidr: "0.0.0.0/30", routed_to_host_id: vmh_id)
assigned_address = AssignedVmAddress.new(ip: "0.0.0.0")
assigned_address = AssignedVmAddress.new(ip: NetAddr::IPv4Net.parse("10.0.0.1"))

expect(nx).to receive(:allocate).and_return(vmh_id)
expect(VmHost).to receive(:[]).with(vmh_id) { vmh }
expect(vmh).to receive(:ip4_random_vm_network).and_return(["0.0.0.0", address])
expect(vm).to receive(:ip4_enabled).and_return(true).twice
expect(AssignedVmAddress).to receive(:create_with_id).and_return(assigned_address)
expect(vm).to receive(:update)
expect(vm).to receive(:assigned_vm_address).and_return(assigned_address)
expect(vm).to receive(:sshable).and_return(instance_double(Sshable)).at_least(:once)
expect(vm.sshable).to receive(:update).with(host: assigned_address.ip.network)

expect { nx.start }.to hop("create_unix_user")
end
Expand Down

0 comments on commit 987c976

Please sign in to comment.