Add ability to show pending apply state after config changes

This commit is contained in:
Ilia Ross
2026-05-18 23:05:25 +02:00
parent 1d03afbdd5
commit 578a41769e
4 changed files with 104 additions and 9 deletions

View File

@@ -32,6 +32,7 @@ index_stopdesc=Shut down all running Nginx webserver processes.
index_start=Start Nginx Webserver
index_startdesc=Start up the Nginx webserver using the current configuration.
index_restart=Apply Nginx Configuration
index_apply_changes=Apply Changes
index_restartdesc=Apply the current configuration by stopping and re-starting the Nginx webserver.
index_delete=Delete Selected Server Blocks
index_eglobal=You are not allowed to edit global settings

View File

@@ -11,8 +11,11 @@ eval "use WebminCore;";
our %access = &get_module_acl();
our ($get_config_cache, $get_config_parent_cache, %list_directives_cache,
@list_modules_cache, @open_config_files);
our (%config, %text, %in, $module_root_directory);
our ($last_config_change_flag, $last_restart_time_flag);
our (%config, %text, %in, $module_root_directory, $module_var_directory);
&set_nginx_config_defaults();
$last_config_change_flag = $module_var_directory."/config-flag";
$last_restart_time_flag = $module_var_directory."/restart-flag";
my @lock_all_config_files_cache;
@@ -522,9 +525,11 @@ if ($parent->{'type'}) {
sub flush_config_file_lines
{
my ($parent) = @_;
foreach my $f (&unique(@open_config_files)) {
my @files = &unique(@open_config_files);
foreach my $f (@files) {
&flush_file_lines($f);
}
&update_last_config_change() if (@files);
@open_config_files = ( );
}
@@ -1480,7 +1485,11 @@ return $? ? $out : undef;
sub start_nginx
{
my $out = &backquote_logged("$config{'start_cmd'} 2>&1 </dev/null");
return $? ? $out : undef;
if ($?) {
return $out;
}
&update_last_restart_time();
return undef;
}
# apply_nginx()
@@ -1489,7 +1498,11 @@ return $? ? $out : undef;
sub apply_nginx
{
my $out = &backquote_logged("$config{'apply_cmd'} 2>&1 </dev/null");
return $? ? $out : undef;
if ($?) {
return $out;
}
&update_last_restart_time();
return undef;
}
# nginx_action_links()
@@ -1502,7 +1515,11 @@ if (&is_nginx_running()) {
if ($access{'stop'}) {
push(@rv, &ui_link("stop.cgi?$args", $text{'index_stop'}));
}
push(@rv, &ui_link("restart.cgi?$args", $text{'index_restart'}));
my $needs = &needs_config_restart();
my $apply = $text{'index_apply_changes'} || $text{'index_restart'};
my $label = $needs ? "<b>$apply</b>" : $apply;
my $url = "restart.cgi?$args";
push(@rv, &ui_link($url, $label));
}
elsif ($access{'stop'}) {
push(@rv, &ui_link("start.cgi?$args", $text{'index_start'}));
@@ -1510,6 +1527,33 @@ elsif ($access{'stop'}) {
return join("<br>\n", @rv);
}
# update_last_config_change()
# Updates the flag file indicating when the config was changed
sub update_last_config_change
{
&open_lock_tempfile(my $fh, ">$last_config_change_flag", 0, 1);
&close_tempfile($fh);
}
# update_last_restart_time()
# Updates the flag file indicating when the config was applied
sub update_last_restart_time
{
&open_lock_tempfile(my $fh, ">$last_restart_time_flag", 0, 1);
&close_tempfile($fh);
}
# needs_config_restart()
# Returns 1 if a restart is needed after a config change
sub needs_config_restart
{
my @cst = stat($last_config_change_flag);
my @rst = stat($last_restart_time_flag);
return 0 if (!@cst);
return 1 if (!@rst);
return $cst[9] > $rst[9] ? 1 : 0;
}
# this_url()
# Returns the current module URL
sub this_url
@@ -1610,6 +1654,7 @@ if ($empty) {
}
&unlink_logged($file);
}
&update_last_config_change();
return scalar(@servers);
}
@@ -1723,6 +1768,7 @@ if ($err) {
&unlink_logged($link);
return &text('enable_etest', "<tt>".&html_escape($err)."</tt>");
}
&update_last_config_change();
return undef;
}
@@ -1756,6 +1802,7 @@ if ($err) {
}
return &text('enable_etest', "<tt>".&html_escape($err)."</tt>");
}
&update_last_config_change();
return undef;
}
@@ -2037,7 +2084,8 @@ if ($config{'add_link'}) {
my $link = $server->{'file'};
$link =~ s/^.*\///;
$link = $config{'add_link'}."/".$link;
&symlink_logged($server->{'file'}, $link);
&update_last_config_change()
if (&symlink_logged($server->{'file'}, $link));
}
}
@@ -2049,6 +2097,7 @@ sub delete_server_link
my ($server) = @_;
if ($config{'add_link'}) {
my $file = $server->{'file'};
my $changed;
my $short = $file;
$short =~ s/^.*\///;
opendir(LINKDIR, $config{'add_link'});
@@ -2056,10 +2105,11 @@ if ($config{'add_link'}) {
if ($f ne "." && $f ne ".." &&
(&resolve_links($config{'add_link'}."/".$f) eq $file ||
$short eq $f)) {
&unlink_logged($config{'add_link'}."/".$f);
$changed++ if (&unlink_logged($config{'add_link'}."/".$f));
}
}
closedir(LINKDIR);
&update_last_config_change() if ($changed);
}
}
@@ -2074,7 +2124,8 @@ foreach my $l (@$lref) {
$count++ if ($l =~ /\S/);
}
if (!$count) {
&unlink_logged($server->{'file'});
&update_last_config_change()
if (&unlink_logged($server->{'file'}));
}
}

