Files
pve-purestorage-plugin/tests/unit/test_command_validation.t
T
Timur Kumakbayev f9f8eed94b Active Cluster Support, Pod Quota Fixes,Token Cache and CI Improvements (#81)
feat: add Active Cluster support and improve plugin stability

## Major Features

### Active Cluster Support (Experimental) (#42)
- Add support for multiple PureStorage arrays in Active Cluster configuration
- Automatic volume connection on all arrays for high availability
- Configure via comma-separated addresses and tokens

### Pod Quota Limit Support (#69)
- Add proper handling of pod quota_limit parameter
- Use quota_limit when set, fall back to array capacity when unlimited
- Fix capacity reporting for pods with quotas

### Session Token Caching
- Implement session token caching in /etc/pve/priv/purestorage/
- Automatic token refresh at 80% of TTL to prevent expiration
- In-memory and file-based caching with jitter to prevent thundering herd

### Debug Logging
- Add configurable debug logging with 4 levels (0-3)
- Support both config parameter and PURESTORAGE_DEBUG environment variable

## Bug Fixes
- Fix volume deletion when connected to multiple hosts
- Query all connections before destroy
- Disconnect from all hosts on all arrays
- Prevents "Cannot destroy volume because it is currently connected" error

## CI/CD Improvements
- Add workflow_dispatch trigger to checks workflow
- Add option to check all files or only changed files
- Add comprehensive markdown linting with markdownlint-cli2

## Testing
- Add token caching tests (tests/token_cache_test.pl)
- Test token validation, expiration, and race conditions
- Test cleanup of expired cache files

## Refactoring (#72)
- Refactor volume removal logic to handle multiple host connections
- Improve device cleanup sequence (LVM, partitions, multipath)
- Extract connection querying logic before volume destruction
- Refactor CI/CD workflows for better maintainability and flexibility
- Improve error handling and logging throughout the plugin
- Enhance Active Cluster support with proper multi-array operations

## Code Formatting
- Format all Perl files with perltidy using .perltidyrc configuration
- Ensure consistent code style across the codebase
- Fix formatting issues in PureStoragePlugin.pm and test files

## Documentation
- Update README with new functionality
2026-01-17 17:20:28 +05:00

129 lines
3.2 KiB
Perl

#!/usr/bin/env perl
use strict;
use warnings;
use Test::More tests => 9;
use File::Temp qw(tempdir);
use File::Path qw(make_path);
# Mock the command paths for testing
my $test_dir = tempdir( CLEANUP => 1 );
my $cmd = {
multipath => "$test_dir/multipath",
multipathd => "$test_dir/multipathd",
blockdev => "$test_dir/blockdev",
dmsetup => "$test_dir/dmsetup",
kpartx => "$test_dir/kpartx"
};
# Track if commands were checked
my $commands_checked = 0;
sub ensure_commands_checked {
return if $commands_checked;
check_commands();
$commands_checked = 1;
}
sub get_command_path {
my ( $name ) = @_;
ensure_commands_checked();
my $path = $cmd->{ $name };
if ( !defined $path ) {
die "Error :: Unknown command '$name'\n";
}
if ( !-x $path ) {
die "Error :: Command '$name' not found or not executable at '$path'\n";
}
return $path;
}
sub check_commands {
my @missing;
foreach my $name ( keys %$cmd ) {
my $path = $cmd->{ $name };
if ( !-x $path ) {
push @missing, "$name ($path)";
}
}
if ( @missing ) {
note "Warning :: The following commands are not available:";
note " - $_" foreach @missing;
}
return scalar @missing == 0;
}
# Test 1: Unknown command should die
eval { get_command_path('unknown_command') };
like( $@, qr/Unknown command/, 'Unknown command throws error' );
# Test 2: Non-existent command should die
eval { get_command_path('multipath') };
like( $@, qr/not found or not executable/, 'Non-existent command throws error' );
# Test 3: Create executable commands
foreach my $name ( keys %$cmd ) {
my $path = $cmd->{ $name };
open my $fh, '>', $path or die "Cannot create $path: $!";
print $fh "#!/bin/sh\necho 'test'\n";
close $fh;
chmod 0755, $path;
}
# Reset check flag to re-run validation
$commands_checked = 0;
# Test 4: Valid command should return path
my $path = get_command_path('multipath');
is( $path, $cmd->{multipath}, 'Valid command returns correct path' );
# Test 5: Commands should be checked only once
my $check_count = 0;
{
no warnings 'redefine';
my $original = \&check_commands;
*check_commands = sub {
$check_count++;
$original->();
};
$commands_checked = 0;
get_command_path('multipath');
get_command_path('dmsetup');
get_command_path('kpartx');
}
is( $check_count, 1, 'check_commands called only once' );
# Test 6: All commands should be validated
my $result = check_commands();
ok( $result, 'All commands are valid' );
# Test 7: Make one command non-executable
chmod 0644, $cmd->{kpartx};
$result = check_commands();
ok( !$result, 'Non-executable command detected' );
# Test 8: Check that non-executable command fails
eval { get_command_path('kpartx') };
like( $@, qr/not executable/, 'Non-executable command throws error' );
# Test 9: Restore executable and verify it works
chmod 0755, $cmd->{kpartx};
$commands_checked = 0;
$path = get_command_path('kpartx');
is( $path, $cmd->{kpartx}, 'Restored command works' );
# Test 10: Verify all expected commands exist in hash
my @expected = qw(multipath multipathd blockdev dmsetup kpartx);
my @actual = sort keys %$cmd;
is_deeply( \@actual, [sort @expected], 'All expected commands present in hash' );
done_testing();