3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-10-01 17:16:39 +02:00

Add data/config migration framework

This commit is contained in:
Pragmatic Software 2020-04-20 10:53:35 -07:00
parent b007cb3b9d
commit b9d3fa5f03
5 changed files with 149 additions and 29 deletions

89
PBot/Migration.pm Normal file
View File

@ -0,0 +1,89 @@
# File: Migration.pm
# Author: pragma_
#
# Purpose: Migrates data/configration files to new locations/formats based
# on versioning information. Ensures data/configuration files are in the
# proper location and using the latest data structure.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
package PBot::Migration;
use parent 'PBot::Class';
use warnings; use strict;
use feature 'unicode_strings';
use File::Basename;
sub initialize {
my ($self, %conf) = @_;
$self->{data_dir} = $conf{data_dir};
$self->{migration_dir} = $conf{migration_dir};
}
sub migrate {
my ($self) = @_;
$self->{pbot}->{logger}->log("Checking if migration needed...\n");
my $current_version = $self->get_current_version;
my $last_migration_version = $self->get_last_migration_version;
$self->{pbot}->{logger}->log("Current version: $current_version; last migration version: $last_migration_version\n");
if ($last_migration_version >= $current_version) {
$self->{pbot}->{logger}->log("No migration necessary.\n");
return 0;
}
my @migrations = $self->get_available_migrations($last_migration_version);
if (not @migrations ) {
$self->{pbot}->{logger}->log("No migrations available.\n");
return 0;
}
foreach my $migration (@migrations) {
$self->{pbot}->{logger}->log("Executing migration script: $migration\n");
my $output = `$migration $self->{data_dir}`;
my $exit = $? >> 8;
$self->{pbot}->{logger}->log("Script completed. Exit $exit. Output: $output");
return $exit if $exit != 0;
}
return $self->put_last_migration_version($current_version);
}
sub get_available_migrations {
my ($self, $last_migration_version) = @_;
my @migrations = sort glob "$self->{migration_dir}/*";
return grep { my ($version) = split /_/, basename $_; $version > $last_migration_version ? 1 : 0 } @migrations;
}
sub get_current_version {
return PBot::VERSION::BUILD_REVISION;
}
sub get_last_migration_version {
my ($self) = @_;
open(my $fh, '<', "$self->{data_dir}/last_migration") or return 0;
chomp(my $last_migration = <$fh>);
close $fh;
return $last_migration;
}
sub put_last_migration_version {
my ($self, $version) = @_;
if (open(my $fh, '>', "$self->{data_dir}/last_migration")) {
print $fh "$version\n";
close $fh;
return 0;
} else {
$self->{pbot}->{logger}->log("Could not save last migration to $self->{data_dir}/last_migration: $!\n");
return 1;
}
}
1;

View File

