Upgrade to 4.2.8
[usit-rt.git] / sbin / rt-setup-database
CommitLineData
84fb5b46
MKG
1#!/usr/bin/perl
2# BEGIN BPS TAGGED BLOCK {{{
3#
4# COPYRIGHT:
5#
3ffc5f4f 6# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
84fb5b46
MKG
7# <sales@bestpractical.com>
8#
9# (Except where explicitly superseded by other copyright notices)
10#
11#
12# LICENSE:
13#
14# This work is made available to you under the terms of Version 2 of
15# the GNU General Public License. A copy of that license should have
16# been provided with this software, but in any event can be snarfed
17# from www.gnu.org.
18#
19# This work is distributed in the hope that it will be useful, but
20# WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22# General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License
25# along with this program; if not, write to the Free Software
26# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27# 02110-1301 or visit their web page on the internet at
28# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
29#
30#
31# CONTRIBUTION SUBMISSION POLICY:
32#
33# (The following paragraph is not intended to limit the rights granted
34# to you to modify and distribute this software under the terms of
35# the GNU General Public License and is only of importance to you if
36# you choose to contribute your changes and enhancements to the
37# community by submitting them to Best Practical Solutions, LLC.)
38#
39# By intentionally submitting any modifications, corrections or
40# derivatives to this work, or any other work intended for use with
41# Request Tracker, to Best Practical Solutions, LLC, you confirm that
42# you are the copyright holder for those contributions and you grant
43# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
44# royalty-free, perpetual, license to use, copy, create derivative
45# works based on those contributions, and sublicense and distribute
46# those contributions and any derivatives thereof.
47#
48# END BPS TAGGED BLOCK }}}
49use strict;
50use warnings;
51
52use vars qw($Nobody $SystemUser $item);
53
54# fix lib paths, some may be relative
3ffc5f4f 55BEGIN { # BEGIN RT CMD BOILERPLATE
84fb5b46 56 require File::Spec;
3ffc5f4f 57 require Cwd;
84fb5b46
MKG
58 my @libs = ("lib", "local/lib");
59 my $bin_path;
60
61 for my $lib (@libs) {
62 unless ( File::Spec->file_name_is_absolute($lib) ) {
3ffc5f4f 63 $bin_path ||= ( File::Spec->splitpath(Cwd::abs_path(__FILE__)) )[1];
84fb5b46
MKG
64 $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
65 }
66 unshift @INC, $lib;
67 }
68
69}
70
71use Term::ReadKey;
72use Getopt::Long;
3ffc5f4f 73use Data::GUID;
84fb5b46
MKG
74
75$| = 1; # unbuffer all output.
76
77my %args = (
01e3b242 78 package => 'RT',
84fb5b46
MKG
79);
80GetOptions(
81 \%args,
82 'action=s',
83 'force', 'debug',
01e3b242 84 'dba=s', 'dba-password=s', 'prompt-for-dba-password', 'package=s',
84fb5b46 85 'datafile=s', 'datadir=s', 'skip-create', 'root-password-file=s',
3ffc5f4f
MKG
86 'package=s', 'ext-version=s',
87 'upgrade-from=s', 'upgrade-to=s',
84fb5b46
MKG
88 'help|h',
89);
90
91no warnings 'once';
92if ( $args{help} || ! $args{'action'} ) {
93 require Pod::Usage;
94 Pod::Usage::pod2usage({ verbose => 2 });
95 exit;
96}
97
98require RT;
99RT->LoadConfig();
100RT->InitClasses();
101
102# Force warnings to be output to STDERR if we're not already logging
103# them at a higher level
3ffc5f4f
MKG
104RT->Config->Set( LogToSTDERR => 'warning')
105 unless ( RT->Config->Get( 'LogToSTDERR' )
106 && RT->Config->Get( 'LogToSTDERR' ) =~ /^(debug|info|notice)$/ );
107RT::InitLogging();
84fb5b46
MKG
108
109# get customized root password
110my $root_password;
111if ( $args{'root-password-file'} ) {
112 open( my $fh, '<', $args{'root-password-file'} )
113 or die "Couldn't open 'args{'root-password-file'}' for reading: $!";
114 $root_password = <$fh>;
115 chomp $root_password;
116 my $min_length = RT->Config->Get('MinimumPasswordLength');
117 if ($min_length) {
118 die
119"password needs to be at least $min_length long, please check file '$args{'root-password-file'}'"
120 if length $root_password < $min_length;
121 }
122 close $fh;
123}
124
125
126# check and setup @actions
127my @actions = grep $_, split /,/, $args{'action'};
128if ( @actions > 1 && $args{'datafile'} ) {
129 print STDERR "You can not use --datafile option with multiple actions.\n";
130 exit(-1);
131}
132foreach ( @actions ) {
3ffc5f4f 133 unless ( /^(?:init|create|drop|schema|acl|indexes|coredata|insert|upgrade)$/ ) {
84fb5b46
MKG
134 print STDERR "$0 called with an invalid --action parameter.\n";
135 exit(-1);
136 }
137 if ( /^(?:init|drop|upgrade)$/ && @actions > 1 ) {
138 print STDERR "You can not mix init, drop or upgrade action with any action.\n";
139 exit(-1);
140 }
141}
142
143# convert init to multiple actions
144my $init = 0;
145if ( $actions[0] eq 'init' ) {
146 if ($args{'skip-create'}) {
147 @actions = qw(schema coredata insert);
148 } else {
149 @actions = qw(create schema acl coredata insert);
150 }
151 $init = 1;
152}
153
154# set options from environment
155foreach my $key(qw(Type Host Name User Password)) {
156 next unless exists $ENV{ 'RT_DB_'. uc $key };
157 print "Using Database$key from RT_DB_". uc($key) ." environment variable.\n";
158 RT->Config->Set( "Database$key", $ENV{ 'RT_DB_'. uc $key });
159}
160
161my $db_type = RT->Config->Get('DatabaseType') || '';
162my $db_host = RT->Config->Get('DatabaseHost') || '';
01e3b242 163my $db_port = RT->Config->Get('DatabasePort') || '';
84fb5b46
MKG
164my $db_name = RT->Config->Get('DatabaseName') || '';
165my $db_user = RT->Config->Get('DatabaseUser') || '';
166my $db_pass = RT->Config->Get('DatabasePassword') || '';
167
168# load it here to get error immidiatly if DB type is not supported
169require RT::Handle;
170
171if ( $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name) ) {
172 $db_name = File::Spec->catfile($RT::VarPath, $db_name);
173 RT->Config->Set( DatabaseName => $db_name );
174}
175
3ffc5f4f 176my $dba_user = $args{'dba'} || $ENV{'RT_DBA_USER'} || RT->Config->Get('DatabaseAdmin') || '';
84fb5b46
MKG
177my $dba_pass = $args{'dba-password'} || $ENV{'RT_DBA_PASSWORD'};
178
179if ($args{'skip-create'}) {
180 $dba_user = $db_user;
181 $dba_pass = $db_pass;
182} else {
183 if ( !$args{force} && ( !defined $dba_pass || $args{'prompt-for-dba-password'} ) ) {
184 $dba_pass = get_dba_password();
185 chomp $dba_pass if defined($dba_pass);
186 }
187}
188
01e3b242
MKG
189my $version_word_regex = join '|', RT::Handle->version_words;
190my $version_dir = qr/^\d+\.\d+\.\d+(?:$version_word_regex)?\d*$/;
191
84fb5b46 192print "Working with:\n"
01e3b242 193 ."Type:\t$db_type\nHost:\t$db_host\nPort:\t$db_port\nName:\t$db_name\n"
84fb5b46
MKG
194 ."User:\t$db_user\nDBA:\t$dba_user" . ($args{'skip-create'} ? ' (No DBA)' : '') . "\n";
195
3ffc5f4f
MKG
196my $package = $args{'package'} || 'RT';
197my $ext_version = $args{'ext-version'};
198my $full_id = Data::GUID->new->as_string;
199
200my $log_actions = 0;
201if ($args{'package'} ne 'RT') {
202 RT->ConnectToDatabase();
203 RT->InitSystemObjects();
204 $log_actions = 1;
205}
206
84fb5b46
MKG
207foreach my $action ( @actions ) {
208 no strict 'refs';
209 my ($status, $msg) = *{ 'action_'. $action }{'CODE'}->( %args );
210 error($action, $msg) unless $status;
211 print $msg .".\n" if $msg;
212 print "Done.\n";
213}
214
215sub action_create {
216 my %args = @_;
217 my $dbh = get_system_dbh();
5b0d0914 218 my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'create' );
84fb5b46
MKG
219 return ($status, $msg) unless $status;
220
221 print "Now creating a $db_type database $db_name for RT.\n";
222 return RT::Handle->CreateDatabase( $dbh );
223}
224
225sub action_drop {
226 my %args = @_;
227
228 print "Dropping $db_type database $db_name.\n";
229 unless ( $args{'force'} ) {
230 print <<END;
231
01e3b242 232About to drop $db_type database $db_name on $db_host (port '$db_port').
84fb5b46
MKG
233WARNING: This will erase all data in $db_name.
234
235END
236 exit(-2) unless _yesno();
237 }
238
239 my $dbh = get_system_dbh();
240 return RT::Handle->DropDatabase( $dbh );
241}
242
243sub action_schema {
244 my %args = @_;
245 my $dbh = get_admin_dbh();
5b0d0914 246 my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'schema' );
84fb5b46
MKG
247 return ($status, $msg) unless $status;
248
3ffc5f4f
MKG
249 my $individual_id = Data::GUID->new->as_string();
250 my %upgrade_data = (
251 action => 'schema',
252 filename => Cwd::abs_path($args{'datafile'} || $args{'datadir'} || ''),
253 stage => 'before',
254 full_id => $full_id,
255 individual_id => $individual_id,
256 );
257 $upgrade_data{'ext_version'} = $ext_version if $ext_version;
258 RT->System->AddUpgradeHistory($package => \%upgrade_data) if $log_actions;
259
84fb5b46 260 print "Now populating database schema.\n";
3ffc5f4f
MKG
261 my @ret = RT::Handle->InsertSchema( $dbh, $args{'datafile'} || $args{'datadir'} );
262
263 %upgrade_data = (
264 stage => 'after',
265 individual_id => $individual_id,
266 return_value => [ @ret ],
267 );
268 RT->System->AddUpgradeHistory($package => \%upgrade_data) if $log_actions;
269
270 return @ret;
84fb5b46
MKG
271}
272
273sub action_acl {
274 my %args = @_;
275 my $dbh = get_admin_dbh();
5b0d0914 276 my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'acl' );
84fb5b46
MKG
277 return ($status, $msg) unless $status;
278
3ffc5f4f
MKG
279 my $individual_id = Data::GUID->new->as_string();
280 my %upgrade_data = (
281 action => 'acl',
282 filename => Cwd::abs_path($args{'datafile'} || $args{'datadir'} || ''),
283 stage => 'before',
284 full_id => $full_id,
285 individual_id => $individual_id,
286 );
287 $upgrade_data{'ext_version'} = $ext_version if $ext_version;
288 RT->System->AddUpgradeHistory($package => \%upgrade_data) if $log_actions;
289
84fb5b46 290 print "Now inserting database ACLs.\n";
3ffc5f4f
MKG
291 my @ret = RT::Handle->InsertACL( $dbh, $args{'datafile'} || $args{'datadir'} );
292
293 %upgrade_data = (
294 stage => 'after',
295 individual_id => $individual_id,
296 return_value => [ @ret ],
297 );
298 RT->System->AddUpgradeHistory($package => \%upgrade_data) if $log_actions;
299
300 return @ret;
301}
302
303sub action_indexes {
304 my %args = @_;
305 RT->ConnectToDatabase;
306 my $individual_id = Data::GUID->new->as_string();
307 my %upgrade_data = (
308 action => 'indexes',
309 filename => Cwd::abs_path($args{'datafile'} || $args{'datadir'} || ''),
310 stage => 'before',
311 full_id => $full_id,
312 individual_id => $individual_id,
313 );
314 $upgrade_data{'ext_version'} = $ext_version if $ext_version;
315 RT->System->AddUpgradeHistory($package => \%upgrade_data) if $log_actions;
316
317 my $dbh = get_admin_dbh();
318 $RT::Handle = RT::Handle->new;
319 $RT::Handle->dbh( $dbh );
320 RT::InitLogging();
321
322 print "Now inserting database indexes.\n";
323 my @ret = RT::Handle->InsertIndexes( $dbh, $args{'datafile'} || $args{'datadir'} );
324
325 $RT::Handle = RT::Handle->new;
326 $RT::Handle->dbh( undef );
327 RT->ConnectToDatabase;
328 %upgrade_data = (
329 stage => 'after',
330 individual_id => $individual_id,
331 return_value => [ @ret ],
332 );
333 RT->System->AddUpgradeHistory($package => \%upgrade_data) if $log_actions;
334
335 return @ret;
84fb5b46
MKG
336}
337
338sub action_coredata {
339 my %args = @_;
340 $RT::Handle = RT::Handle->new;
341 $RT::Handle->dbh( undef );
342 RT::ConnectToDatabase();
5b0d0914 343 my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'coredata' );
84fb5b46
MKG
344 return ($status, $msg) unless $status;
345
346 print "Now inserting RT core system objects.\n";
347 return $RT::Handle->InsertInitialData;
348}
349
350sub action_insert {
351 my %args = @_;
352 $RT::Handle = RT::Handle->new;
353 RT::Init();
3ffc5f4f
MKG
354 $log_actions = 1;
355
5b0d0914 356 my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'insert' );
84fb5b46
MKG
357 return ($status, $msg) unless $status;
358
359 print "Now inserting data.\n";
360 my $file = $args{'datafile'};
361 $file = $RT::EtcPath . "/initialdata" if $init && !$file;
362 $file ||= $args{'datadir'}."/content";
363
3ffc5f4f
MKG
364 my $individual_id = Data::GUID->new->as_string();
365 my %upgrade_data = (
366 action => 'insert',
367 filename => Cwd::abs_path($file),
368 stage => 'before',
369 full_id => $full_id,
370 individual_id => $individual_id
371 );
372 $upgrade_data{'ext_version'} = $ext_version if $ext_version;
373
374 open my $handle, '<', $file or warn "Unable to open $file: $!";
375 $upgrade_data{content} = do {local $/; <$handle>} if $handle;
376
377 RT->System->AddUpgradeHistory($package => \%upgrade_data);
378
379 my @ret;
380
381 my $upgrade = sub { @ret = $RT::Handle->InsertData( $file, $root_password ) };
382
383 for my $file (@{$args{backcompat} || []}) {
384 my $lines = do {local $/; local @ARGV = ($file); <>};
385 my $sub = eval "sub {\n# line 1 $file\n$lines\n}";
386 unless ($sub) {
387 warn "Failed to load backcompat $file: $@";
388 next;
84fb5b46 389 }
3ffc5f4f
MKG
390 my $current = $upgrade;
391 $upgrade = sub { $sub->($current) };
84fb5b46
MKG
392 }
393
3ffc5f4f
MKG
394 $upgrade->();
395
396 # XXX Reconnecting to insert the history entry
397 # until we can sort out removing
398 # the disconnect at the end of InsertData.
399 RT->ConnectToDatabase();
400
401 %upgrade_data = (
402 stage => 'after',
403 individual_id => $individual_id,
404 return_value => [ @ret ],
405 );
406
407 RT->System->AddUpgradeHistory($package => \%upgrade_data);
408
409 my $db_type = RT->Config->Get('DatabaseType');
410 $RT::Handle->Disconnect() unless $db_type eq 'SQLite';
84fb5b46 411
84fb5b46
MKG
412 return @ret;
413}
414
415sub action_upgrade {
416 my %args = @_;
417 my $base_dir = $args{'datadir'} || "./etc/upgrade";
418 return (0, "Couldn't read dir '$base_dir' with upgrade data")
419 unless -d $base_dir || -r _;
420
421 my $upgrading_from = undef;
422 do {
423 if ( defined $upgrading_from ) {
424 print "Doesn't match #.#.#: ";
425 } else {
01e3b242 426 print "Enter $args{package} version you're upgrading from: ";
84fb5b46 427 }
3ffc5f4f 428 $upgrading_from = $args{'upgrade-from'} || scalar <STDIN>;
84fb5b46
MKG
429 chomp $upgrading_from;
430 $upgrading_from =~ s/\s+//g;
01e3b242 431 } while $upgrading_from !~ /$version_dir/;
84fb5b46
MKG
432
433 my $upgrading_to = $RT::VERSION;
434 return (0, "The current version $upgrading_to is lower than $upgrading_from")
435 if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) > 0;
436
437 return (1, "The version $upgrading_to you're upgrading to is up to date")
438 if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) == 0;
439
440 my @versions = get_versions_from_to($base_dir, $upgrading_from, undef);
441 return (1, "No DB changes since $upgrading_from")
442 unless @versions;
443
444 if (RT::Handle::cmp_version($versions[-1], $upgrading_to) > 0) {
445 print "\n***** There are upgrades for $versions[-1], which is later than $upgrading_to,\n";
446 print "***** which you are nominally upgrading to. Upgrading to $versions[-1] instead.\n";
447 $upgrading_to = $versions[-1];
448 }
449
450 print "\nGoing to apply following upgrades:\n";
451 print map "* $_\n", @versions;
452
453 {
454 my $custom_upgrading_to = undef;
455 do {
456 if ( defined $custom_upgrading_to ) {
457 print "Doesn't match #.#.#: ";
458 } else {
01e3b242 459 print "\nEnter $args{package} version if you want to stop upgrade at some point,\n";
84fb5b46
MKG
460 print " or leave it blank if you want apply above upgrades: ";
461 }
3ffc5f4f 462 $custom_upgrading_to = $args{'upgrade-to'} || scalar <STDIN>;
84fb5b46
MKG
463 chomp $custom_upgrading_to;
464 $custom_upgrading_to =~ s/\s+//g;
465 last unless $custom_upgrading_to;
01e3b242 466 } while $custom_upgrading_to !~ /$version_dir/;
84fb5b46
MKG
467
468 if ( $custom_upgrading_to ) {
469 return (
470 0, "The version you entered ($custom_upgrading_to) is lower than\n"
471 ."version you're upgrading from ($upgrading_from)"
472 ) if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) > 0;
473
474 return (1, "The version you're upgrading to is up to date")
475 if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) == 0;
476
477 if ( RT::Handle::cmp_version( $RT::VERSION, $custom_upgrading_to ) < 0 ) {
478 print "Version you entered is greater than installed ($RT::VERSION).\n";
479 _yesno() or exit(-2);
480 }
481 # ok, checked everything no let's refresh list
482 $upgrading_to = $custom_upgrading_to;
483 @versions = get_versions_from_to($base_dir, $upgrading_from, $upgrading_to);
484
485 return (1, "No DB changes between $upgrading_from and $upgrading_to")
486 unless @versions;
487
488 print "\nGoing to apply following upgrades:\n";
489 print map "* $_\n", @versions;
490 }
491 }
492
3ffc5f4f
MKG
493 unless ( $args{'force'} ) {
494 print "\nIT'S VERY IMPORTANT TO BACK UP BEFORE THIS STEP\n\n";
495 _yesno() or exit(-2);
496 }
84fb5b46 497
3ffc5f4f
MKG
498 RT->ConnectToDatabase();
499 RT->InitSystemObjects();
500 $log_actions = 1;
501
502 RT->System->AddUpgradeHistory($package => {
503 type => 'full upgrade',
504 action => 'upgrade',
505 stage => 'before',
506 from => $upgrading_from,
507 to => $upgrading_to,
508 versions => [@versions],
509 full_id => $full_id,
510 individual_id => $full_id
511 });
512
513 # Ensure that the Attributes column is big enough to hold the
514 # upgrade steps we're going to add; this step exists in 4.0.6 for
515 # mysql, but that may be too late. Run it as soon as possible.
516 if (RT->Config->Get('DatabaseType') eq 'mysql'
517 and RT::Handle::cmp_version( $upgrading_from, '4.0.6') < 0) {
518 my $dbh = get_admin_dbh();
519 # Before the binary switch in 3.7.87, we want to alter text ->
520 # longtext, not blob -> longblob
521 if (RT::Handle::cmp_version( $upgrading_from, '3.7.87') < 0) {
522 $dbh->do("ALTER TABLE Attributes MODIFY Content LONGTEXT")
523 } else {
524 $dbh->do("ALTER TABLE Attributes MODIFY Content LONGBLOB")
525 }
526 }
527
528 my $previous = $upgrading_from;
84fb5b46
MKG
529 my ( $ret, $msg );
530 foreach my $n ( 0..$#versions ) {
531 my $v = $versions[$n];
3ffc5f4f
MKG
532 my $individual_id = Data::GUID->new->as_string();
533
84fb5b46
MKG
534 my @back = grep {-e $_} map {"$base_dir/$versions[$_]/backcompat"} $n+1..$#versions;
535 print "Processing $v\n";
3ffc5f4f
MKG
536
537 RT->System->AddUpgradeHistory($package => {
538 action => 'upgrade',
539 type => 'individual upgrade',
540 stage => 'before',
541 from => $previous,
542 to => $v,
543 full_id => $full_id,
544 individual_id => $individual_id,
545 });
546
84fb5b46 547 my %tmp = (%args, datadir => "$base_dir/$v", datafile => undef, backcompat => \@back);
3ffc5f4f 548
84fb5b46
MKG
549 if ( -e "$base_dir/$v/schema.$db_type" ) {
550 ( $ret, $msg ) = action_schema( %tmp );
551 return ( $ret, $msg ) unless $ret;
552 }
553 if ( -e "$base_dir/$v/acl.$db_type" ) {
554 ( $ret, $msg ) = action_acl( %tmp );
555 return ( $ret, $msg ) unless $ret;
556 }
3ffc5f4f
MKG
557 if ( -e "$base_dir/$v/indexes" ) {
558 ( $ret, $msg ) = action_indexes( %tmp );
559 return ( $ret, $msg ) unless $ret;
560 }
84fb5b46
MKG
561 if ( -e "$base_dir/$v/content" ) {
562 ( $ret, $msg ) = action_insert( %tmp );
563 return ( $ret, $msg ) unless $ret;
564 }
3ffc5f4f
MKG
565
566 # XXX: Another connect since the insert called
567 # previous to this step will disconnect.
568
569 RT->ConnectToDatabase();
570
571 RT->System->AddUpgradeHistory($package => {
572 stage => 'after',
573 individual_id => $individual_id,
574 });
575
576 $previous = $v;
84fb5b46 577 }
3ffc5f4f
MKG
578
579 RT->System->AddUpgradeHistory($package => {
580 stage => 'after',
581 individual_id => $full_id,
582 });
583
84fb5b46
MKG
584 return 1;
585}
586
587sub get_versions_from_to {
588 my ($base_dir, $from, $to) = @_;
589
590 opendir( my $dh, $base_dir ) or die "couldn't open dir: $!";
01e3b242 591 my @versions = grep -d "$base_dir/$_" && /$version_dir/, readdir $dh;
84fb5b46
MKG
592 closedir $dh;
593
01e3b242
MKG
594 die "\nERROR: No upgrade data found in '$base_dir'! Perhaps you specified the wrong --datadir?\n"
595 unless @versions;
596
84fb5b46
MKG
597 return
598 grep defined $to ? RT::Handle::cmp_version($_, $to) <= 0 : 1,
599 grep RT::Handle::cmp_version($_, $from) > 0,
600 sort RT::Handle::cmp_version @versions;
601}
602
603sub error {
604 my ($action, $msg) = @_;
605 print STDERR "Couldn't finish '$action' step.\n\n";
606 print STDERR "ERROR: $msg\n\n";
607 exit(-1);
608}
609
610sub get_dba_password {
611 print "In order to create or update your RT database,"
612 . " this script needs to connect to your "
01e3b242 613 . " $db_type instance on $db_host (port '$db_port') as $dba_user\n";
84fb5b46
MKG
614 print "Please specify that user's database password below. If the user has no database\n";
615 print "password, just press return.\n\n";
616 print "Password: ";
617 ReadMode('noecho');
618 my $password = ReadLine(0);
619 ReadMode('normal');
620 print "\n";
621 return ($password);
622}
623
624# get_system_dbh
625# Returns L<DBI> database handle connected to B<system> with DBA credentials.
626# See also L<RT::Handle/SystemDSN>.
627
628
629sub get_system_dbh {
630 return _get_dbh( RT::Handle->SystemDSN, $dba_user, $dba_pass );
631}
632
633sub get_admin_dbh {
634 return _get_dbh( RT::Handle->DSN, $dba_user, $dba_pass );
635}
636
637# get_rt_dbh [USER, PASSWORD]
638
639# Returns L<DBI> database handle connected to RT database,
640# you may specify credentials(USER and PASSWORD) to connect
641# with. By default connects with credentials from RT config.
642
643sub get_rt_dbh {
644 return _get_dbh( RT::Handle->DSN, $db_user, $db_pass );
645}
646
647sub _get_dbh {
648 my ($dsn, $user, $pass) = @_;
649 my $dbh = DBI->connect(
650 $dsn, $user, $pass,
651 { RaiseError => 0, PrintError => 0 },
652 );
653 unless ( $dbh ) {
654 my $msg = "Failed to connect to $dsn as user '$user': ". $DBI::errstr;
655 if ( $args{'debug'} ) {
656 require Carp; Carp::confess( $msg );
657 } else {
658 print STDERR $msg; exit -1;
659 }
660 }
661 return $dbh;
662}
663
664sub _yesno {
665 print "Proceed [y/N]:";
666 my $x = scalar(<STDIN>);
667 $x =~ /^y/i;
668}
669
6701;
671
672__END__
673
674=head1 NAME
675
676rt-setup-database - Set up RT's database
677
678=head1 SYNOPSIS
679
680 rt-setup-database --action ...
681
682=head1 OPTIONS
683
684=over
685
686=item action
687
688Several actions can be combined using comma separated list.
689
690=over
691
692=item init
693
694Initialize the database. This is combination of multiple actions listed below.
695Create DB, schema, setup acl, insert core data and initial data.
696
697=item upgrade
698
699Apply all needed schema/acl/content updates (will ask for version to upgrade
700from)
701
702=item create
703
704Create the database.
705
706=item drop
707
708Drop the database. This will B<ERASE ALL YOUR DATA>.
709
710=item schema
711
712Initialize only the database schema
713
714To use a local or supplementary datafile, specify it using the '--datadir'
715option below.
716
717=item acl
718
719Initialize only the database ACLs
720
721To use a local or supplementary datafile, specify it using the '--datadir'
722option below.
723
724=item coredata
725
726Insert data into RT's database. This data is required for normal functioning of
727any RT instance.
728
729=item insert
730
731Insert data into RT's database. By default, will use RT's installation data.
732To use a local or supplementary datafile, specify it using the '--datafile'
733option below.
734
735=back
736
737=item datafile
738
739file path of the data you want to action on
740
741e.g. C<--datafile /path/to/datafile>
742
743=item datadir
744
745Used to specify a path to find the local database schema and acls to be
746installed.
747
748e.g. C<--datadir /path/to/>
749
750=item dba
751
752dba's username
753
754=item dba-password
755
756dba's password
757
758=item prompt-for-dba-password
759
760Ask for the database administrator's password interactively
761
762=item skip-create
763
764for 'init': skip creating the database and the user account, so we don't need
765administrator privileges
766
767=item root-password-file
768
769for 'init' and 'insert': rather than using the default administrative password
770for RT's "root" user, use the password in this file.
771
3ffc5f4f
MKG
772=item package
773
774the name of the entity performing a create or upgrade. Used for logging changes
775in the DB. Defaults to RT, otherwise it should be the fully qualified package name
776of the extension or plugin making changes to the DB.
777
778=item ext-version
779
780current version of extension making a change. Not needed for RT since RT has a
781more elaborate system to track upgrades across multiple versions.
782
783=item upgrade-from
784
785for 'upgrade': specifies the version to upgrade from, and do not prompt
786for it if it appears to be a valid version.
787
788=item upgrade-to
789
790for 'upgrade': specifies the version to upgrade to, and do not prompt
791for it if it appears to be a valid version.
792
84fb5b46 793=back
3ffc5f4f
MKG
794
795=cut