View File

@@ -43,6 +43,6 @@ else {
&print_tempfile($fh, $in{'data'});
&close_tempfile($fh);
}
&update_last_config_change();
&webmin_log("manual", undef, $in{'file'});
&redirect("");

View File

@@ -130,11 +130,17 @@ subtest 'sites-available files are manageable and ordered' => sub {
};
subtest 'disable removes only the enabled symlink' => sub {
no warnings 'once';
unlink($main::last_config_change_flag);
unlink($main::last_restart_time_flag);
{
no warnings 'redefine';
local *main::test_config = sub { return undef; };
is(main::disable_server_file($alpha), undef, 'disable succeeds');
}
ok(main::needs_config_restart(),
'disable marks config as needing apply');
ok(-f $alpha, 'disable leaves the sites-available file in place');
ok(!-e File::Spec->catfile($enabled, 'alpha.conf'),
@@ -150,11 +156,17 @@ subtest 'disable removes only the enabled symlink' => sub {
};
subtest 'enable creates a symlink without touching the source file' => sub {
no warnings 'once';
unlink($main::last_config_change_flag);
unlink($main::last_restart_time_flag);
{
no warnings 'redefine';
local *main::test_config = sub { return undef; };
is(main::enable_server_file($beta), undef, 'enable succeeds');
}
ok(main::needs_config_restart(),
'enable marks config as needing apply');
my $link = File::Spec->catfile($enabled, 'beta.conf');
ok(-f $beta, 'enable leaves the sites-available file in place');
@@ -164,18 +176,28 @@ subtest 'enable creates a symlink without touching the source file' => sub {
};
subtest 'legacy create/delete link helpers still manage symlinks' => sub {
no warnings 'once';
my $echo = File::Spec->catfile($available, 'echo.conf');
my $echo_link = File::Spec->catfile($enabled, 'echo.conf');
write_text($echo, server_conf('echo.example', "\troot /srv/echo;\n"));
my $server = { 'file' => $echo };
unlink($main::last_config_change_flag);
unlink($main::last_restart_time_flag);
main::create_server_link($server);
ok(-l $echo_link, 'create_server_link creates expected symlink');
is(readlink($echo_link), $echo, 'created symlink points to server file');
ok(main::needs_config_restart(),
'create_server_link marks config as needing apply');
main::update_last_restart_time();
my $old = time() - 10;
utime($old, $old, $main::last_restart_time_flag);
main::delete_server_link($server);
ok(!-e $echo_link, 'delete_server_link removes expected symlink');
ok(-f $echo, 'delete_server_link leaves server file in place');
ok(main::needs_config_restart(),
'delete_server_link marks config as needing apply');
};
subtest 'disabled server blocks can be deleted from available files' => sub {
@@ -342,4 +364,25 @@ subtest 'root and proxy summaries are detected' => sub {
'named-location proxy URL uses HTTP default port');
};
subtest 'config change apply flag tracks pending changes' => sub {
no warnings 'once';
unlink($main::last_config_change_flag);
unlink($main::last_restart_time_flag);
ok(!main::needs_config_restart(),
'no apply needed when no change flag exists');
main::update_last_config_change();
ok(main::needs_config_restart(),
'apply needed after config change');
main::update_last_restart_time();
ok(!main::needs_config_restart(),
'apply not needed after config has been applied');
my $old = time() - 10;
utime($old, $old, $main::last_restart_time_flag);
main::update_last_config_change();
ok(main::needs_config_restart(),
'apply needed when config change is newer than last apply');
};
done_testing();