Files
ubicloud/spec/lib/validation_spec.rb
Eren Başak e6711245c0 Introduce Kubernetes Cluster Provisioning Logic
In this commit, we introduce two progs to provision a k8s cluster.

KubernetesClusterNexus.assemble call creates a KubernetesCluster object
and manages the creation of control plane nodes using the
ProvisionKubernetesNode prog.

Nodes are created from plain images and k8s components are installed during
the provisioning, increasing the time to provision. Later, we will switch to
dedicated VM images.

Co-authored-by: Eren Başak <eren@ubicloud.com>
Co-authored-by: mohi-kalantari <mohi.kalantari1@gmail.com>
2025-01-28 09:59:38 +02:00

508 lines
19 KiB
Ruby

# frozen_string_literal: true
RSpec.describe Validation do
describe "#validate_name" do
it "valid names" do
[
"abc",
"abc123",
"abc-123",
"123abc",
"abc--123",
"a-b-c-1-2",
"a" * 63
].each do |name|
expect(described_class.validate_name(name)).to be_nil
end
end
it "invalid names" do
[
nil,
"-abc",
"abc-",
"-abc-",
"ABC",
"ABC_123",
"ABC$123",
"a" * 64
].each do |name|
expect { described_class.validate_name(name) }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_vm_size" do
it "valid vm size" do
expect(described_class.validate_vm_size("standard-2", "x64").name).to eq("standard-2")
end
it "invalid vm size" do
expect { described_class.validate_vm_size("standard-3", "x64") }.to raise_error described_class::ValidationFailed
end
it "no IO limits for standard x64" do
io_limits = described_class.validate_vm_size("standard-2", "x64").io_limits
expect(io_limits.max_ios_per_sec).to be_nil
expect(io_limits.max_read_mbytes_per_sec).to be_nil
expect(io_limits.max_write_mbytes_per_sec).to be_nil
end
it "no IO limits for standard arm64" do
io_limits = described_class.validate_vm_size("standard-2", "arm64").io_limits
expect(io_limits.max_ios_per_sec).to be_nil
expect(io_limits.max_read_mbytes_per_sec).to be_nil
expect(io_limits.max_write_mbytes_per_sec).to be_nil
end
it "no IO limits for standard-gpu" do
io_limits = described_class.validate_vm_size("standard-gpu-6", "x64").io_limits
expect(io_limits.max_ios_per_sec).to be_nil
expect(io_limits.max_read_mbytes_per_sec).to be_nil
expect(io_limits.max_write_mbytes_per_sec).to be_nil
end
end
describe "#validate_vm_storage_size" do
it "valid vm storage sizes" do
[
["standard-2", "40"],
["standard-2", "80"],
["standard-4", "160"]
].each do |vm_size, storage_size|
expect(described_class.validate_vm_storage_size(vm_size, "x64", storage_size)).to eq(storage_size.to_f)
end
end
it "invalid vm storage sizes" do
[
["standard-2", "160"],
["standard-2", "37.4"],
["standard-2", ""],
["standard-2", nil],
["standard-5", "40"],
[nil, "40"]
].each do |vm_size, storage_size|
expect { described_class.validate_vm_storage_size(vm_size, "x64", storage_size) }.to raise_error described_class::ValidationFailed
end
end
end
describe "#validate_location" do
it "valid locations" do
[
"hetzner-fsn1",
"github-runners"
].each do |location|
expect(described_class.validate_location(location)).to be_nil
end
end
it "invalid locations" do
[
"hetzner-hel2",
"hetzner-fsn2"
].each do |location|
expect { described_class.validate_location(location) }.to raise_error described_class::ValidationFailed
end
end
end
describe "#validate_os_user_name" do
it "valid os user names" do
[
"abc",
"abc123",
"abc-123",
"abc_123",
"_abc",
"abc-_-123",
"a-b-c-1-2",
"a" * 32
].each do |name|
expect(described_class.validate_os_user_name(name)).to be_nil
end
end
it "invalid os user names" do
[
nil,
"-abc",
"ABC",
"123abc",
"abc$",
"a" * 33
].each do |name|
expect { described_class.validate_os_user_name(name) }.to raise_error described_class::ValidationFailed
end
end
end
describe "#validate_storage_volumes" do
it "succeeds if there's at least one volume" do
expect { described_class.validate_storage_volumes([{encrypted: true}], 0) }.not_to raise_error
end
it "succeeds if rate limits are set" do
expect { described_class.validate_storage_volumes([{encrypted: true, max_ios_per_sec: 100, max_read_mbytes_per_sec: 10, max_write_mbytes_per_sec: 10}], 0) }.not_to raise_error
end
it "fails if no volumes" do
expect { described_class.validate_storage_volumes([], 0) }.to raise_error described_class::ValidationFailed
end
it "fails if boot_disk_index out of range" do
expect { described_class.validate_storage_volumes([{encrypted: true}], -1) }.to raise_error described_class::ValidationFailed
expect { described_class.validate_storage_volumes([{encrypted: true}], 1) }.to raise_error described_class::ValidationFailed
end
it "fails if contains an invalid key" do
expect { described_class.validate_storage_volumes([xyz: 1], 0) }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_minio_username" do
it "valid minio user names" do
[
"abc",
"abc123",
"abc-123",
"abc_123",
"_abc",
"abc-_-123",
"a-b-c-1-2",
"a" * 32
].each do |name|
expect(described_class.validate_minio_username(name)).to be_nil
end
end
it "invalid minio user names" do
[
nil,
"ab",
"-abc",
"ABC",
"123abc",
"abc$",
"a" * 33
].each do |name|
expect { described_class.validate_minio_username(name) }.to raise_error described_class::ValidationFailed
end
end
end
describe "#validate_postgres_size" do
it "valid postgres size" do
expect(described_class.validate_postgres_size("hetzner-fsn1", "standard-2").name).to eq("standard-2")
end
it "invalid postgres size" do
expect { described_class.validate_postgres_size("hetzner-fsn1", "standard-3") }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_postgres_storage_size" do
it "valid postgres storage sizes" do
[
["hetzner-fsn1", "standard-2", "64"],
["hetzner-fsn1", "standard-2", "256"],
["hetzner-fsn1", "standard-4", "512"],
["leaseweb-wdc02", "standard-4", "256"]
].each do |location, pg_size, storage_size|
expect(described_class.validate_postgres_storage_size(location, pg_size, storage_size)).to eq(storage_size.to_f)
end
end
it "invalid postgres storage sizes" do
[
["hetzner-fsn1", "standard-2", "1024"],
["hetzner-fsn1", "standard-2", "37.4"],
["hetzner-fsn1", "standard-2", ""],
["hetzner-fsn1", "standard-2", nil],
["hetzner-fsn1", "standard-5", "128"],
["leaseweb-wdc02,", "standard-4", "1024"],
["hetzner-fsn1", nil, "128"]
].each do |location, pg_size, storage_size|
expect { described_class.validate_postgres_storage_size(location, pg_size, storage_size) }.to raise_error described_class::ValidationFailed
end
end
end
describe "#validate_postgres_ha_type" do
it "valid postgres ha_type" do
[PostgresResource::HaType::NONE, PostgresResource::HaType::ASYNC, PostgresResource::HaType::SYNC].each { |ha_type| expect { described_class.validate_postgres_ha_type(ha_type) }.not_to raise_error }
end
it "invalid postgres ha_type" do
["quorum", "on", "off"].each { |ha_type| expect { described_class.validate_postgres_ha_type(ha_type) }.to raise_error described_class::ValidationFailed }
end
end
describe "#validate_date" do
it "valid date" do
expect(described_class.validate_date("2023-11-30 11:41")).to eq(Time.new(2023, 11, 30, 11, 41, 0, "UTC"))
expect(described_class.validate_date(Time.new(2023, 11, 30, 11, 41))).to eq(Time.new(2023, 11, 30, 11, 41))
end
it "invalid date" do
expect { described_class.validate_date("") }.to raise_error described_class::ValidationFailed, "Validation failed for following fields: date"
expect { described_class.validate_date(nil) }.to raise_error described_class::ValidationFailed, "Validation failed for following fields: date"
expect { described_class.validate_date("invalid-date", "restore_date") }.to raise_error described_class::ValidationFailed, "Validation failed for following fields: restore_date"
end
end
describe "#validate_postgres_superuser_password" do
it "valid password" do
expect { described_class.validate_postgres_superuser_password("Dummy-pass-123", "Dummy-pass-123") }.not_to raise_error
end
it "invalid password" do
expect { described_class.validate_postgres_superuser_password("", "") }.to raise_error(described_class::ValidationFailed)
expect { described_class.validate_postgres_superuser_password("Short1", "Short1") }.to raise_error(described_class::ValidationFailed)
expect { described_class.validate_postgres_superuser_password("NOLOWERCASE123", "NOLOWERCASE123") }.to raise_error(described_class::ValidationFailed)
expect { described_class.validate_postgres_superuser_password("nouppercase123", "nouppercase123") }.to raise_error(described_class::ValidationFailed)
expect { described_class.validate_postgres_superuser_password("nodigitNODIGIT", "nodigitNODIGIT") }.to raise_error(described_class::ValidationFailed)
expect { described_class.validate_postgres_superuser_password("Different12345", "dIFFERENT12345") }.to raise_error(described_class::ValidationFailed)
end
end
describe "#validate_port_range" do
it "valid port range" do
expect(described_class.validate_port_range("1234")).to eq([1234])
expect(described_class.validate_port_range("1234..1235")).to eq([1234, 1235])
expect(described_class.validate_port_range("1234..1234")).to eq([1234, 1234])
end
it "invalid port range" do
expect { described_class.validate_port_range("") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_port_range("65555") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_port_range("0..65555") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_port_range("10..9") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_port_range("65556..65556") }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_port" do
it "valid port" do
expect(described_class.validate_port(:src_port, "1234")).to eq(1234)
end
it "invalid port" do
expect { described_class.validate_port(:src_port, "abc") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_port(:dst_port, "65555") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_port(:dst_port, "-1") }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_cidr" do
it "valid cidr" do
expect { described_class.validate_cidr("0.0.0.0/0") }.not_to raise_error
expect { described_class.validate_cidr("0.0.0.0/1") }.not_to raise_error
expect { described_class.validate_cidr("192.168.1.0/24") }.not_to raise_error
expect { described_class.validate_cidr("255.255.255.255/0") }.not_to raise_error
expect { described_class.validate_cidr("::/0") }.not_to raise_error
expect { described_class.validate_cidr("::1/128") }.not_to raise_error
expect { described_class.validate_cidr("2001:db8::/32") }.not_to raise_error
end
it "invalid cidr" do
expect { described_class.validate_cidr("192.168.1.256/24") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_cidr("10.256.0.0/8") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_cidr("172.16.0.0/33") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_cidr("not_a_cidr") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_cidr("::1/129") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_cidr("::1/::1") }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_boot_image" do
it "valid boot image" do
expect { described_class.validate_boot_image("ubuntu-jammy") }.not_to raise_error
expect { described_class.validate_boot_image("almalinux-9") }.not_to raise_error
end
it "invalid boot image" do
expect { described_class.validate_boot_image("invalid-boot-image") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_boot_image("postgres-ubuntu-2204") }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_short_text" do
it "valid short text" do
expect { described_class.validate_short_text("abcABC123()!?* ", "name") }.not_to raise_error
end
it "invalid short text" do
expect { described_class.validate_short_text("", "name") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_short_text("a" * 256, "name") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_short_text("'", "name") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_short_text("%", "name") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_short_text("~", "name") }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_usage_limit" do
it "valid usage limit" do
expect(described_class.validate_usage_limit("123")).to eq(123)
end
it "invalid usage limit" do
expect { described_class.validate_usage_limit("abc") }.to raise_error described_class::ValidationFailed
expect { described_class.validate_usage_limit("0") }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_cloudflare_turnstile" do
it "no ops when cloudflare site key not configured" do
expect(Config).to receive(:cloudflare_turnstile_site_key).and_return(nil)
expect(described_class.validate_cloudflare_turnstile("cf_response")).to be_nil
end
it "valid cloudflare response" do
expect(Config).to receive(:cloudflare_turnstile_site_key).and_return("cf_site_key")
Excon.stub({url: "https://challenges.cloudflare.com/turnstile/v0/siteverify", method: :post}, {status: 200, body: {"success" => true}.to_json})
expect(described_class.validate_cloudflare_turnstile("cf_response")).to be_nil
end
it "invalid cloudflare response" do
expect(Config).to receive(:cloudflare_turnstile_site_key).and_return("cf_site_key")
Excon.stub({url: "https://challenges.cloudflare.com/turnstile/v0/siteverify", method: :post}, {status: 200, body: {"success" => false, "error-codes" => "123"}.to_json})
expect { described_class.validate_cloudflare_turnstile("cf_response") }.to raise_error described_class::ValidationFailed
end
end
end
describe "#validate_account_name" do
it "valid account names" do
[
"John Doe",
"john doe",
"John Doe-Smith",
"Jøhn Döe",
"John2 Doe",
"J" * 63
].each do |name|
expect(described_class.validate_account_name(name)).to be_nil
end
end
it "invalid account names" do
[
nil,
"",
" John Doe",
"1john Doe",
".john Doe",
"https://example.com",
"Click this link: http://example.com",
"Click this link example.com",
"🚀 emojis not allowed",
"J" * 64
].each do |name|
expect { described_class.validate_account_name(name) }.to raise_error described_class::ValidationFailed
end
end
end
describe "#validate_request_params" do
it "rejects requests without required params" do
expect { described_class.validate_request_params({extra: true}, []) }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_url" do
it "valid account names" do
[
"https://example.com",
"https://example.com:1234"
].each do |url|
expect(described_class.validate_url(url)).to be_nil
end
end
it "invalid account names" do
[
nil,
"",
"1.2.3.4",
"http://example.com",
"https://",
"ftp://example.com"
].each do |url|
expect { described_class.validate_url(url) }.to raise_error described_class::ValidationFailed
end
end
end
describe "#validate_vcpu_quota" do
it "sufficient cpu quota" do
p = instance_double(Project, current_resource_usage: 5, effective_quota_value: 10, quota_available?: true)
expect { described_class.validate_vcpu_quota(p, "VmVCpu", 2) }.not_to raise_error
end
it "insufficient cpu quota" do
p = instance_double(Project, current_resource_usage: 10, effective_quota_value: 10, quota_available?: false)
expect { described_class.validate_vcpu_quota(p, "VmVCpu", 2) }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_load_balancer_stack" do
it "valid load balancer stack" do
expect { described_class.validate_load_balancer_stack("ipv4") }.not_to raise_error
expect { described_class.validate_load_balancer_stack("ipv6") }.not_to raise_error
expect { described_class.validate_load_balancer_stack("dual") }.not_to raise_error
end
it "invalid load balancer stack" do
expect { described_class.validate_load_balancer_stack("invalid") }.to raise_error described_class::ValidationFailed
end
end
describe "#validate_kubernetes_name" do
it "valid names" do
[
"abc",
"abc123",
"abc-123",
"123abc",
"abc--123",
"a-b-c-1-2",
"a" * 40
].each do |name|
expect(described_class.validate_kubernetes_name(name)).to be_nil
end
end
it "invalid names" do
[
nil,
"-abc",
"abc-",
"-abc-",
"ABC",
"ABC_123",
"ABC$123",
"a" * 42
].each do |name|
expect { described_class.validate_kubernetes_name(name) }.to raise_error described_class::ValidationFailed
end
end
end
describe "#validate_kubernetes_cp_node_count" do
it "valid numbers" do
[1, 3].each do |count|
expect(described_class.validate_kubernetes_cp_node_count(count)).to be_nil
end
end
it "invalid numbers" do
[-37, 0, 2, 4, 5].each do |count|
expect { described_class.validate_kubernetes_cp_node_count(count) }.to raise_error described_class::ValidationFailed
end
end
end
end