App-FuguVM

 view release on metacpan or  search on metacpan

t/fuguvm/config.t  view on Meta::CPAN

    open my $gh, '>', "$homedir/.fuguvmrc" or die $!;
    print $gh "distfile_cache 1K\n";
    print $gh "verify yes\n";
    print $gh "signify_dir /global/keys\n";
    close $gh;

    my $config = $write->("distfile_cache 2K\n"
	. "verify no\n"
	. "signify_dir /project/keys\n");
    is($config->distfile_cache, 2048,
	'the project distfile_cache wins over the global one');
    is($config->verify, 0, 'the project verify wins over the global one');
    is($config->signify_dir, '/project/keys',
	'the project signify_dir wins over the global one');

    # The global file serves without a project value
    $config = $write->("cache_dir /tmp\n");
    is($config->distfile_cache, 1024, 'the global distfile_cache serves');
    is($config->verify, 1, 'the global verify serves');
    is($config->signify_dir, '/global/keys', 'the global signify_dir serves');
}

# The verify directive: the default is on, and it reads the yes/no
# spellings like image_cache
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    local $ENV{HOME} = $homedir;
    make_path("$tmpdir/.fuguvm/vms");

    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "vm test {\n}\n";
    close $fh;
    my $config = App::FuguVM::Config->new($tmpdir);
    is($config->verify, 1, 'verify defaults to 1');
    is($config->load_vm('test')->{verify}, 1,
	'and the default reaches the VM hash');

    open $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "verify no\n";
    print $fh "vm test {\n}\n";
    close $fh;
    $config = App::FuguVM::Config->new($tmpdir);
    is($config->verify, 0, 'verify no reads as 0');
    is($config->load_vm('test')->{verify}, 0, 'and reaches the VM hash');
}

# The signify_dir directive: the tilde, the project-relative path,
# the VM hash, and the refusal of a value that is not a directory
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    local $ENV{HOME} = $homedir;
    make_path("$tmpdir/.fuguvm/vms", "$tmpdir/keys", "$homedir/keys");

    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "signify_dir ~/keys\n";
    print $fh "vm test {\n}\n";
    close $fh;
    my $config = App::FuguVM::Config->new($tmpdir);
    is($config->signify_dir, "$homedir/keys", 'signify_dir expands a tilde');
    is($config->load_vm('test')->{signify_dir}, "$homedir/keys",
	'and load_vm carries it into the VM hash');

    open $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "signify_dir keys\n";
    print $fh "vm test {\n}\n";
    close $fh;
    $config = App::FuguVM::Config->new($tmpdir);
    like($config->signify_dir, qr{\Q/keys\E$},
	'a relative path resolves against the project root');
    like($config->signify_dir, qr{^/}, 'and the result is absolute');

    # A value that is not a directory is a refusal at the boundary
    open $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "vm test {\n";
    print $fh "    signify_dir absent-keys\n";
    print $fh "}\n";
    close $fh;
    $config = App::FuguVM::Config->new($tmpdir);
    is($config->load_vm('test'), undef,
	'an absent signify_dir makes load_vm return undef');
    like($config->error, qr/absent-keys/, 'and error names the path');

    # The distfile cap is a project fact, and load_vm injects it
    open $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "distfile_cache 4G\n";
    print $fh "vm test {\n}\n";
    close $fh;
    $config = App::FuguVM::Config->new($tmpdir);
    is($config->load_vm('test')->{distfile_cache}, 4 * 1024**3,
	'load_vm injects the distfile cap of the project');

    # A cap in a VM block does not apply, and the operator hears
    # about it
    open $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "distfile_cache 4G\n";
    print $fh "vm test {\n";
    print $fh "    distfile_cache 8G\n";
    print $fh "}\n";
    close $fh;
    my $warning = '';
    my $loaded;
    {
	local *STDERR;
	open STDERR, '>', \$warning or die "capture stderr: $!";
	$loaded = App::FuguVM::Config->new($tmpdir)->load_vm('test');
    }
    is($loaded->{distfile_cache}, 4 * 1024**3,
	'the project cap wins over a cap in a VM block');
    like($warning, qr/distfile_cache/,
	'and the loader warns about the block value');
}