@ -50,6 +50,7 @@ use PBot::Plugins;
use PBot::Functions;
use PBot::Modules;
use PBot::ProcessManager;
use PBot::Migration;
sub new {
my ($proto, %conf) = @_;
@ -63,24 +64,26 @@ sub initialize {
my ($self, %conf) = @_;
$self->{startup_timestamp} = time;
my $data_dir = $conf{data_dir};
my $module_dir = $conf{module_dir};
my $plugin_dir = $conf{plugin_dir};
my $data_dir = $conf{data_dir};
my $module_dir = $conf{module_dir};
my $plugin_dir = $conf{plugin_dir};
my $migration_dir = $conf{migration_dir};
# check command-line arguments for directory overrides
foreach my $arg (@ARGV) {
if ($arg =~ m/^-?(?:general\.)?((?:data|module|plugin)_dir)=(.*)$/) {
my $override = $1;
my $value = $2;
$data_dir = $value if $override eq 'data_dir';
$module_dir = $value if $override eq 'module_dir';
$plugin_dir = $value if $override eq 'plugin_dir';
if ($arg =~ m/^-?(?:general\.)?((?:data|module|plugin|migration)_dir)=(.*)$/) {
my $override = $1;
my $value = $2;
$data_dir = $value if $override eq 'data_dir';
$module_dir = $value if $override eq 'module_dir';
$plugin_dir = $value if $override eq 'plugin_dir';
$migration_dir = $value if $override eq 'migration_dir';
}
}
# check command-line arguments for registry overrides
foreach my $arg (@ARGV) {
next if $arg =~ m/^-?(?:general\.)?(?:config|data|module|plugin)_dir=.*$/; # already processed
next if $arg =~ m/^-?(?:general\.)?(?:config|data|module|plugin|migration)_dir=.*$/; # already processed
my ($item, $value) = split /=/, $arg, 2;
if (not defined $item or not defined $value) {
@ -102,15 +105,16 @@ sub initialize {
$self->{atexit} = PBot::Registerable->new(%conf, pbot => $self);
$self->register_signal_handlers;
# create logger
$self->{logger} = PBot::Logger->new(pbot => $self, filename => "$data_dir/log/log", %conf);
# make sure the environment is sane
# make sure the data directory exists
if (not -d $data_dir) {
$self->{logger}->log("Data directory ($data_dir) does not exist; aborting...\n");
print STDERR "Data directory ($data_dir) does not exist; aborting...\n";
exit;
}
# create logger
$self->{logger} = PBot::Logger->new(pbot => $self, filename => "$data_dir/log/log", %conf);
# make sure the rest of the environment is sane
if (not -d $module_dir) {
$self->{logger}->log("Modules directory ($module_dir) does not exist; aborting...\n");
exit;
@ -121,10 +125,23 @@ sub initialize {
exit;
}
# then capabilities so commands can add new capabilities
if (not -d $migration_dir) {
$self->{logger}->log("Migration directory ($migration_dir) does not exist; aborting...\n");
exit;
}
# migrate/update any data files to new locations/formats
$self->{migrator} = PBot::Migration->new(pbot => $self, data_dir => $data_dir, migration_dir => $migration_dir);
if ($self->{migrator}->migrate) {
$self->{logger}->log("Migration failed.\n");
exit 0;
}
# create capabilities so commands can add new capabilities
$self->{capabilities} = PBot::Capabilities->new(pbot => $self, filename => "$data_dir/capabilities", %conf);
# then commands so the modules can register new commands
# create commands so the modules can register new commands
$self->{commands} = PBot::Commands->new(pbot => $self, filename => "$data_dir/commands", %conf);
# add some commands
@ -143,10 +160,11 @@ sub initialize {
$self->{logger}->log($self->{version}->version . "\n");
$self->{logger}->log("Args: @ARGV\n") if @ARGV;
# log the configured paths
$self->{logger}->log("data_dir: $data_dir\n");
$self->{logger}->log("module_dir: $module_dir\n");
$self->{logger}->log("plugin_dir: $plugin_dir\n");
$self->{logger}->log("data_dir: $data_dir\n");
$self->{logger}->log("migration_dir: $migration_dir\n");
$self->{timer} = PBot::Timer->new(pbot => $self, timeout => 10, name => 'PBot Timer', %conf);
$self->{modules} = PBot::Modules->new(pbot => $self, %conf);
@ -156,10 +174,11 @@ sub initialize {
# create registry and set some defaults
$self->{registry} = PBot::Registry->new(pbot => $self, filename => "$data_dir/registry", %conf);
$self->{registry}->add_default('text', 'general', 'data_dir', $data_dir);
$self->{registry}->add_default('text', 'general', 'module_dir', $module_dir);
$self->{registry}->add_default('text', 'general', 'plugin_dir', $plugin_dir);
$self->{registry}->add_default('text', 'general', 'trigger', $conf{trigger} // '!');
$self->{registry}->add_default('text', 'general', 'data_dir', $data_dir);
$self->{registry}->add_default('text', 'general', 'module_dir', $module_dir);
$self->{registry}->add_default('text', 'general', 'plugin_dir', $plugin_dir);
$self->{registry}->add_default('text', 'general', 'migration_dir', $migration_dir);
$self->{registry}->add_default('text', 'general', 'trigger', $conf{trigger} // '!');
$self->{registry}->add_default('text', 'irc', 'debug', $conf{irc_debug} // 0);
$self->{registry}->add_default('text', 'irc', 'show_motd', $conf{show_motd} // 1);
@ -183,9 +202,10 @@ sub initialize {
if (-e $self->{registry}->{registry}->{filename}) { $self->{registry}->load; }
# update important paths
$self->{registry}->set('general', 'data_dir', 'value', $data_dir, 0, 1);
$self->{registry}->set('general', 'module_dir', 'value', $module_dir, 0, 1);
$self->{registry}->set('general', 'plugin_dir', 'value', $plugin_dir, 0, 1);
$self->{registry}->set('general', 'data_dir', 'value', $data_dir, 0, 1);
$self->{registry}->set('general', 'module_dir', 'value', $module_dir, 0, 1);
$self->{registry}->set('general', 'plugin_dir', 'value', $plugin_dir, 0, 1);
$self->{registry}->set('general', 'migration_dir', 'value', $migration_dir, 0, 1);
# override registry entries with command-line arguments, if any
foreach my $override (keys %{$self->{overrides}}) {

View File

@ -16,6 +16,7 @@ use warnings; use strict;
use feature 'unicode_strings';
use Module::Refresh;
use File::Basename;
sub initialize {
my ($self, %conf) = @_;
@ -25,6 +26,14 @@ sub initialize {
sub refresh {
my ($self, $from, $nick, $user, $host, $arguments) = @_;
my $last_migration = $self->{pbot}->{migrator}->get_last_migration_version;
my @migrations = $self->{pbot}->{migrator}->get_available_migrations($last_migration);
if (@migrations) {
return "Migration available; cannot refresh. Please restart PBot to begin migration of " . join(', ', map { basename $_ } @migrations);
}
my $refresh_error;
local $SIG{__WARN__} = sub {
my $warning = shift;
@ -55,6 +64,7 @@ sub refresh {
$self->{pbot}->{logger}->log("Error refreshing: $@\n");
return $@;
}
return $result;
}

0
migration/.keep Normal file
View File

7
pbot
View File

@ -15,9 +15,10 @@ BEGIN {
# configuration is overridden via command-line arguments, do not modify
# see doc/QuickStart.md
my %config = (
data_dir => "$bothome/data",
module_dir => "$bothome/modules",
plugin_dir => "$bothome/Plugins",
data_dir => "$bothome/data",
module_dir => "$bothome/modules",
plugin_dir => "$bothome/Plugins",
migration_dir => "$bothome/migration",
);
use PBot::PBot;