Plugin(apps::redis::sentinel and database::redis) - Custom(cli): add certs and key parameters (#5674)

Co-authored-by: Mathias Top <mathias.top@epitech.eu>
This commit is contained in:
Sylvain Cresto 2025-08-11 12:58:30 +02:00 committed by GitHub
parent cfba5998d6
commit 5158211087
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 194 additions and 29 deletions

View File

@ -49,6 +49,8 @@ sub new {
'password:s' => { name => 'password' }, 'password:s' => { name => 'password' },
'tls' => { name => 'tls' }, 'tls' => { name => 'tls' },
'cacert:s' => { name => 'cacert' }, 'cacert:s' => { name => 'cacert' },
'cert:s' => { name => 'cert' },
'key:s' => { name => 'key' },
'insecure' => { name => 'insecure' }, 'insecure' => { name => 'insecure' },
'timeout:s' => { name => 'timeout' } 'timeout:s' => { name => 'timeout' }
}); });
@ -73,15 +75,18 @@ sub set_defaults {}
sub check_options { sub check_options {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->{ssh_hostname} = defined($self->{option_results}->{ssh_hostname}) && $self->{option_results}->{ssh_hostname} ne '' ? $self->{option_results}->{ssh_hostname} : ''; $self->{ssh_hostname} = $self->{option_results}->{ssh_hostname} // '';
$self->{server} = defined($self->{option_results}->{server}) && $self->{option_results}->{server} ne '' ? $self->{option_results}->{server} : ''; $self->{server} = $self->{option_results}->{server} // '';
$self->{port} = defined($self->{option_results}->{port}) && $self->{option_results}->{port} ne '' ? $self->{option_results}->{port} : 26379; $self->{port} = $self->{option_results}->{port} || 26379;
$self->{username} = defined($self->{option_results}->{username}) && $self->{option_results}->{username} ne '' ? $self->{option_results}->{username} : ''; $self->{username} = $self->{option_results}->{username} // '';
$self->{password} = defined($self->{option_results}->{password}) && $self->{option_results}->{password} ne '' ? $self->{option_results}->{password} : ''; $self->{password} = $self->{option_results}->{password} // '';
$self->{timeout} = defined($self->{option_results}->{timeout}) && $self->{option_results}->{timeout} =~ /(\d+)/ ? $1 : 10; $self->{timeout} = defined($self->{option_results}->{timeout}) && $self->{option_results}->{timeout} =~ /(\d+)/ ? $1 : 10;
$self->{tls} = defined($self->{option_results}->{tls}) ? 1 : 0;
$self->{insecure} = defined($self->{option_results}->{insecure}) ? 1 : 0; $self->{insecure} = defined($self->{option_results}->{insecure}) ? 1 : 0;
$self->{cacert} = defined($self->{option_results}->{cacert}) && $self->{option_results}->{cacert} ne '' ? $self->{option_results}->{cacert} : ''; $self->{cacert} = $self->{option_results}->{cacert} // '';
$self->{cert} = $self->{option_results}->{cert} // '';
$self->{key} = $self->{option_results}->{key} // '';
# --tls is implied by --key and --cert
$self->{tls} = ($self->{cert} ne '' || $self->{key} ne '' || defined($self->{option_results}->{tls})) ? 1 : 0;
if ($self->{server} eq '') { if ($self->{server} eq '') {
$self->{output}->add_option_msg(short_msg => 'Need to specify --server option.'); $self->{output}->add_option_msg(short_msg => 'Need to specify --server option.');
@ -159,6 +164,8 @@ sub get_extra_options {
my $options = ''; my $options = '';
$options .= ' --tls' if ($self->{tls} == 1); $options .= ' --tls' if ($self->{tls} == 1);
$options .= " --cacert '" . $self->{cacert} . "'" if ($self->{cacert} ne ''); $options .= " --cacert '" . $self->{cacert} . "'" if ($self->{cacert} ne '');
$options .= " --cert '" . $self->{cert} . "'" if ($self->{cert} ne '');
$options .= " --key '" . $self->{key} . "'" if ($self->{key} ne '');
$options .= ' --insecure' if ($self->{insecure} == 1); $options .= ' --insecure' if ($self->{insecure} == 1);
$options .= " --user '" . $self->{username} . "'" if ($self->{username} ne ''); $options .= " --user '" . $self->{username} . "'" if ($self->{username} ne '');
$options .= " -a '" . $self->{password} . "'" if ($self->{password} ne ''); $options .= " -a '" . $self->{password} . "'" if ($self->{password} ne '');
@ -221,11 +228,20 @@ Sentinel port (default: 26379).
=item B<--tls> =item B<--tls>
Establish a secure TLS connection (redis-cli >= 6.x mandatory). Establish a secure TLS connection (redis-cli >= 6.x mandatory).
--tls is automatically enabled when --cert or --key are used.
=item B<--cacert> =item B<--cacert>
CA Certificate file to verify with (redis-cli >= 6.x mandatory). CA Certificate file to verify with (redis-cli >= 6.x mandatory).
=item B<--cert>
Client certificate to authenticate with (redis-cli >= 6.x mandatory).
=item B<--key>
Private key file to authenticate with (redis-cli >= 6.x mandatory).
=item B<--insecure> =item B<--insecure>
Allow insecure TLS connection by skipping cert validation (since redis-cli 6.2.0). Allow insecure TLS connection by skipping cert validation (since redis-cli 6.2.0).

View File

@ -52,6 +52,8 @@ sub new {
'service:s' => { name => 'service' }, 'service:s' => { name => 'service' },
'tls' => { name => 'tls' }, 'tls' => { name => 'tls' },
'cacert:s' => { name => 'cacert' }, 'cacert:s' => { name => 'cacert' },
'cert:s' => { name => 'cert' },
'key:s' => { name => 'key' },
'insecure' => { name => 'insecure' }, 'insecure' => { name => 'insecure' },
'timeout:s' => { name => 'timeout' } 'timeout:s' => { name => 'timeout' }
}); });
@ -76,31 +78,34 @@ sub set_defaults {}
sub check_options { sub check_options {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->{ssh_hostname} = defined($self->{option_results}->{ssh_hostname}) && $self->{option_results}->{ssh_hostname} ne '' ? $self->{option_results}->{ssh_hostname} : ''; $self->{ssh_hostname} = $self->{option_results}->{ssh_hostname} // '';
$self->{server} = defined($self->{option_results}->{server}) && $self->{option_results}->{server} ne '' ? $self->{option_results}->{server} : ''; $self->{server} = $self->{option_results}->{server} // '';
$self->{port} = defined($self->{option_results}->{port}) && $self->{option_results}->{port} ne '' ? $self->{option_results}->{port} : 6379; $self->{port} = $self->{option_results}->{port} || 6379;
$self->{sentinel_port} = defined($self->{option_results}->{sentinel_port}) && $self->{option_results}->{sentinel_port} =~ /(\d+)/ ? $1 : 26379; $self->{sentinel_port} = $self->{option_results}->{sentinel_port} && $self->{option_results}->{sentinel_port} =~ /(\d+)/ ? $1 : 26379;
$self->{username} = defined($self->{option_results}->{password}) && $self->{option_results}->{username} ne '' ? $self->{option_results}->{username} : ''; $self->{username} = $self->{option_results}->{username} // '';
$self->{password} = defined($self->{option_results}->{password}) && $self->{option_results}->{password} ne '' ? $self->{option_results}->{password} : ''; $self->{password} = $self->{option_results}->{password} // '';
$self->{timeout} = defined($self->{option_results}->{timeout}) && $self->{option_results}->{timeout} =~ /(\d+)/ ? $1 : 10; $self->{timeout} = defined($self->{option_results}->{timeout}) && $self->{option_results}->{timeout} =~ /(\d+)/ ? $1 : 10;
$self->{tls} = defined($self->{option_results}->{tls}) ? 1 : 0;
$self->{insecure} = defined($self->{option_results}->{insecure}) ? 1 : 0; $self->{insecure} = defined($self->{option_results}->{insecure}) ? 1 : 0;
$self->{cacert} = defined($self->{option_results}->{cacert}) && $self->{option_results}->{cacert} ne '' ? $self->{option_results}->{cacert} : ''; $self->{cacert} = $self->{option_results}->{cacert} // '';
$self->{cert} = $self->{option_results}->{cert} // '';
$self->{key} = $self->{option_results}->{key} // '';
# --tls is implied by --key and --cert
$self->{tls} = ($self->{cert} ne '' || $self->{key} ne '' || defined($self->{option_results}->{tls})) ? 1 : 0;
$self->{sentinel} = []; $self->{sentinel} = [];
if (defined($self->{option_results}->{sentinel})) { if (defined($self->{option_results}->{sentinel})) {
foreach my $addr (@{$self->{option_results}->{sentinel}}) { foreach my $addr (@{$self->{option_results}->{sentinel}}) {
next if ($addr eq ''); next if ($addr eq '');
push @{$self->{sentinel}}, $addr . ($self->{sentinel_port} ne '' ? ':' . $self->{sentinel_port} : '') push @{$self->{sentinel}}, $addr . ':' . $self->{sentinel_port};
} }
} }
$self->{service} = defined($self->{option_results}->{service}) && $self->{option_results}->{service} ne '' ? $self->{option_results}->{service} : ''; $self->{service} = $self->{option_results}->{service} // '';
if ($self->{server} eq '' && scalar(@{$self->{sentinel}}) <= 0) { if ($self->{server} eq '' && not @{$self->{sentinel}}) {
$self->{output}->add_option_msg(short_msg => 'Need to specify --server or --sentinel option.'); $self->{output}->add_option_msg(short_msg => 'Need to specify --server or --sentinel option.');
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
if (scalar(@{$self->{sentinel}}) > 0 && $self->{service} eq '') { if (@{$self->{sentinel}} && $self->{service} eq '') {
$self->{output}->add_option_msg(short_msg => 'Need to specify --service option.'); $self->{output}->add_option_msg(short_msg => 'Need to specify --service option.');
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
@ -172,6 +177,8 @@ sub get_extra_options {
my $options = ''; my $options = '';
$options .= ' --tls' if ($self->{tls} == 1); $options .= ' --tls' if ($self->{tls} == 1);
$options .= " --cacert '" . $self->{cacert} . "'" if ($self->{cacert} ne ''); $options .= " --cacert '" . $self->{cacert} . "'" if ($self->{cacert} ne '');
$options .= " --cert '" . $self->{cert} . "'" if ($self->{cert} ne '');
$options .= " --key '" . $self->{key} . "'" if ($self->{key} ne '');
$options .= ' --insecure' if ($self->{insecure} == 1); $options .= ' --insecure' if ($self->{insecure} == 1);
$options .= " --user '" . $self->{username} . "'" if ($self->{username} ne ''); $options .= " --user '" . $self->{username} . "'" if ($self->{username} ne '');
$options .= " -a '" . $self->{password} . "'" if ($self->{password} ne ''); $options .= " -a '" . $self->{password} . "'" if ($self->{password} ne '');
@ -185,6 +192,7 @@ sub sentinels_get_master {
foreach my $addr (@{$self->{sentinel}}) { foreach my $addr (@{$self->{sentinel}}) {
my ($sentinel_host, $sentinel_port) = split(/:/, $addr); my ($sentinel_host, $sentinel_port) = split(/:/, $addr);
my $command_options = "-h '" . $sentinel_host . "' -p " . (defined($sentinel_port) ? $sentinel_port : 26379); my $command_options = "-h '" . $sentinel_host . "' -p " . (defined($sentinel_port) ? $sentinel_port : 26379);
$command_options .= $self->get_extra_options();
$command_options .= ' --no-raw'; $command_options .= ' --no-raw';
$command_options .= ' sentinel get-master-addr-by-name ' . $self->{service}; $command_options .= ' sentinel get-master-addr-by-name ' . $self->{service};
my ($stdout, $exit_code) = $self->execute_command( my ($stdout, $exit_code) = $self->execute_command(
@ -209,7 +217,7 @@ sub get_info {
my ($self, %options) = @_; my ($self, %options) = @_;
my $command_options; my $command_options;
if (scalar(@{$self->{sentinel}}) > 0) { if (@{$self->{sentinel}}) {
my ($host, $port) = $self->sentinels_get_master(); my ($host, $port) = $self->sentinels_get_master();
$command_options = "-h '" . $host . "' -p " . $port; $command_options = "-h '" . $host . "' -p " . $port;
} else { } else {
@ -264,11 +272,20 @@ Redis port (default: 6379).
=item B<--tls> =item B<--tls>
Establish a secure TLS connection (redis-cli >= 6.x mandatory). Establish a secure TLS connection (redis-cli >= 6.x mandatory).
--tls is automatically enabled when --cert or --key are used.
=item B<--cacert> =item B<--cacert>
CA Certificate file to verify with (redis-cli >= 6.x mandatory). CA Certificate file to verify with (redis-cli >= 6.x mandatory).
=item B<--cert>
Client certificate to authenticate with (redis-cli >= 6.x mandatory).
=item B<--key>
Private key file to authenticate with (redis-cli >= 6.x mandatory).
=item B<--insecure> =item B<--insecure>
Allow insecure TLS connection by skipping cert validation (since redis-cli 6.2.0). Allow insecure TLS connection by skipping cert validation (since redis-cli 6.2.0).

View File

@ -48,7 +48,11 @@ sub new {
'password:s' => { name => 'password' }, 'password:s' => { name => 'password' },
'sentinel:s@' => { name => 'sentinel' }, 'sentinel:s@' => { name => 'sentinel' },
'sentinel-port:s' => { name => 'sentinel_port' }, 'sentinel-port:s' => { name => 'sentinel_port' },
'service:s' => { name => 'service' } 'service:s' => { name => 'service' },
'tls' => { name => 'tls' },
'cacert:s' => { name => 'cacert' },
'cert:s' => { name => 'cert' },
'key:s' => { name => 'key' }
}); });
} }
$options{options}->add_help(package => __PACKAGE__, sections => 'REDIS OPTIONS', once => 1); $options{options}->add_help(package => __PACKAGE__, sections => 'REDIS OPTIONS', once => 1);
@ -69,11 +73,14 @@ sub set_defaults {}
sub check_options { sub check_options {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->{server} = defined($self->{option_results}->{server}) && $self->{option_results}->{server} ne '' ? $self->{option_results}->{server} : ''; $self->{server} = $self->{option_results}->{server} // '';
$self->{port} = defined($self->{option_results}->{port}) && $self->{option_results}->{port} =~ /(\d+)/ ? $1 : 6379; $self->{port} = defined($self->{option_results}->{port}) && $self->{option_results}->{port} =~ /(\d+)/ ? $1 : 6379;
$self->{sentinel_port} = defined($self->{option_results}->{sentinel_port}) && $self->{option_results}->{sentinel_port} =~ /(\d+)/ ? $1 : 26379; $self->{sentinel_port} = defined($self->{option_results}->{sentinel_port}) && $self->{option_results}->{sentinel_port} =~ /(\d+)/ ? $1 : 26379;
$self->{username} = defined($self->{option_results}->{username}) && $self->{option_results}->{username} ne '' ? $self->{option_results}->{username} : ''; $self->{username} = $self->{option_results}->{username} // '';
$self->{password} = defined($self->{option_results}->{password}) && $self->{option_results}->{password} ne '' ? $self->{option_results}->{password} : ''; $self->{password} = $self->{option_results}->{password} // '';
$self->{cacert} = $self->{option_results}->{cacert} // '';
$self->{cert} = $self->{option_results}->{cert} // '';
$self->{key} = $self->{option_results}->{key} // '';
$self->{sentinel} = []; $self->{sentinel} = [];
if (defined($self->{option_results}->{sentinel})) { if (defined($self->{option_results}->{sentinel})) {
foreach my $addr (@{$self->{option_results}->{sentinel}}) { foreach my $addr (@{$self->{option_results}->{sentinel}}) {
@ -92,9 +99,12 @@ sub check_options {
$self->{output}->add_option_msg(short_msg => 'Need to specify --service option.'); $self->{output}->add_option_msg(short_msg => 'Need to specify --service option.');
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
if ($self->{username} ne '') {
$self->{output}->add_option_msg(short_msg => 'Unsupported --username option.'); foreach (qw/username cert key/) {
$self->{output}->option_exit(); if ($self->{$_} ne '') {
$self->{output}->add_option_msg(short_msg => "Unsupported --$_ option.");
$self->{output}->option_exit();
}
} }
return 0; return 0;
@ -144,11 +154,11 @@ __END__
=head1 NAME =head1 NAME
REDIS perlmod REDIS Perl mode
=head1 SYNOPSIS =head1 SYNOPSIS
Redis perlmod Redis Perl mode
=head1 REDIS OPTIONS =head1 REDIS OPTIONS

View File

@ -0,0 +1,59 @@
use strict;
use warnings;
package MockOptions;
sub new { bless { extra_arguments => [ ], option_results => {}, default => {}, custom => {} }, shift }
sub add_options { }
sub add_help { }
package MockOutput;
sub new { bless {}, shift }
sub add_option_msg { }
sub output_add { }
sub option_exit { }
package main;
# Unit tests to check that the plugin constructs a valid redis-cli command with the corresponding parameters
use Test2::V0;
use FindBin;
use lib "$FindBin::RealBin/../../../src";
use apps::redis::sentinel::mode::listclusters;
use apps::redis::sentinel::custom::cli;
my $capture;
my $options = MockOptions->new();
my $output = MockOutput->new();
my $plugin_misc = mock 'centreon::plugins::misc';
$plugin_misc->override('execute' => sub {
my (%options) = @_;
$capture = "$options{command} $options{command_options}";
});
my $plugin = apps::redis::sentinel::mode::listclusters->new(
options => $options,
output => $output,
);
$plugin->init(%$options);
my $cust = apps::redis::sentinel::custom::cli->new(
options => $options,
output => $output,
);
$options->{custom} = $cust;
foreach my $test ({ title => 'Test --key parameter', param => { key => 'private.key'}, expect => q(--key 'private.key') },
{ title => 'Test --cert parameter', param => { cert => 'dummy.crt'}, expect => q(--cert 'dummy.crt') },
{ title => 'Test --cacert parameter', param => { cacert => 'ca.crt'}, expect => q(--cacert 'ca.crt') },) {
$cust->set_options(option_results => $test->{param} );
$cust->check_options();
$plugin->manage_selection(%$options);
ok($capture =~ /$test->{expect}/, "$test->{title}");
}
done_testing;

View File

@ -0,0 +1,61 @@
use strict;
use warnings;
package MockOptions;
sub new { bless { extra_arguments => [ ], option_results => {}, default => {}, custom => {} }, shift }
sub add_options { }
sub add_help { }
package MockOutput;
sub new { bless {}, shift }
sub add_option_msg { }
sub output_add { }
sub use_new_perfdata { }
sub option_exit { }
package main;
# Unit tests to check that the plugin constructs a valid redis-cli command with the corresponding parameters
use Test2::V0;
use FindBin;
use lib "$FindBin::RealBin/../../../src";
use database::redis::mode::commands;
use database::redis::custom::cli;
my $capture;
my $options = MockOptions->new();
my $output = MockOutput->new();
my $plugin_misc = mock 'centreon::plugins::misc';
$plugin_misc->override('execute' => sub {
my (%options) = @_;
$capture = "$options{command} $options{command_options}";
});
my $plugin = database::redis::mode::commands->new(
options => $options,
output => $output,
mode => 'commands',
);
$plugin->init(%$options);
my $cust = database::redis::custom::cli->new(
options => $options,
output => $output,
);
$options->{custom} = $cust;
foreach my $test ({ title => 'Test --key parameter', param => { key => 'private.key'}, expect => q(--key 'private.key') },
{ title => 'Test --cert parameter', param => { cert => 'dummy.crt'}, expect => q(--cert 'dummy.crt') },
{ title => 'Test --cacert parameter', param => { cacert => 'ca.crt'}, expect => q(--cacert 'ca.crt') },) {
$cust->set_options(option_results => $test->{param} );
$cust->check_options();
$plugin->manage_selection(%$options);
ok($capture =~ /$test->{expect}/, "$test->{title}");
}
done_testing;

View File

@ -254,6 +254,7 @@ ReplicaJobSession
RestAPI RestAPI
RFC1628 RFC1628
RRDCached RRDCached
redis-cli
rsrp rsrp
rsrq rsrq
rssi rssi
@ -294,6 +295,7 @@ Teldat
temperatureambient temperatureambient
timeframe timeframe
TiMOS TiMOS
TLS
TMM TMM
tmnxSasAlarmInputDescription tmnxSasAlarmInputDescription
topic-messages-inflighted topic-messages-inflighted