# The parser normalizes image_cache inside a vm block like the global
# directive
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    local $ENV{HOME} = $homedir;
    make_path("$tmpdir/.fuguvm/vms");

    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "image_cache yes\n";
    print $fh "vm test {\n";
    print $fh "    image_cache no\n";
    print $fh "}\n";
    close $fh;

    my $vm = App::FuguVM::Config->new($tmpdir)->load_vm('test');
    is($vm->{image_cache}, 0,
	'a VM block switches its own image cache off, as a number');
}

# A VM without any cache_dir configured still gets the default
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    local $ENV{HOME} = $homedir;
    make_path("$tmpdir/.fuguvm/vms");

    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "vm test {\n}\n";
    close $fh;

    my $config = App::FuguVM::Config->new($tmpdir);
    my $vm = $config->load_vm('test');

    is($vm->{cache_dir}, "$homedir/.cache/fuguvm",
	'default cache_dir is injected and tilde-expanded');
}

# Test ssh_pubkey from project config
{
    my $tmpdir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");
    
    # Create a config with ssh_pubkey at the project root
    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "ssh_pubkey ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI test\@example\n";
    close $fh;
    
    my $config = App::FuguVM::Config->new($tmpdir);
    like($config->ssh_pubkey, qr/^ssh-ed25519/, 'ssh_pubkey parsed from project config');
}

# Test ssh_pubkey from global config fallback
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");
    
    # No ssh_pubkey in project config
    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "default_vm test\n";
    close $fh;
    
    # Create a global config with ssh_pubkey
    open my $gh, '>', "$homedir/.fuguvmrc";
    print $gh "ssh_pubkey ssh-rsa AAAAB3NzaC1 global\@test\n";
    close $gh;
    
    local $ENV{HOME} = $homedir;
    my $config = App::FuguVM::Config->new($tmpdir);
    like($config->ssh_pubkey, qr/^ssh-rsa/, 'ssh_pubkey falls back to global config');
}

# Test ssh_pubkey included in VM config
{
    my $tmpdir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");
    
    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "ssh_pubkey ssh-ed25519 TESTKEY test\@vm\n";
    print $fh "\n";
    print $fh "vm \"test\" {\n";
    print $fh "    memory 2048\n";
    print $fh "}\n";
    close $fh;
    
    my $config = App::FuguVM::Config->new($tmpdir);
    my $vm = $config->load_vm('test');
    is($vm->{ssh_pubkey}, 'ssh-ed25519 TESTKEY test@vm', 'ssh_pubkey included in VM config');
}

# Test project config overrides global config
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");

t/fuguvm/config.t  view on Meta::CPAN

    print $fh "# This is a comment\n";
    print $fh "   \n";
    print $fh "default_vm test  # inline comment\n";
    print $fh "  cache_dir   /path/with/spaces   \n";
    close $fh;
    
    my $config = App::FuguVM::Config->new($tmpdir);
    is($config->default_vm, 'test', 'inline comments stripped');
    is($config->cache_dir, '/path/with/spaces', 'whitespace trimmed');
}

# Test data_dir accessor
{
    my $tmpdir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");
    
    open my $fh, '>', "$tmpdir/.fuguvmrc";
    close $fh;
    
    my $config = App::FuguVM::Config->new($tmpdir);
    is($config->{data_dir}, "$tmpdir/.fuguvm", 'data_dir set correctly');
}

# Test state_dir default
{
    my $tmpdir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");
    
    open my $fh, '>', "$tmpdir/.fuguvmrc";
    close $fh;
    
    my $config = App::FuguVM::Config->new($tmpdir);
    is($config->state_dir, "$tmpdir/.fuguvm/state", 'state_dir defaults to .fuguvm/state');
}

