Files
pve-purestorage-plugin/tests/unit/test_token_cache.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

154 lines
4.4 KiB
Perl

#!/usr/bin/env perl
use strict;
use warnings;
use Test::More tests => 16;
use File::Temp qw(tempdir);
use File::Path qw(make_path remove_tree);
use JSON;
# Create temporary cache directory
my $cache_dir = tempdir( CLEANUP => 1 );
# Mock functions from plugin
sub get_token_cache_path {
my ( $storeid, $array_index ) = @_;
my $dir = "$cache_dir/purestorage";
make_path($dir) unless -d $dir;
chmod 0700, $dir;
return "$dir/${storeid}_array${array_index}.json";
}
sub write_token_cache {
my ( $cache_path, $token_data ) = @_;
my $temp_path = "$cache_path.tmp.$$";
open my $fh, '>', $temp_path or die "Cannot write to $temp_path: $!";
print $fh encode_json($token_data);
close $fh;
chmod 0600, $temp_path;
rename $temp_path, $cache_path or die "Cannot rename $temp_path to $cache_path: $!";
}
sub read_token_cache {
my ( $cache_path ) = @_;
return undef unless -f $cache_path;
open my $fh, '<', $cache_path or return undef;
my $content = do { local $/; <$fh> };
close $fh;
return undef unless $content;
my $data = eval { decode_json($content) };
return undef if $@;
return $data;
}
sub is_token_valid {
my ( $token_data, $ttl ) = @_;
return 0 unless defined $token_data;
return 0 unless defined $token_data->{auth_token};
return 0 unless defined $token_data->{created_at};
my $now = time();
my $age = $now - $token_data->{created_at};
my $refresh_threshold = $ttl * 0.8;
return $age < $refresh_threshold;
}
sub cleanup_expired_cache {
my ( $cache_path, $ttl ) = @_;
my $token_data = read_token_cache($cache_path);
return unless $token_data;
my $now = time();
if ( $now > $token_data->{expires_at} ) {
unlink $cache_path;
}
}
# Test 1: Cache directory creation
my $cache_path = get_token_cache_path('pure', 0);
ok( -d "$cache_dir/purestorage", 'Cache directory created' );
# Test 2: Cache directory permissions
my $mode = (stat("$cache_dir/purestorage"))[2] & 0777;
is( $mode, 0700, 'Cache directory has correct permissions (700)' );
# Test 3: Write token cache
my $token_data = {
auth_token => 'test-token-12345',
request_id => 'req-67890',
created_at => time(),
ttl => 3600,
expires_at => time() + 3600
};
write_token_cache($cache_path, $token_data);
ok( -f $cache_path, 'Token cache file created' );
# Test 4: Cache file permissions
$mode = (stat($cache_path))[2] & 0777;
is( $mode, 0600, 'Cache file has correct permissions (600)' );
# Test 5: Read token cache
my $read_data = read_token_cache($cache_path);
ok( defined $read_data, 'Token cache read successfully' );
# Test 6: Verify token data
is( $read_data->{auth_token}, 'test-token-12345', 'Auth token matches' );
is( $read_data->{request_id}, 'req-67890', 'Request ID matches' );
# Test 7: Valid token (fresh)
ok( is_token_valid($read_data, 3600), 'Fresh token is valid' );
# Test 8: Valid token at 79% of TTL
$token_data->{created_at} = time() - (3600 * 0.79);
write_token_cache($cache_path, $token_data);
$read_data = read_token_cache($cache_path);
ok( is_token_valid($read_data, 3600), 'Token at 79% TTL is still valid' );
# Test 9: Invalid token at 81% of TTL
$token_data->{created_at} = time() - (3600 * 0.81);
write_token_cache($cache_path, $token_data);
$read_data = read_token_cache($cache_path);
ok( !is_token_valid($read_data, 3600), 'Token at 81% TTL is invalid' );
# Test 10: Multiple array caches
my $cache_path_1 = get_token_cache_path('pure', 1);
write_token_cache($cache_path_1, $token_data);
ok( -f $cache_path_1, 'Second array cache created' );
isnt( $cache_path, $cache_path_1, 'Different cache files for different arrays' );
# Test 11: Cleanup expired cache
$token_data->{created_at} = time() - 4000;
$token_data->{expires_at} = time() - 400; # Expired
write_token_cache($cache_path, $token_data);
cleanup_expired_cache($cache_path, 3600);
ok( !-f $cache_path, 'Expired cache file removed' );
# Test 12: Read non-existent cache
my $missing_cache = read_token_cache("$cache_dir/nonexistent.json");
is( $missing_cache, undef, 'Non-existent cache returns undef' );
# Test 13: Invalid JSON in cache
my $corrupt_cache = "$cache_dir/purestorage/corrupt.json";
open my $fh, '>', $corrupt_cache;
print $fh "{ invalid json }";
close $fh;
my $corrupt_data = read_token_cache($corrupt_cache);
is( $corrupt_data, undef, 'Corrupt cache returns undef' );
# Test 14: Missing required fields
my $incomplete_data = { auth_token => 'test' }; # Missing created_at
ok( !is_token_valid($incomplete_data, 3600), 'Incomplete token data is invalid' );
done_testing();