summaryrefslogtreecommitdiff
path: root/rt/sbin/rt-setup-database.in
diff options
context:
space:
mode:
Diffstat (limited to 'rt/sbin/rt-setup-database.in')
-rw-r--r--rt/sbin/rt-setup-database.in476
1 files changed, 476 insertions, 0 deletions
diff --git a/rt/sbin/rt-setup-database.in b/rt/sbin/rt-setup-database.in
new file mode 100644
index 0000000..ea9b99b
--- /dev/null
+++ b/rt/sbin/rt-setup-database.in
@@ -0,0 +1,476 @@
+#!@PERL@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+use strict;
+use warnings;
+
+use vars qw($Nobody $SystemUser $item);
+
+# fix lib paths, some may be relative
+BEGIN {
+ require File::Spec;
+ my @libs = ("@RT_LIB_PATH@", "@LOCAL_LIB_PATH@");
+ my $bin_path;
+
+ for my $lib (@libs) {
+ unless ( File::Spec->file_name_is_absolute($lib) ) {
+ unless ($bin_path) {
+ if ( File::Spec->file_name_is_absolute(__FILE__) ) {
+ $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
+ }
+ else {
+ require FindBin;
+ no warnings "once";
+ $bin_path = $FindBin::Bin;
+ }
+ }
+ $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
+ }
+ unshift @INC, $lib;
+ }
+
+}
+
+#This drags in RT's config.pm
+# We do it in a begin block because RT::Handle needs to know the type to do its
+# inheritance
+BEGIN {
+ use RT;
+ RT::LoadConfig();
+ RT::InitClasses();
+}
+
+use Term::ReadKey;
+use Getopt::Long;
+
+$| = 1; # unbuffer all output.
+
+my %args;
+GetOptions(
+ \%args,
+ 'action=s',
+ 'force', 'debug',
+ 'dba=s', 'dba-password=s', 'prompt-for-dba-password',
+ 'datafile=s', 'datadir=s'
+);
+
+unless ( $args{'action'} ) {
+ help();
+ exit(-1);
+}
+
+# check and setup @actions
+my @actions = grep $_, split /,/, $args{'action'};
+if ( @actions > 1 && $args{'datafile'} ) {
+ print STDERR "You can not use --datafile option with multiple actions.\n";
+ exit(-1);
+}
+foreach ( @actions ) {
+ unless ( /^(?:init|create|drop|schema|acl|coredata|insert|upgrade)$/ ) {
+ print STDERR "$0 called with an invalid --action parameter.\n";
+ exit(-1);
+ }
+ if ( /^(?:init|drop|upgrade)$/ && @actions > 1 ) {
+ print STDERR "You can not mix init, drop or upgrade action with any action.\n";
+ exit(-1);
+ }
+}
+
+# convert init to multiple actions
+my $init = 0;
+if ( $actions[0] eq 'init' ) {
+ @actions = qw(create schema acl coredata insert);
+ $init = 1;
+}
+
+# set options from environment
+foreach my $key(qw(Type Host Name User Password)) {
+ next unless exists $ENV{ 'RT_DB_'. uc $key };
+ print "Using Database$key from RT_DB_". uc($key) ." environment variable.\n";
+ RT->Config->Set( "Database$key", $ENV{ 'RT_DB_'. uc $key });
+}
+
+my $db_type = RT->Config->Get('DatabaseType') || '';
+my $db_host = RT->Config->Get('DatabaseHost') || '';
+my $db_name = RT->Config->Get('DatabaseName') || '';
+my $db_user = RT->Config->Get('DatabaseUser') || '';
+my $db_pass = RT->Config->Get('DatabasePassword') || '';
+
+# load it here to get error immidiatly if DB type is not supported
+require RT::Handle;
+
+if ( $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name) ) {
+ $db_name = File::Spec->catfile($RT::VarPath, $db_name);
+ RT->Config->Set( DatabaseName => $db_name );
+}
+
+my $dba_user = $args{'dba'} || $ENV{'RT_DBA_USER'} || $db_user || '';
+my $dba_pass = exists($args{'dba-password'})
+ ? $args{'dba-password'}
+ : $ENV{'RT_DBA_PASSWORD'};
+
+if ( !$args{force} && ( !defined $dba_pass || $args{'prompt-for-dba-password'} ) ) {
+ $dba_pass = get_dba_password();
+ chomp $dba_pass if defined($dba_pass);
+}
+
+print "Working with:\n"
+ ."Type:\t$db_type\nHost:\t$db_host\nName:\t$db_name\n"
+ ."User:\t$db_user\nDBA:\t$dba_user\n";
+
+foreach my $action ( @actions ) {
+ no strict 'refs';
+ my ($status, $msg) = *{ 'action_'. $action }{'CODE'}->( %args );
+ error($action, $msg) unless $status;
+ print $msg ."\n" if $msg;
+ print "Done.\n";
+}
+
+sub action_create {
+ my %args = @_;
+ my $dbh = get_system_dbh();
+ my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'pre' );
+ return ($status, $msg) unless $status;
+
+ print "Now creating a $db_type database $db_name for RT.\n";
+ return RT::Handle->CreateDatabase( $dbh );
+}
+
+sub action_drop {
+ my %args = @_;
+
+ print "Dropping $db_type database $db_name.\n";
+ unless ( $args{'force'} ) {
+ print <<END;
+
+About to drop $db_type database $db_name on $db_host.
+WARNING: This will erase all data in $db_name.
+
+END
+ exit(-2) unless _yesno();
+ }
+
+ my $dbh = get_system_dbh();
+ return RT::Handle->DropDatabase( $dbh );
+}
+
+sub action_schema {
+ my %args = @_;
+ my $dbh = get_admin_dbh();
+ my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'pre' );
+ return ($status, $msg) unless $status;
+
+ print "Now populating database schema.\n";
+ return RT::Handle->InsertSchema( $dbh, $args{'datafile'} || $args{'datadir'} );
+}
+
+sub action_acl {
+ my %args = @_;
+ my $dbh = get_admin_dbh();
+ my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'pre' );
+ return ($status, $msg) unless $status;
+
+ print "Now inserting database ACLs\n";
+ return RT::Handle->InsertACL( $dbh, $args{'datafile'} || $args{'datadir'} );
+}
+
+sub action_coredata {
+ my %args = @_;
+ $RT::Handle = new RT::Handle;
+ $RT::Handle->dbh( undef );
+ RT::ConnectToDatabase();
+ RT::InitLogging();
+ my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'pre' );
+ return ($status, $msg) unless $status;
+
+ print "Now inserting RT core system objects\n";
+ return $RT::Handle->InsertInitialData;
+}
+
+sub action_insert {
+ my %args = @_;
+ $RT::Handle = new RT::Handle;
+ RT::Init();
+ my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'pre' );
+ return ($status, $msg) unless $status;
+
+ print "Now inserting data\n";
+ my $file = $args{'datafile'};
+ $file = $RT::EtcPath . "/initialdata" if $init && !$file;
+ $file ||= $args{'datadir'}."/content";
+ return $RT::Handle->InsertData( $file );
+}
+
+sub action_upgrade {
+ my %args = @_;
+ my $base_dir = $args{'datadir'} || "./etc/upgrade";
+ return (0, "Couldn't read dir '$base_dir' with upgrade data")
+ unless -d $base_dir || -r _;
+
+ my $upgrading_from = undef;
+ do {
+ if ( defined $upgrading_from ) {
+ print "Doesn't match #.#.#: ";
+ } else {
+ print "Enter RT version you're upgrading from: ";
+ }
+ $upgrading_from = scalar <STDIN>;
+ chomp $upgrading_from;
+ $upgrading_from =~ s/\s+//g;
+ } while $upgrading_from !~ /^\d+\.\d+\.\d+$/;
+
+ my $upgrading_to = $RT::VERSION;
+ return (0, "The current version $upgrading_to is lower than $upgrading_from")
+ if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) > 0;
+
+ return (1, "The version $upgrading_to you're upgrading to is up to date")
+ if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) == 0;
+
+ my @versions = get_versions_from_to($base_dir, $upgrading_from, $upgrading_to);
+
+ return (1, "No DB changes between $upgrading_from and $upgrading_to")
+ unless @versions;
+
+ print "\nGoing to apply following upgrades:\n";
+ print map "* $_\n", @versions;
+
+ {
+ my $custom_upgrading_to = undef;
+ do {
+ if ( defined $custom_upgrading_to ) {
+ print "Doesn't match #.#.#: ";
+ } else {
+ print "\nEnter RT version if you want to stop upgrade at some point,\n";
+ print " or leave it blank if you want apply above upgrades: ";
+ }
+ $custom_upgrading_to = scalar <STDIN>;
+ chomp $custom_upgrading_to;
+ $custom_upgrading_to =~ s/\s+//g;
+ last unless $custom_upgrading_to;
+ } while $custom_upgrading_to !~ /^\d+\.\d+\.\d+$/;
+
+ if ( $custom_upgrading_to ) {
+ return (
+ 0, "The version you entered ($custom_upgrading_to) is lower than\n"
+ ."version you're upgrading from ($upgrading_from)"
+ ) if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) > 0;
+
+ return (1, "The version you're upgrading to is up to date")
+ if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) == 0;
+
+ if ( RT::Handle::cmp_version( $RT::VERSION, $custom_upgrading_to ) < 0 ) {
+ print "Version you entered is greater than installed ($RT::VERSION).\n";
+ _yesno() or exit(-2);
+ }
+ # ok, checked everything no let's refresh list
+ $upgrading_to = $custom_upgrading_to;
+ @versions = get_versions_from_to($base_dir, $upgrading_from, $upgrading_to);
+
+ return (1, "No DB changes between $upgrading_from and $upgrading_to")
+ unless @versions;
+
+ print "\nGoing to apply following upgrades:\n";
+ print map "* $_\n", @versions;
+ }
+ }
+
+ print "\nIT'S VERY IMPORTANT TO BACK UP BEFORE THIS STEP\n\n";
+ _yesno() or exit(-2) unless $args{'force'};
+
+ foreach my $v ( @versions ) {
+ print "Processing $v\n";
+ my %tmp = (%args, datadir => "$base_dir/$v", datafile => undef);
+ if ( -e "$base_dir/$v/schema.$db_type" ) {
+ action_schema( %tmp );
+ }
+ if ( -e "$base_dir/$v/acl.$db_type" ) {
+ action_acl( %tmp );
+ }
+ if ( -e "$base_dir/$v/content" ) {
+ action_insert( %tmp );
+ }
+ }
+ return 1;
+}
+
+sub get_versions_from_to {
+ my ($base_dir, $from, $to) = @_;
+
+ opendir my $dh, $base_dir or die "couldn't open dir: $!";
+ my @versions = grep -d "$base_dir/$_" && /\d+\.\d+\.\d+/, readdir $dh;
+ closedir $dh;
+
+ return
+ grep RT::Handle::cmp_version($_, $to) <= 0,
+ grep RT::Handle::cmp_version($_, $from) > 0,
+ sort RT::Handle::cmp_version @versions;
+}
+
+sub error {
+ my ($action, $msg) = @_;
+ print STDERR "Couldn't finish '$action' step.\n\n";
+ print STDERR "ERROR: $msg\n\n";
+ exit(-1);
+}
+
+sub get_dba_password {
+ print "In order to create or update your RT database,"
+ . " this script needs to connect to your "
+ . " $db_type instance on $db_host as $dba_user\n";
+ print "Please specify that user's database password below. If the user has no database\n";
+ print "password, just press return.\n\n";
+ print "Password: ";
+ ReadMode('noecho');
+ my $password = ReadLine(0);
+ ReadMode('normal');
+ print "\n";
+ return ($password);
+}
+
+=head2 get_system_dbh
+
+Returns L<DBI> database handle connected to B<system> with DBA credentials.
+
+See also L<RT::Handle/SystemDSN>.
+
+=cut
+
+sub get_system_dbh {
+ return _get_dbh( RT::Handle->SystemDSN, $dba_user, $dba_pass );
+}
+
+sub get_admin_dbh {
+ return _get_dbh( RT::Handle->DSN, $dba_user, $dba_pass );
+}
+
+=head2 get_rt_dbh [USER, PASSWORD]
+
+Returns L<DBI> database handle connected to RT database,
+you may specify credentials(USER and PASSWORD) to connect
+with. By default connects with credentials from RT config.
+
+=cut
+
+sub get_rt_dbh {
+ return _get_dbh( RT::Handle->DSN, $db_user, $db_pass );
+}
+
+sub _get_dbh {
+ my ($dsn, $user, $pass) = @_;
+ my $dbh = DBI->connect(
+ $dsn, $user, $pass,
+ { RaiseError => 0, PrintError => 0 },
+ );
+ unless ( $dbh ) {
+ my $msg = "Failed to connect to $dsn as user '$user': ". $DBI::errstr;
+ if ( $args{'debug'} ) {
+ require Carp; Carp::confess( $msg );
+ } else {
+ print STDERR $msg; exit -1;
+ }
+ }
+ return $dbh;
+}
+
+sub _yesno {
+ print "Proceed [y/N]:";
+ my $x = scalar(<STDIN>);
+ $x =~ /^y/i;
+}
+
+sub help {
+
+ print <<EOF;
+
+$0: Set up RT's database
+
+--action init Initialize the database. This is combination of
+ multiple actions listed below. Create DB, schema,
+ setup acl, insert core data and initial data.
+
+ upgrade Apply all needed schema/acl/content updates (will ask
+ for version to upgrade from)
+
+ create Create the database.
+
+ drop Drop the database.
+ This will ERASE ALL YOUR DATA
+
+ schema Initialize only the database schema
+ To use a local or supplementary datafile, specify it
+ using the '--datadir' option below.
+
+ acl Initialize only the database ACLs
+ To use a local or supplementary datafile, specify it
+ using the '--datadir' option below.
+
+ coredata Insert data into RT's database. This data is required
+ for normal functioning of any RT instance.
+
+ insert Insert data into RT's database.
+ By default, will use RT's installation data.
+ To use a local or supplementary datafile, specify it
+ using the '--datafile' option below.
+
+Several actions can be combined using comma separated list.
+
+--datafile /path/to/datafile
+--datadir /path/to/ Used to specify a path to find the local
+ database schema and acls to be installed.
+
+
+--dba dba's username
+--dba-password dba's password
+--prompt-for-dba-password Ask for the database administrator's password interactively
+
+
+EOF
+
+}
+
+1;