# Test state_dir from config
{
    my $tmpdir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");
    
    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "state_dir /custom/state\n";
    close $fh;
    
    my $config = App::FuguVM::Config->new($tmpdir);
    is($config->state_dir, '/custom/state', 'state_dir from config');
}

# Test cache_dir tilde expansion
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");
    
    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "cache_dir ~/cache/fuguvm\n";
    close $fh;
    
    local $ENV{HOME} = $homedir;
    my $config = App::FuguVM::Config->new($tmpdir);
    is($config->cache_dir, "$homedir/cache/fuguvm", 'cache_dir expands tilde');
}

# Test VM block in global config
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");
    
    # VM defined in global config
    open my $gh, '>', "$homedir/.fuguvmrc";
    print $gh "vm \"shared\" {\n";
    print $gh "    memory 1024\n";
    print $gh "    version 7.8\n";
    print $gh "}\n";
    close $gh;
    
    open my $fh, '>', "$tmpdir/.fuguvmrc";
    close $fh;
    
    local $ENV{HOME} = $homedir;
    my $config = App::FuguVM::Config->new($tmpdir);
    my $vm = $config->load_vm('shared');
    
    ok(defined $vm, 'VM loaded from global config');
    is($vm->{memory}, 1024, 'VM memory from global config');
}

# Test project VM overrides global VM
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    make_path("$tmpdir/.fuguvm/vms");
    
    # VM in global config
    open my $gh, '>', "$homedir/.fuguvmrc";
    print $gh "vm \"test\" {\n";
    print $gh "    memory 1024\n";
    print $gh "}\n";
    close $gh;
    
    # Same VM name in project config with different settings
    open my $fh, '>', "$tmpdir/.fuguvmrc";
    print $fh "vm \"test\" {\n";
    print $fh "    memory 4096\n";
    print $fh "}\n";
    close $fh;
    
    local $ENV{HOME} = $homedir;
    my $config = App::FuguVM::Config->new($tmpdir);
    my $vm = $config->load_vm('test');
    
    is($vm->{memory}, 4096, 'project VM config overrides global');
}

# The bind_address directive: the merge, the fallback chain, the
# default, and the validation at the configuration boundary
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    local $ENV{HOME} = $homedir;

t/fuguvm/config.t  view on Meta::CPAN


    ok($ports->{2300} && $ports->{4500},
	'declared_ports holds the fixed ports of a project block');
    ok($ports->{2400} && $ports->{4600},
	'and the fixed ports of a global block');
    ok($ports->{2222} && $ports->{4444},
	'a declaration that omits a directive holds the default port');
    ok($ports->{2500}, 'a vms/ file counts too');
    ok(!$ports->{auto}, 'the word auto is not a port');

    is_deeply($config->load_vm('fleet')->{declared_ports}, $ports,
	'load_vm folds the set into the per-VM configuration');
}

