Files
ubicloud/spec/routes/web/vm_spec.rb
Jeremy Evans 215f09541a Make access_tag only for project <-> accounts join table
For the includers of HyperTagMethods, this changes the authorization
code and object_tag member validation code to look at the project_id
column for the object, instead of looking a row with the project and
object in the access_tag table.

This removes all calls to associate_with_project, other than
those for Account.  It removes the projects association for
the includers of HyperTagMethods, and adds a project association to
the models that didn't already have one, since there is only a single
project for each object now.

Most HyperTagMethods code is inlined into Account, since it is only
user of the code now.  Temporarily, other models will still include
HyperTagMethods for the before_destroy hook, but eventually it will
go away completely.

The associations in Projects that previous used access_tag as a join
table, are changed from many_to_many to one_to_many, except for
Account (which still uses the join table).

Project#has_resources now needs separate queries for all of the
resource classes to see if there any associated objects.

This causes a lot of fallout in the specs, but unfortunately that is
unavoidable due the extensive use of projects.first in the specs to
get the related project for the objects, as well as the extensive
use of associate_with_project.
2025-01-17 08:32:46 -08:00

388 lines
13 KiB
Ruby

# frozen_string_literal: true
require_relative "spec_helper"
RSpec.describe Clover, "vm" do
let(:user) { create_account }
let(:project) { user.create_project_with_default_policy("project-1") }
let(:project_wo_permissions) { user.create_project_with_default_policy("project-2", default_policy: nil) }
let(:vm) do
vm = Prog::Vm::Nexus.assemble("dummy-public-key", project.id, name: "dummy-vm-1").subject
vm.update(ephemeral_net6: "2a01:4f8:173:1ed3:aa7c::/79")
vm.reload # without reload ephemeral_net6 is string and can't call .network
end
let(:vm_wo_permission) { Prog::Vm::Nexus.assemble("dummy-public-key", project_wo_permissions.id, name: "dummy-vm-2").subject }
describe "unauthenticated" do
it "can not list without login" do
visit "/vm"
expect(page.title).to eq("Ubicloud - Login")
end
it "can not create without login" do
visit "/vm/create"
expect(page.title).to eq("Ubicloud - Login")
end
end
describe "authenticated" do
before do
login(user.email)
end
describe "list" do
it "can list no virtual machines" do
visit "#{project.path}/vm"
expect(page.title).to eq("Ubicloud - Virtual Machines")
expect(page).to have_content "No virtual machines"
click_link "Create Virtual Machine"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
end
it "can not list virtual machines when does not have permissions" do
vm
vm_wo_permission
visit "#{project.path}/vm"
expect(page.title).to eq("Ubicloud - Virtual Machines")
expect(page).to have_content vm.name
expect(page).to have_no_content vm_wo_permission.name
end
end
describe "create" do
it "can create new virtual machine" do
project
visit "#{project.path}/vm/create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
name = "dummy-vm"
fill_in "Name", with: name
choose option: "eu-central-h1"
uncheck "enable_ip4"
choose option: "ubuntu-jammy"
choose option: "standard-2"
click_button "Create"
expect(page.title).to eq("Ubicloud - #{name}")
expect(page).to have_flash_notice("'#{name}' will be ready in a few minutes")
expect(Vm.count).to eq(1)
expect(Vm.first.project_id).to eq(project.id)
expect(Vm.first.private_subnets.first.id).not_to be_nil
expect(Vm.first.ip4_enabled).to be_falsey
visit project.path
expect(page).to have_content("2/32 (6%)")
Vm.first.update(vcpus: 25)
page.refresh
expect(page).to have_content("25/32 (78%)")
Vm.first.update(vcpus: 31)
page.refresh
expect(page).to have_content("31/32 (96%)")
end
it "shows expected information on index page" do
project
visit "#{project.path}/vm"
expect(page).to have_content "Get started by creating a new virtual machine."
click_link "Create Virtual Machine"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
click_button "Create"
vm_host = VmHost.new_with_id(location: "test")
Sshable.create { |s| s.id = vm_host.id }
vm_host.save_changes
address = Address.create(
cidr: "1.2.3.0/24",
routed_to_host_id: vm_host.id
)
vm.assigned_vm_address = AssignedVmAddress.new_with_id(
ip: "1.2.3.4",
address_id: address.id
)
spdk_installation = SpdkInstallation.create(
version: "1",
allocation_weight: 1
) { |obj| obj.id = SpdkInstallation.generate_uuid }
storage_device = StorageDevice.create(
name: "t",
total_storage_gib: 147,
available_storage_gib: 24
)
storage_volume = VmStorageVolume.new(
boot: true,
size_gib: 123,
disk_index: 1,
spdk_installation_id: spdk_installation.id,
storage_device_id: storage_device.id
)
vm.add_vm_storage_volume(storage_volume)
visit "#{project.path}/vm"
page.refresh
expect(page).to have_content "Create Virtual Machine"
expect(page).to have_content "123 GB"
expect(page).to have_content "1.2.3.4"
click_link vm.name
expect(page).to have_content "123 GB"
expect(page.body).to include "auto-refresh hidden"
vm.this.update(display_state: "running")
page.refresh
expect(page.body).not_to include "auto-refresh hidden"
AccessControlEntry.dataset.destroy
AccessControlEntry.create(project_id: project.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Vm:view"])
storage_volume.update(size_gib: 0)
vm.assigned_vm_address.destroy
vm.update(ephemeral_net6: nil)
visit "#{project.path}/vm"
expect(page).to have_no_content "Create Virtual Machine"
expect(page).to have_content "Not assigned yet"
Nic.dataset.destroy
vm.destroy
page.refresh
expect(page).to have_no_content "New Virtual Machine"
expect(page).to have_content "You don't have permission to create virtual machines."
end
it "can create new virtual machine with public ipv4" do
project
visit "#{project.path}/vm/create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
name = "dummy-vm"
fill_in "Name", with: name
choose option: "eu-central-h1"
check "enable_ip4"
choose option: "ubuntu-jammy"
choose option: "standard-2"
click_button "Create"
expect(page.title).to eq("Ubicloud - #{name}")
expect(page).to have_flash_notice("'#{name}' will be ready in a few minutes")
expect(Vm.count).to eq(1)
expect(Vm.first.project_id).to eq(project.id)
expect(Vm.first.private_subnets.first.id).not_to be_nil
expect(Vm.first.ip4_enabled).to be_truthy
end
it "can create new virtual machine with chosen private subnet" do
project
ps_id = Prog::Vnet::SubnetNexus.assemble(project.id, name: "dummy-ps-1").id
ps = PrivateSubnet[ps_id]
visit "#{project.path}/vm/create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
expect(page).to have_content ps.name
name = "dummy-vm"
fill_in "Name", with: name
choose option: "eu-central-h1"
select match: :prefer_exact, text: ps.name
choose option: "ubuntu-jammy"
choose option: "standard-2"
click_button "Create"
expect(page.title).to eq("Ubicloud - #{name}")
expect(page).to have_flash_notice("'#{name}' will be ready in a few minutes")
expect(Vm.count).to eq(1)
expect(Vm.first.project_id).to eq(project.id)
expect(Vm.first.private_subnets.first.id).to eq(ps.id)
end
it "can create new virtual machine in default location subnet" do
project
ps_id = Prog::Vnet::SubnetNexus.assemble(project.id, name: "dummy-ps-1").id
ps = PrivateSubnet[ps_id]
visit "#{project.path}/vm/create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
expect(page).to have_content "Default"
name = "dummy-vm"
fill_in "Name", with: name
choose option: "eu-central-h1"
select match: :prefer_exact, text: "Default"
choose option: "ubuntu-jammy"
choose option: "standard-2"
click_button "Create"
expect(page.title).to eq("Ubicloud - #{name}")
expect(page).to have_flash_notice("'#{name}' will be ready in a few minutes")
expect(Vm.count).to eq(1)
expect(Vm.first.project_id).to eq(project.id)
expect(Vm.first.private_subnets.first.id).not_to eq(ps.id)
expect(Vm.first.private_subnets.first.name).to eq("default-#{LocationNameConverter.to_display_name(ps.location)}")
# can create a second vm in the same location and it will use the same subnet
visit "#{project.path}/vm/create"
fill_in "Name", with: "dummy-vm-2"
choose option: "eu-central-h1"
select match: :prefer_exact, text: "Default"
choose option: "ubuntu-jammy"
choose option: "standard-2"
click_button "Create"
expect(page.title).to eq("Ubicloud - dummy-vm-2")
expect(Vm.count).to eq(2)
expect(Vm.find(name: "dummy-vm-2").private_subnets.first.id).to eq(Vm.find(name: "dummy-vm").private_subnets.first.id)
end
it "can not create virtual machine with invalid name" do
project
visit "#{project.path}/vm/create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
fill_in "Name", with: "invalid name"
choose option: "eu-central-h1"
choose option: "ubuntu-jammy"
choose option: "standard-2"
click_button "Create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
expect(page).to have_content "Name must only contain"
expect((find "input[name=name]")["value"]).to eq("invalid name")
end
it "can not create virtual machine with same name" do
project
visit "#{project.path}/vm/create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
fill_in "Name", with: vm.name
choose option: "eu-central-h1"
choose option: "ubuntu-jammy"
choose option: "standard-2"
click_button "Create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
expect(page).to have_flash_error("project_id and location and name is already taken")
end
it "can not create virtual machine if project has no valid payment method" do
expect(Project).to receive(:from_ubid).and_return(project).at_least(:once)
expect(Config).to receive(:stripe_secret_key).and_return("secret_key").at_least(:once)
visit "#{project.path}/vm/create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
expect(page).to have_content "Project doesn't have valid billing information"
fill_in "Name", with: "dummy-vm"
choose option: "eu-central-h1"
choose option: "ubuntu-jammy"
choose option: "standard-2"
click_button "Create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
expect(page).to have_content "Project doesn't have valid billing information"
end
it "can not select invisible location" do
project
visit "#{project.path}/vm/create"
expect(page.title).to eq("Ubicloud - Create Virtual Machine")
expect { choose option: "github-runners" }.to raise_error Capybara::ElementNotFound
end
it "can not create vm in a project when does not have permissions" do
project_wo_permissions
visit "#{project_wo_permissions.path}/vm/create"
expect(page.title).to eq("Ubicloud - Forbidden")
expect(page.status_code).to eq(403)
expect(page).to have_content "Forbidden"
end
end
describe "show" do
it "can show virtual machine details" do
vm
visit "#{project.path}/vm"
expect(page.title).to eq("Ubicloud - Virtual Machines")
expect(page).to have_content vm.name
click_link vm.name, href: "#{project.path}#{vm.path}"
expect(page.title).to eq("Ubicloud - #{vm.name}")
expect(page).to have_content vm.name
end
it "raises forbidden when does not have permissions" do
visit "#{project_wo_permissions.path}#{vm_wo_permission.path}"
expect(page.title).to eq("Ubicloud - Forbidden")
expect(page.status_code).to eq(403)
expect(page).to have_content "Forbidden"
end
it "raises not found when virtual machine not exists" do
visit "#{project.path}/location/eu-central-h1/vm/08s56d4kaj94xsmrnf5v5m3mav"
expect(page.title).to eq("Ubicloud - ResourceNotFound")
expect(page.status_code).to eq(404)
expect(page).to have_content "ResourceNotFound"
end
end
describe "delete" do
it "can delete virtual machine" do
visit "#{project.path}#{vm.path}"
# We send delete request manually instead of just clicking to button because delete action triggered by JavaScript.
# UI tests run without a JavaScript engine.
btn = find "#vm-delete-#{vm.ubid} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(SemSnap.new(vm.id).set?("destroy")).to be true
end
it "can not delete virtual machine when does not have permissions" do
# Give permission to view, so we can see the detail page
AccessControlEntry.create_with_id(project_id: project_wo_permissions.id, subject_id: user.id, action_id: ActionType::NAME_MAP["Vm:view"])
visit "#{project_wo_permissions.path}#{vm_wo_permission.path}"
expect(page.title).to eq "Ubicloud - dummy-vm-2"
expect { find ".delete-btn" }.to raise_error Capybara::ElementNotFound
end
end
describe "restart" do
it "can restart vm" do
visit "#{project.path}#{vm.path}"
expect(page).to have_content "Restart"
click_button "Restart"
expect(page.status_code).to eq(200)
expect(vm.restart_set?).to be true
end
end
end
end