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