# The three image-lifecycle directives: the resolution, the derived
# install_mode, and each refusal at the configuration boundary
{
    my $tmpdir = tempdir(CLEANUP => 1);
    my $homedir = tempdir(CLEANUP => 1);
    local $ENV{HOME} = $homedir;
    make_path("$tmpdir/.fuguvm/vms");

    # The files that the directives point at
    for my $seed (["$tmpdir/install.conf", "System hostname = image\n"],
	["$tmpdir/base.qcow2", 'not a real image'],
	["$homedir/password", "secret\n"]) {
	open my $fh, '>', $seed->[0] or die $!;
	print $fh $seed->[1];
	close $fh;
    }

    my $write = sub ($body) {
	open my $fh, '>', "$tmpdir/.fuguvmrc" or die $!;
	print $fh $body;
	close $fh;
	return App::FuguVM::Config->new($tmpdir);
    };

    # install_mode derives from the directives; there is no
    # install_mode directive
    my $config = $write->("vm \"plain\" {\n}\n");
    is($config->load_vm('plain')->{install_mode}, 'expect',
	'no directive derives the expect mode');

    $config = $write->("vm \"auto\" {\n    autoinstall install.conf\n}\n");
    my $vm = $config->load_vm('auto');
    is($vm->{install_mode}, 'autoinstall',
	'autoinstall derives the autoinstall mode');
    is($vm->{autoinstall}, "$tmpdir/install.conf",
	'a relative path resolves against the project root');

    $config = $write->("vm \"import\" {\n    base_disk base.qcow2\n}\n");
    $vm = $config->load_vm('import');
    is($vm->{install_mode}, 'import', 'base_disk derives the import mode');
    is($vm->{base_disk}, "$tmpdir/base.qcow2",
	'and its path resolves the same way');

    $config = $write->(
	"vm \"tilde\" {\n    root_password_file ~/password\n}\n");
    is($config->load_vm('tilde')->{root_password_file},
	"$homedir/password", 'a leading tilde expands');

    # An absent file is a refusal that names the resolved path
    $config = $write->("vm \"gone\" {\n    autoinstall absent.conf\n}\n");
    is($config->load_vm('gone'), undef,
	'an absent autoinstall file makes load_vm return undef');
    like($config->error, qr{\Q$tmpdir/absent.conf\E},
	'and error names the resolved path');

    $config = $write->("vm \"gone\" {\n    base_disk absent.qcow2\n}\n");
    is($config->load_vm('gone'), undef,
	'an absent base_disk file behaves the same way');
    like($config->error, qr{\Q$tmpdir/absent.qcow2\E},
	'and error names the resolved path');

    # Two origins contradict each other
    $config = $write->("vm \"both\" {\n"
	. "    autoinstall install.conf\n"
	. "    base_disk base.qcow2\n"
	. "}\n");
    is($config->load_vm('both'), undef,
	'autoinstall with base_disk makes load_vm return undef');
    like($config->error, qr/autoinstall/, 'error names one directive');
    like($config->error, qr/base_disk/, 'and the other');

    # An imported base lives in the cache, so the cache must be on
    $config = $write->("vm \"import\" {\n"
	. "    base_disk base.qcow2\n"
	. "    image_cache no\n"
	. "}\n");
    is($config->load_vm('import'), undef,
	'base_disk with image_cache no makes load_vm return undef');
    like($config->error, qr/image cache/, 'and error names the cause');

    # ssh_pubkey without root_password_file refuses outside the
    # expect mode, because the tool cannot authenticate
    for my $origin ('autoinstall install.conf', 'base_disk base.qcow2') {
	$config = $write->("ssh_pubkey ssh-ed25519 KEY test\@host\n"
	    . "vm \"keyed\" {\n    $origin\n}\n");
	is($config->load_vm('keyed'), undef,
	    "ssh_pubkey with no root_password_file refuses ($origin)");
	like($config->error, qr/root_password_file/,
	    'and error names one remedy');
	like($config->error, qr/ssh_pubkey/, 'and the other');
    }

    $config = $write->("ssh_pubkey ssh-ed25519 KEY test\@host\n"
	. "vm \"keyed\" {\n"
	. "    autoinstall install.conf\n"
	. "    root_password_file ~/password\n"
	. "}\n");
    ok(defined $config->load_vm('keyed'),
	'root_password_file satisfies the refusal');

    $config = $write->("ssh_pubkey ssh-ed25519 KEY test\@host\n"
	. "vm \"keyed\" {\n}\n");
    ok(defined $config->load_vm('keyed'),
	'the expect mode needs no password file');
    is($config->error, undef, 'and error returns undef after it');
}



( run in 1.816 second using v1.01-cache-2.11-cpan-a49fcb8fa48 )