Merge lp:~percona-toolkit-dev/percona-toolkit/release-2.2.8 into lp:percona-toolkit/2.2

Proposed by Daniel Nichter
Status: Merged
Approved by: Daniel Nichter
Approved revision: 604
Merged at revision: 605
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/release-2.2.8
Merge into: lp:percona-toolkit/2.2
Diff against target: 5689 lines (+347/-4800) (has conflicts)
59 files modified
Changelog (+10/-0)
bin/pt-archiver (+1/-1)
bin/pt-config-diff (+1/-1)
bin/pt-deadlock-logger (+17/-2)
bin/pt-diskstats (+1/-1)
bin/pt-duplicate-key-checker (+1/-1)
bin/pt-find (+1/-1)
bin/pt-fk-error-logger (+1/-1)
bin/pt-heartbeat (+1/-1)
bin/pt-index-usage (+1/-1)
bin/pt-kill (+1/-1)
bin/pt-online-schema-change (+25/-16)
bin/pt-query-digest (+1/-1)
bin/pt-slave-delay (+1/-1)
bin/pt-slave-restart (+1/-1)
bin/pt-table-checksum (+128/-17)
bin/pt-table-sync (+1/-1)
bin/pt-upgrade (+1/-1)
bin/pt-variable-advisor (+1/-1)
lib/Percona/Agent/Logger.pm (+0/-341)
lib/Percona/Test/Mock/AgentLogger.pm (+0/-129)
lib/Percona/Test/Mock/UserAgent.pm (+0/-71)
lib/Percona/Toolkit.pm (+1/-1)
lib/Percona/WebAPI/Client.pm (+0/-321)
lib/Percona/WebAPI/Exception/Request.pm (+0/-69)
lib/Percona/WebAPI/Exception/Resource.pm (+0/-66)
lib/Percona/WebAPI/Representation.pm (+0/-86)
lib/Percona/WebAPI/Resource/Agent.pm (+0/-77)
lib/Percona/WebAPI/Resource/Config.pm (+0/-55)
lib/Percona/WebAPI/Resource/LogEntry.pm (+0/-66)
lib/Percona/WebAPI/Resource/Service.pm (+0/-94)
lib/Percona/WebAPI/Resource/Task.pm (+0/-62)
t/lib/Percona/WebAPI/Client.t (+0/-236)
t/lib/Percona/WebAPI/Representation.t (+0/-51)
t/pt-agent/basics.t (+0/-101)
t/pt-agent/get_services.t (+0/-423)
t/pt-agent/init_agent.t (+0/-333)
t/pt-agent/make_new_crontab.t (+0/-151)
t/pt-agent/replace_special_vars.t (+0/-73)
t/pt-agent/run_agent.t (+0/-527)
t/pt-agent/run_service.t (+0/-503)
t/pt-agent/samples/crontab001.out (+0/-2)
t/pt-agent/samples/crontab002.in (+0/-1)
t/pt-agent/samples/crontab002.out (+0/-3)
t/pt-agent/samples/crontab003.in (+0/-3)
t/pt-agent/samples/crontab003.out (+0/-3)
t/pt-agent/samples/crontab004.in (+0/-2)
t/pt-agent/samples/crontab004.out (+0/-2)
t/pt-agent/samples/query-history/data001.json (+0/-152)
t/pt-agent/samples/query-history/data001.send (+0/-166)
t/pt-agent/samples/service001 (+0/-19)
t/pt-agent/samples/write_services001 (+0/-19)
t/pt-agent/schedule_services.t (+0/-200)
t/pt-agent/send_data.t (+0/-234)
t/pt-agent/write_services.t (+0/-108)
t/pt-online-schema-change/plugin.t (+1/-0)
t/pt-online-schema-change/samples/plugins/all_hooks.pm (+7/-0)
t/pt-table-checksum/plugin.t (+98/-0)
t/pt-table-checksum/samples/plugins/all_hooks.pm (+45/-0)
Contents conflict in bin/pt-agent
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/release-2.2.8
Reviewer Review Type Date Requested Status
Daniel Nichter Approve
Review via email: mp+221476@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Changelog'
--- Changelog 2014-02-20 08:10:16 +0000
+++ Changelog 2014-05-30 01:02:37 +0000
@@ -1,5 +1,15 @@
1Changelog for Percona Toolkit1Changelog for Percona Toolkit
22
3 * Removed pt-agent
4 * Added pt-slave-restart GTID support
5 * Added pt-table-checksum --plugin
6 * Fixed bug 1304062: --ignore-tables does not work correctly
7 * Fixed bug 1295667: pt-deadlock-logger logs incorrect ts
8 * Fixed bug 1254233: pt-mysql-summary blank InnoDB section for 5.6
9 * Fixed bug 1286250: pt-online-schema-change requests password twice
10 * Fixed bug 965553: pt-query-digest dosn't fingerprint true/false literals correctly
11 * Fixed bug 290911: pt-show-grant --ask-pass prints "Enter password" to STDOUT
12
3v2.2.7 released 2014-02-2013v2.2.7 released 2014-02-20
414
5 * Fixed bug 1279502: --version-check behaves like spyware15 * Fixed bug 1279502: --version-check behaves like spyware
616
=== renamed file 'bin/pt-agent' => 'bin/pt-agent.THIS'
=== modified file 'bin/pt-archiver'
--- bin/pt-archiver 2014-05-24 21:36:33 +0000
+++ bin/pt-archiver 2014-05-30 01:02:37 +0000
@@ -43,7 +43,7 @@
43{43{
44package Percona::Toolkit;44package Percona::Toolkit;
4545
46our $VERSION = '2.2.7';46our $VERSION = '2.2.8';
4747
48use strict;48use strict;
49use warnings FATAL => 'all';49use warnings FATAL => 'all';
5050
=== modified file 'bin/pt-config-diff'
--- bin/pt-config-diff 2014-05-24 21:36:33 +0000
+++ bin/pt-config-diff 2014-05-30 01:02:37 +0000
@@ -43,7 +43,7 @@
43{43{
44package Percona::Toolkit;44package Percona::Toolkit;
4545
46our $VERSION = '2.2.7';46our $VERSION = '2.2.8';
4747
48use strict;48use strict;
49use warnings FATAL => 'all';49use warnings FATAL => 'all';
5050
=== modified file 'bin/pt-deadlock-logger'
--- bin/pt-deadlock-logger 2014-05-24 21:36:33 +0000
+++ bin/pt-deadlock-logger 2014-05-30 01:02:37 +0000
@@ -42,7 +42,7 @@
42{42{
43package Percona::Toolkit;43package Percona::Toolkit;
4444
45our $VERSION = '2.2.7';45our $VERSION = '2.2.8';
4646
47use strict;47use strict;
48use warnings FATAL => 'all';48use warnings FATAL => 'all';
@@ -4440,12 +4440,27 @@
44404440
4441 my $dst;4441 my $dst;
4442 if ( my $dst_dsn = $o->get('dest') ) {4442 if ( my $dst_dsn = $o->get('dest') ) {
4443
4444 # set time_zone = SYSTEM , addresses https://bugs.launchpad.net/percona-toolkit/+bug/1295667
4445 my $set_tz = sub {
4446 my ($dbh) = @_;
4447 my $sql = "SET time_zone=SYSTEM /* pt-deadlock-logger */";
4448 eval {
4449 PTDEBUG && _d($dbh, $sql);
4450 $dbh->do($sql);
4451 };
4452 if ( $EVAL_ERROR ) {
4453 die "Failed to $sql: $EVAL_ERROR\n";
4454 }
4455 };
4456
4443 $dst = Cxn->new(4457 $dst = Cxn->new(
4444 dsn => $dst_dsn,4458 dsn => $dst_dsn,
4445 prev_dsn => ($src ? $src->dsn : undef),4459 prev_dsn => ($src ? $src->dsn : undef),
4446 parent => $o->get('daemonize'),4460 parent => $o->get('daemonize'),
4447 DSNParser => $dp,4461 DSNParser => $dp,
4448 OptionParser => $o,4462 OptionParser => $o,
4463 set => $set_tz,
4449 );4464 );
4450 }4465 }
44514466
@@ -5199,7 +5214,7 @@
51995214
5200 CREATE TABLE deadlocks (5215 CREATE TABLE deadlocks (
5201 server char(20) NOT NULL,5216 server char(20) NOT NULL,
5202 ts datetime NOT NULL,5217 ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
5203 thread int unsigned NOT NULL,5218 thread int unsigned NOT NULL,
5204 txn_id bigint unsigned NOT NULL,5219 txn_id bigint unsigned NOT NULL,
5205 txn_time smallint unsigned NOT NULL,5220 txn_time smallint unsigned NOT NULL,
52065221
=== modified file 'bin/pt-diskstats'
--- bin/pt-diskstats 2014-05-24 21:36:33 +0000
+++ bin/pt-diskstats 2014-05-30 01:02:37 +0000
@@ -38,7 +38,7 @@
38{38{
39package Percona::Toolkit;39package Percona::Toolkit;
4040
41our $VERSION = '2.2.7';41our $VERSION = '2.2.8';
4242
43use strict;43use strict;
44use warnings FATAL => 'all';44use warnings FATAL => 'all';
4545
=== modified file 'bin/pt-duplicate-key-checker'
--- bin/pt-duplicate-key-checker 2014-05-28 01:15:25 +0000
+++ bin/pt-duplicate-key-checker 2014-05-30 01:02:37 +0000
@@ -39,7 +39,7 @@
39{39{
40package Percona::Toolkit;40package Percona::Toolkit;
4141
42our $VERSION = '2.2.7';42our $VERSION = '2.2.8';
4343
44use strict;44use strict;
45use warnings FATAL => 'all';45use warnings FATAL => 'all';
4646
=== modified file 'bin/pt-find'
--- bin/pt-find 2014-05-24 21:36:33 +0000
+++ bin/pt-find 2014-05-30 01:02:37 +0000
@@ -35,7 +35,7 @@
35{35{
36package Percona::Toolkit;36package Percona::Toolkit;
3737
38our $VERSION = '2.2.7';38our $VERSION = '2.2.8';
3939
40use strict;40use strict;
41use warnings FATAL => 'all';41use warnings FATAL => 'all';
4242
=== modified file 'bin/pt-fk-error-logger'
--- bin/pt-fk-error-logger 2014-05-24 21:36:33 +0000
+++ bin/pt-fk-error-logger 2014-05-30 01:02:37 +0000
@@ -37,7 +37,7 @@
37{37{
38package Percona::Toolkit;38package Percona::Toolkit;
3939
40our $VERSION = '2.2.7';40our $VERSION = '2.2.8';
4141
42use strict;42use strict;
43use warnings FATAL => 'all';43use warnings FATAL => 'all';
4444
=== modified file 'bin/pt-heartbeat'
--- bin/pt-heartbeat 2014-05-24 21:36:33 +0000
+++ bin/pt-heartbeat 2014-05-30 01:02:37 +0000
@@ -38,7 +38,7 @@
38{38{
39package Percona::Toolkit;39package Percona::Toolkit;
4040
41our $VERSION = '2.2.7';41our $VERSION = '2.2.8';
4242
43use strict;43use strict;
44use warnings FATAL => 'all';44use warnings FATAL => 'all';
4545
=== modified file 'bin/pt-index-usage'
--- bin/pt-index-usage 2014-05-28 17:03:30 +0000
+++ bin/pt-index-usage 2014-05-30 01:02:37 +0000
@@ -45,7 +45,7 @@
45{45{
46package Percona::Toolkit;46package Percona::Toolkit;
4747
48our $VERSION = '2.2.7';48our $VERSION = '2.2.8';
4949
50use strict;50use strict;
51use warnings FATAL => 'all';51use warnings FATAL => 'all';
5252
=== modified file 'bin/pt-kill'
--- bin/pt-kill 2014-05-28 17:03:30 +0000
+++ bin/pt-kill 2014-05-30 01:02:37 +0000
@@ -47,7 +47,7 @@
47{47{
48package Percona::Toolkit;48package Percona::Toolkit;
4949
50our $VERSION = '2.2.7';50our $VERSION = '2.2.8';
5151
52use strict;52use strict;
53use warnings FATAL => 'all';53use warnings FATAL => 'all';
5454
=== modified file 'bin/pt-online-schema-change'
--- bin/pt-online-schema-change 2014-05-24 21:36:33 +0000
+++ bin/pt-online-schema-change 2014-05-30 01:02:37 +0000
@@ -54,7 +54,7 @@
54{54{
55package Percona::Toolkit;55package Percona::Toolkit;
5656
57our $VERSION = '2.2.7';57our $VERSION = '2.2.8';
5858
59use strict;59use strict;
60use warnings FATAL => 'all';60use warnings FATAL => 'all';
@@ -3771,7 +3771,7 @@
37713771
3772 my $dbh = $self->{dbh};3772 my $dbh = $self->{dbh};
3773 if ( !$dbh || !$dbh->ping() ) {3773 if ( !$dbh || !$dbh->ping() ) {
3774 if ( $self->{ask_pass} && !$self->{asked_for_pass} ) {3774 if ( $self->{ask_pass} && !$self->{asked_for_pass} && !defined $dsn->{p}) {
3775 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");3775 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
3776 $self->{asked_for_pass} = 1;3776 $self->{asked_for_pass} = 1;
3777 }3777 }
@@ -8105,21 +8105,29 @@
8105 return;8105 return;
8106 };8106 };
81078107
8108 my $get_lag = sub {8108 my $get_lag;
8109 my ($cxn) = @_;8109 # The plugin is able to override the slavelag check so tools like
8110 my $dbh = $cxn->dbh();8110 # pt-heartbeat or other replicators (Tungsten...) can be used to
8111 if ( !$dbh || !$dbh->ping() ) {8111 # measure replication lag
8112 eval { $dbh = $cxn->connect() }; # connect or die trying8112 if ( $plugin && $plugin->can('get_slave_lag') ) {
8113 if ( $EVAL_ERROR ) {8113 $get_lag = $plugin->get_slave_lag(oktorun => \$oktorun);
8114 $oktorun = 0; # flag for cleanup tasks8114 } else {
8115 chomp $EVAL_ERROR;8115 $get_lag = sub {
8116 die "Lost connection to replica " . $cxn->name()8116 my ($cxn) = @_;
8117 . " while attempting to get its lag ($EVAL_ERROR)\n";8117 my $dbh = $cxn->dbh();
8118 if ( !$dbh || !$dbh->ping() ) {
8119 eval { $dbh = $cxn->connect() }; # connect or die trying
8120 if ( $EVAL_ERROR ) {
8121 $oktorun = 0; # flag for cleanup tasks
8122 chomp $EVAL_ERROR;
8123 die "Lost connection to replica " . $cxn->name()
8124 . " while attempting to get its lag ($EVAL_ERROR)\n";
8125 }
8118 }8126 }
8119 }8127 return $ms->get_slave_lag($dbh);
8120 return $ms->get_slave_lag($dbh);8128 };
8121 };8129 }
81228130
8123 $replica_lag = new ReplicaLagWaiter(8131 $replica_lag = new ReplicaLagWaiter(
8124 slaves => $slave_lag_cxns,8132 slaves => $slave_lag_cxns,
8125 max_lag => $o->get('max-lag'),8133 max_lag => $o->get('max-lag'),
@@ -11345,6 +11353,7 @@
11345 after_drop_old_table11353 after_drop_old_table
11346 before_drop_triggers11354 before_drop_triggers
11347 before_exit11355 before_exit
11356 get_slave_lag
1134811357
11349Each hook is passed different arguments. To see which arguments are passed11358Each hook is passed different arguments. To see which arguments are passed
11350to a hook, search for the hook's name in the tool's source code, like:11359to a hook, search for the hook's name in the tool's source code, like:
1135111360
=== modified file 'bin/pt-query-digest'
--- bin/pt-query-digest 2014-05-28 17:03:30 +0000
+++ bin/pt-query-digest 2014-05-30 01:02:37 +0000
@@ -64,7 +64,7 @@
64{64{
65package Percona::Toolkit;65package Percona::Toolkit;
6666
67our $VERSION = '2.2.7';67our $VERSION = '2.2.8';
6868
69use strict;69use strict;
70use warnings FATAL => 'all';70use warnings FATAL => 'all';
7171
=== modified file 'bin/pt-slave-delay'
--- bin/pt-slave-delay 2014-05-24 21:36:33 +0000
+++ bin/pt-slave-delay 2014-05-30 01:02:37 +0000
@@ -40,7 +40,7 @@
40{40{
41package Percona::Toolkit;41package Percona::Toolkit;
4242
43our $VERSION = '2.2.7';43our $VERSION = '2.2.8';
4444
45use strict;45use strict;
46use warnings FATAL => 'all';46use warnings FATAL => 'all';
4747
=== modified file 'bin/pt-slave-restart'
--- bin/pt-slave-restart 2014-05-28 22:25:08 +0000
+++ bin/pt-slave-restart 2014-05-30 01:02:37 +0000
@@ -41,7 +41,7 @@
41{41{
42package Percona::Toolkit;42package Percona::Toolkit;
4343
44our $VERSION = '2.2.7';44our $VERSION = '2.2.8';
4545
46use strict;46use strict;
47use warnings FATAL => 'all';47use warnings FATAL => 'all';
4848
=== modified file 'bin/pt-table-checksum'
--- bin/pt-table-checksum 2014-05-28 01:15:25 +0000
+++ bin/pt-table-checksum 2014-05-30 01:02:37 +0000
@@ -57,7 +57,7 @@
57{57{
58package Percona::Toolkit;58package Percona::Toolkit;
5959
60our $VERSION = '2.2.7';60our $VERSION = '2.2.8';
6161
62use strict;62use strict;
63use warnings FATAL => 'all';63use warnings FATAL => 'all';
@@ -9222,6 +9222,30 @@
9222 my $slaves = []; # all slaves (that we can find)9222 my $slaves = []; # all slaves (that we can find)
9223 my $slave_lag_cxns; # slaves whose lag we'll check9223 my $slave_lag_cxns; # slaves whose lag we'll check
92249224
9225 # ########################################################################
9226 # Create --plugin.
9227 # ########################################################################
9228 my $plugin;
9229 if ( my $file = $o->get('plugin') ) {
9230 die "--plugin file $file does not exist\n" unless -f $file;
9231 eval {
9232 require $file;
9233 };
9234 die "Error loading --plugin $file: $EVAL_ERROR" if $EVAL_ERROR;
9235 eval {
9236 $plugin = pt_table_checksum_plugin->new(
9237 master_cxn => $master_cxn,
9238 explain => $o->get('explain'),
9239 quiet => $o->get('quiet'),
9240 resume => $o->get('resume'),
9241 Quoter => $q,
9242 TableParser => $tp,
9243 );
9244 };
9245 die "Error creating --plugin: $EVAL_ERROR" if $EVAL_ERROR;
9246 print "Created plugin from $file.\n";
9247 }
9248
9225 my $replica_lag; # ReplicaLagWaiter object9249 my $replica_lag; # ReplicaLagWaiter object
9226 my $replica_lag_pr; # Progress for ReplicaLagWaiter9250 my $replica_lag_pr; # Progress for ReplicaLagWaiter
9227 my $sys_load; # MySQLStatusWaiter object9251 my $sys_load; # MySQLStatusWaiter object
@@ -9446,6 +9470,11 @@
9446 # #####################################################################9470 # #####################################################################
9447 if ( $o->get('replicate-check') && $o->get('replicate-check-only') ) {9471 if ( $o->get('replicate-check') && $o->get('replicate-check-only') ) {
9448 PTDEBUG && _d('Will --replicate-check and exit');9472 PTDEBUG && _d('Will --replicate-check and exit');
9473
9474 # --plugin hook
9475 if ( $plugin && $plugin->can('before_replicate_check') ) {
9476 $plugin->before_replicate_check();
9477 }
94499478
9450 foreach my $slave ( @$slaves ) {9479 foreach my $slave ( @$slaves ) {
9451 my $diffs = $rc->find_replication_differences(9480 my $diffs = $rc->find_replication_differences(
@@ -9466,6 +9495,11 @@
9466 }9495 }
9467 }9496 }
94689497
9498 # --plugin hook
9499 if ( $plugin && $plugin->can('after_replicate_check') ) {
9500 $plugin->after_replicate_check();
9501 }
9502
9469 PTDEBUG && _d('Exit status', $exit_status, 'oktorun', $oktorun);9503 PTDEBUG && _d('Exit status', $exit_status, 'oktorun', $oktorun);
9470 return $exit_status;9504 return $exit_status;
9471 }9505 }
@@ -9544,23 +9578,31 @@
9544 return;9578 return;
9545 };9579 };
95469580
9547 my $get_lag = sub {9581 my $get_lag;
9548 my ($cxn) = @_;9582 # The plugin is able to override the slavelag check so tools like
9549 my $dbh = $cxn->dbh();9583 # pt-heartbeat or other replicators (Tungsten...) can be used to
9550 if ( !$dbh || !$dbh->ping() ) {9584 # measure replication lag
9551 PTDEBUG && _d('Lost connection to slave', $cxn->name(),9585 if ( $plugin && $plugin->can('get_slave_lag') ) {
9552 'while waiting for slave lag');9586 $get_lag = $plugin->get_slave_lag(oktorun => \$oktorun);
9553 eval { $dbh = $cxn->connect() }; # connect or die trying9587 } else {
9554 if ( $EVAL_ERROR ) {9588 $get_lag = sub {
9555 $oktorun = 0; # Fatal error9589 my ($cxn) = @_;
9556 chomp $EVAL_ERROR;9590 my $dbh = $cxn->dbh();
9557 die "Lost connection to replica " . $cxn->name()9591 if ( !$dbh || !$dbh->ping() ) {
9558 . " while attempting to get its lag ($EVAL_ERROR)";9592 PTDEBUG && _d('Lost connection to slave', $cxn->name(),
9593 'while waiting for slave lag');
9594 eval { $dbh = $cxn->connect() }; # connect or die trying
9595 if ( $EVAL_ERROR ) {
9596 $oktorun = 0; # Fatal error
9597 chomp $EVAL_ERROR;
9598 die "Lost connection to replica " . $cxn->name()
9599 . " while attempting to get its lag ($EVAL_ERROR)";
9600 }
9559 }9601 }
9560 }9602 return $ms->get_slave_lag($dbh);
9561 return $ms->get_slave_lag($dbh);9603 };
9562 };9604 }
95639605
9564 $replica_lag = new ReplicaLagWaiter(9606 $replica_lag = new ReplicaLagWaiter(
9565 slaves => $slave_lag_cxns,9607 slaves => $slave_lag_cxns,
9566 max_lag => $o->get('max-lag'),9608 max_lag => $o->get('max-lag'),
@@ -10168,6 +10210,19 @@
10168 };10210 };
1016910211
10170 # ########################################################################10212 # ########################################################################
10213 # Init the --plugin.
10214 # ########################################################################
10215
10216 # --plugin hook
10217 if ( $plugin && $plugin->can('init') ) {
10218 $plugin->init(
10219 slaves => $slaves,
10220 slave_lag_cxns => $slave_lag_cxns,
10221 repl_table => $repl_table,
10222 );
10223 }
10224
10225 # ########################################################################
10171 # Checksum each table.10226 # Checksum each table.
10172 # ########################################################################10227 # ########################################################################
1017310228
@@ -10271,6 +10326,12 @@
10271 @$all_cols;10326 @$all_cols;
10272 $tbl->{checksum_cols} = \@cols;10327 $tbl->{checksum_cols} = \@cols;
1027310328
10329 # --plugin hook
10330 if ( $plugin && $plugin->can('before_checksum_table') ) {
10331 $plugin->before_checksum_table(
10332 tbl => $tbl);
10333 }
10334
10274 # Finally, checksum the table.10335 # Finally, checksum the table.
10275 # The "1 while" loop is necessary because we're executing REPLACE10336 # The "1 while" loop is necessary because we're executing REPLACE
10276 # statements which don't return rows and NibbleIterator only10337 # statements which don't return rows and NibbleIterator only
@@ -10279,6 +10340,11 @@
10279 # from the done callback, uses this start time.10340 # from the done callback, uses this start time.
10280 $tbl->{checksum_results}->{start_time} = time;10341 $tbl->{checksum_results}->{start_time} = time;
10281 1 while $nibble_iter->next();10342 1 while $nibble_iter->next();
10343
10344 # --plugin hook
10345 if ( $plugin && $plugin->can('after_checksum_table') ) {
10346 $plugin->after_checksum_table();
10347 }
10282 }10348 }
10283 };10349 };
10284 if ( $EVAL_ERROR ) {10350 if ( $EVAL_ERROR ) {
@@ -12053,6 +12119,18 @@
12053tool will overwrite the PID file with the current PID. The PID file is12119tool will overwrite the PID file with the current PID. The PID file is
12054removed automatically when the tool exits.12120removed automatically when the tool exits.
1205512121
12122=item --plugin
12123
12124type: string
12125
12126Perl module file that defines a C<pt_table_checksum_plugin> class.
12127A plugin allows you to write a Perl module that can hook into many parts
12128of pt-table-checksum. This requires a good knowledge of Perl and
12129Percona Toolkit conventions, which are beyond this scope of this
12130documentation. Please contact Percona if you have questions or need help.
12131
12132See L<"PLUGIN"> for more information.
12133
12056=item --port12134=item --port
1205712135
12058short form: -P; type: int; group: Connection12136short form: -P; type: int; group: Connection
@@ -12401,6 +12479,39 @@
1240112479
12402=back12480=back
1240312481
12482=head1 PLUGIN
12483
12484The file specified by L<"--plugin"> must define a class (i.e. a package)
12485called C<pt_table_checksum_plugin> with a C<new()> subroutine.
12486The tool will create an instance of this class and call any hooks that
12487it defines. No hooks are required, but a plugin isn't very useful without
12488them.
12489
12490These hooks, in this order, are called if defined:
12491
12492 init
12493 before_replicate_check
12494 after_replicate_check
12495 get_slave_lag
12496 before_checksum_table
12497 after_checksum_table
12498
12499Each hook is passed different arguments. To see which arguments are passed
12500to a hook, search for the hook's name in the tool's source code, like:
12501
12502 # --plugin hook
12503 if ( $plugin && $plugin->can('init') ) {
12504 $plugin->init(
12505 slaves => $slaves,
12506 slave_lag_cxns => $slave_lag_cxns,
12507 repl_table => $repl_table,
12508 );
12509 }
12510
12511The comment C<# --plugin hook> precedes every hook call.
12512
12513Please contact Percona if you have questions or need help.
12514
12404=head1 DSN OPTIONS12515=head1 DSN OPTIONS
1240512516
12406These DSN options are used to create a DSN. Each option is given like12517These DSN options are used to create a DSN. Each option is given like
1240712518
=== modified file 'bin/pt-table-sync'
--- bin/pt-table-sync 2014-05-28 01:15:25 +0000
+++ bin/pt-table-sync 2014-05-30 01:02:37 +0000
@@ -55,7 +55,7 @@
55{55{
56package Percona::Toolkit;56package Percona::Toolkit;
5757
58our $VERSION = '2.2.7';58our $VERSION = '2.2.8';
5959
60use strict;60use strict;
61use warnings FATAL => 'all';61use warnings FATAL => 'all';
6262
=== modified file 'bin/pt-upgrade'
--- bin/pt-upgrade 2014-05-28 17:03:30 +0000
+++ bin/pt-upgrade 2014-05-30 01:02:37 +0000
@@ -61,7 +61,7 @@
61{61{
62package Percona::Toolkit;62package Percona::Toolkit;
6363
64our $VERSION = '2.2.7';64our $VERSION = '2.2.8';
6565
66use strict;66use strict;
67use warnings FATAL => 'all';67use warnings FATAL => 'all';
6868
=== modified file 'bin/pt-variable-advisor'
--- bin/pt-variable-advisor 2014-05-24 21:36:33 +0000
+++ bin/pt-variable-advisor 2014-05-30 01:02:37 +0000
@@ -44,7 +44,7 @@
44{44{
45package Percona::Toolkit;45package Percona::Toolkit;
4646
47our $VERSION = '2.2.7';47our $VERSION = '2.2.8';
4848
49use strict;49use strict;
50use warnings FATAL => 'all';50use warnings FATAL => 'all';
5151
=== removed directory 'lib/Percona/Agent'
=== removed file 'lib/Percona/Agent/Logger.pm'
--- lib/Percona/Agent/Logger.pm 2013-12-11 03:07:36 +0000
+++ lib/Percona/Agent/Logger.pm 1970-01-01 00:00:00 +0000
@@ -1,341 +0,0 @@
1# This program is copyright 2013 Percona Ireland Ltd.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::Agent::Logger package
19# ###########################################################################
20package Percona::Agent::Logger;
21
22use strict;
23use warnings FATAL => 'all';
24use English qw(-no_match_vars);
25
26use constant PTDEBUG => $ENV{PTDEBUG} || 0;
27
28use POSIX qw(SIGALRM);
29
30use Lmo;
31use Transformers;
32use Percona::WebAPI::Resource::LogEntry;
33
34Transformers->import(qw(ts));
35
36has 'exit_status' => (
37 is => 'rw',
38 isa => 'ScalarRef',
39 required => 1,
40);
41
42has 'pid' => (
43 is => 'ro',
44 isa => 'Int',
45 required => 1,
46);
47
48has 'service' => (
49 is => 'rw',
50 isa => 'Maybe[Str]',
51 required => 0,
52 default => sub { return; },
53);
54
55has 'data_ts' => (
56 is => 'rw',
57 isa => 'Maybe[Int]',
58 required => 0,
59 default => sub { return; },
60);
61
62has 'online_logging' => (
63 is => 'ro',
64 isa => 'Bool',
65 required => 0,
66 default => sub { return 1 },
67);
68
69has 'online_logging_enabled' => (
70 is => 'rw',
71 isa => 'Bool',
72 required => 0,
73 default => sub { return 0 },
74);
75
76has 'quiet' => (
77 is => 'rw',
78 isa => 'Int',
79 required => 0,
80 default => sub { return 0 },
81);
82
83has '_buffer' => (
84 is => 'rw',
85 isa => 'ArrayRef',
86 required => 0,
87 default => sub { return []; },
88);
89
90has '_pipe_write' => (
91 is => 'rw',
92 isa => 'Maybe[FileHandle]',
93 required => 0,
94);
95
96sub read_stdin {
97 my ( $t ) = @_;
98
99 # Set the SIGALRM handler.
100 POSIX::sigaction(
101 SIGALRM,
102 POSIX::SigAction->new(sub { die 'read timeout'; }),
103 ) or die "Error setting SIGALRM handler: $OS_ERROR";
104
105 my $timeout = 0;
106 my @lines;
107 eval {
108 alarm $t;
109 while(defined(my $line = <STDIN>)) {
110 push @lines, $line;
111 }
112 alarm 0;
113 };
114 if ( $EVAL_ERROR ) {
115 PTDEBUG && _d('Read error:', $EVAL_ERROR);
116 die $EVAL_ERROR unless $EVAL_ERROR =~ m/read timeout/;
117 $timeout = 1;
118 }
119 return unless scalar @lines || $timeout;
120 return \@lines;
121}
122
123sub start_online_logging {
124 my ($self, %args) = @_;
125 my $client = $args{client};
126 my $log_link = $args{log_link};
127 my $read_timeout = $args{read_timeout} || 3;
128
129 return unless $self->online_logging;
130
131 my $pid = open(my $pipe_write, "|-");
132
133 if ($pid) {
134 # parent
135 select $pipe_write;
136 $OUTPUT_AUTOFLUSH = 1;
137 $self->_pipe_write($pipe_write);
138 $self->online_logging_enabled(1);
139 }
140 else {
141 # child
142 my @log_entries;
143 my $n_errors = 0;
144 my $oktorun = 1;
145 QUEUE:
146 while ($oktorun) {
147 my $lines = read_stdin($read_timeout);
148 last QUEUE unless $lines;
149 LINE:
150 while ( defined(my $line = shift @$lines) ) {
151 # $line = ts,level,n_lines,message
152 my ($ts, $level, $n_lines, $msg) = $line =~ m/^([^,]+),([^,]+),([^,]+),(.+)/s;
153 if ( !$ts || !$level || !$n_lines || !$msg ) {
154 warn "$line\n";
155 next LINE;
156 }
157 if ( $n_lines > 1 ) {
158 $n_lines--; # first line
159 for ( 1..$n_lines ) {
160 $msg .= shift @$lines;
161 }
162 }
163
164 push @log_entries, Percona::WebAPI::Resource::LogEntry->new(
165 pid => $self->pid,
166 entry_ts => $ts,
167 log_level => $level,
168 message => $msg,
169 ($self->service ? (service => $self->service) : ()),
170 ($self->data_ts ? (data_ts => $self->data_ts) : ()),
171 );
172 } # LINE
173
174 if ( scalar @log_entries ) {
175 eval {
176 $client->post(
177 link => $log_link,
178 resources => \@log_entries,
179 );
180 };
181 if ( my $e = $EVAL_ERROR ) {
182 # Safegaurd: don't spam the agent log file with errors.
183 if ( ++$n_errors <= 10 ) {
184 warn "Error sending log entry to API: $e";
185 if ( $n_errors == 10 ) {
186 my $ts = ts(time, 1); # 1=UTC
187 warn "$ts WARNING $n_errors consecutive errors, no more "
188 . "error messages will be printed until log entries "
189 . "are sent successfully again.\n";
190 }
191 }
192 }
193 else {
194 @log_entries = ();
195 $n_errors = 0;
196 }
197 } # have log entries
198
199 # Safeguard: don't use too much memory if we lose connection
200 # to the API for a long time.
201 my $n_log_entries = scalar @log_entries;
202 if ( $n_log_entries > 1_000 ) {
203 warn "$n_log_entries log entries in send buffer, "
204 . "removing first 100 to avoid excessive usage.\n";
205 @log_entries = @log_entries[100..($n_log_entries-1)];
206 }
207 } # QUEUE
208
209 if ( scalar @log_entries ) {
210 my $ts = ts(time, 1); # 1=UTC
211 warn "$ts WARNING Failed to send these log entries "
212 . "(timestamps are UTC):\n";
213 foreach my $log ( @log_entries ) {
214 warn sprintf("%s %s %s\n",
215 $log->entry_ts,
216 level_name($log->log_level),
217 $log->message,
218 );
219 }
220 }
221
222 exit 0;
223 } # child
224
225 return;
226}
227
228sub level_number {
229 my $name = shift;
230 die "No log level name given" unless $name;
231 my $number = $name eq 'DEBUG' ? 1
232 : $name eq 'INFO' ? 2
233 : $name eq 'WARNING' ? 3
234 : $name eq 'ERROR' ? 4
235 : $name eq 'FATAL' ? 5
236 : die "Invalid log level name: $name";
237}
238
239sub level_name {
240 my $number = shift;
241 die "No log level name given" unless $number;
242 my $name = $number == 1 ? 'DEBUG'
243 : $number == 2 ? 'INFO'
244 : $number == 3 ? 'WARNING'
245 : $number == 4 ? 'ERROR'
246 : $number == 5 ? 'FATAL'
247 : die "Invalid log level number: $number";
248}
249
250sub debug {
251 my $self = shift;
252 return if $self->online_logging;
253 return $self->_log(0, 'DEBUG', @_);
254}
255
256sub info {
257 my $self = shift;
258 return $self->_log(1, 'INFO', @_);
259}
260
261sub warning {
262 my $self = shift;
263 $self->_set_exit_status();
264 return $self->_log(1, 'WARNING', @_);
265}
266
267sub error {
268 my $self = shift;
269 $self->_set_exit_status();
270 return $self->_log(1, 'ERROR', @_);
271}
272
273sub fatal {
274 my $self = shift;
275 $self->_set_exit_status();
276 $self->_log(1, 'FATAL', @_);
277 exit $self->exit_status;
278}
279
280sub _set_exit_status {
281 my $self = shift;
282 # exit_status is a scalar ref
283 my $exit_status = $self->exit_status; # get ref
284 $$exit_status |= 1; # deref to set
285 $self->exit_status($exit_status); # save back ref
286 return;
287}
288
289sub _log {
290 my ($self, $online, $level, $msg) = @_;
291
292 my $ts = ts(time, 1); # 1=UTC
293 my $level_number = level_number($level);
294
295 return if $self->quiet && $level_number < $self->quiet;
296
297 chomp($msg);
298 my $n_lines = 1;
299 $n_lines++ while $msg =~ m/\n/g;
300
301 if ( $online && $self->online_logging_enabled ) {
302 while ( defined(my $log_entry = shift @{$self->_buffer}) ) {
303 $self->_queue_log_entry(@$log_entry);
304 }
305 $self->_queue_log_entry($ts, $level_number, $n_lines, $msg);
306 }
307 else {
308 if ( $online && $self->online_logging ) {
309 push @{$self->_buffer}, [$ts, $level_number, $n_lines, $msg];
310 }
311
312 if ( $level_number >= 3 ) { # warning
313 print STDERR "$ts $level $msg\n";
314 }
315 else {
316 print STDOUT "$ts $level $msg\n";
317 }
318 }
319
320 return;
321}
322
323sub _queue_log_entry {
324 my ($self, $ts, $log_level, $n_lines, $msg) = @_;
325 print "$ts,$log_level,$n_lines,$msg\n";
326 return;
327}
328
329sub _d {
330 my ($package, undef, $line) = caller 0;
331 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
332 map { defined $_ ? $_ : 'undef' }
333 @_;
334 print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
335}
336
337no Lmo;
3381;
339# ###########################################################################
340# End Percona::Agent::Logger package
341# ###########################################################################
3420
=== removed directory 'lib/Percona/Test'
=== removed directory 'lib/Percona/Test/Mock'
=== removed file 'lib/Percona/Test/Mock/AgentLogger.pm'
--- lib/Percona/Test/Mock/AgentLogger.pm 2013-06-17 00:28:18 +0000
+++ lib/Percona/Test/Mock/AgentLogger.pm 1970-01-01 00:00:00 +0000
@@ -1,129 +0,0 @@
1# This program is copyright 2013 Percona Ireland Ltd.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::Agent::Logger package
19# ###########################################################################
20package Percona::Test::Mock::AgentLogger;
21
22use strict;
23use warnings FATAL => 'all';
24use English qw(-no_match_vars);
25
26use constant PTDEBUG => $ENV{PTDEBUG} || 0;
27
28sub new {
29 my ($class, %args) = @_;
30 my $self = {
31 log => $args{log},
32
33 exit_status => $args{exit_status},
34 pid => $args{pid},
35 online_logging => $args{online_logging},
36
37 service => undef,
38 data_ts => undef,
39 quiet => 0,
40
41 };
42 return bless $self, $class;
43}
44
45sub service {
46 my $self = shift;
47 my $_service = shift;
48 $self->{service} = $_service if $_service;
49 return $self->{service};
50}
51
52sub data_ts {
53 my $self = shift;
54 my $_data_ts = shift;
55 $self->{data_ts} = $_data_ts if $_data_ts;
56 return $self->{data_ts};
57}
58
59sub quiet {
60 my $self = shift;
61 my $_quiet = shift;
62 $self->{quiet} = $_quiet if $_quiet;
63 return $self->{quiet};
64}
65
66sub start_online_logging {
67 my ($self, %args) = @_;
68 $self->_log('-', 'Called start_online_logging()');
69 return;
70}
71
72sub level_number {
73 my $name = shift;
74 die "No log level name given" unless $name;
75 my $number = $name eq 'DEBUG' ? 1
76 : $name eq 'INFO' ? 2
77 : $name eq 'WARNING' ? 3
78 : $name eq 'ERROR' ? 4
79 : $name eq 'FATAL' ? 5
80 : die "Invalid log level name: $name";
81}
82
83sub level_name {
84 my $number = shift;
85 die "No log level name given" unless $number;
86 my $name = $number == 1 ? 'DEBUG'
87 : $number == 2 ? 'INFO'
88 : $number == 3 ? 'WARNING'
89 : $number == 4 ? 'ERROR'
90 : $number == 5 ? 'FATAL'
91 : die "Invalid log level number: $number";
92}
93
94sub debug {
95 my $self = shift;
96 return $self->_log('DEBUG', @_);
97}
98
99sub info {
100 my $self = shift;
101 return $self->_log('INFO', @_);
102}
103
104sub warning {
105 my $self = shift;
106 return $self->_log('WARNING', @_);
107}
108
109sub error {
110 my $self = shift;
111 return $self->_log('ERROR', @_);
112}
113
114sub fatal {
115 my $self = shift;
116 $self->_log('FATAL', @_);
117 return 255;
118}
119
120sub _log {
121 my ($self, $level, $msg) = @_;
122 push @{$self->{log}}, "$level $msg";
123 return;
124}
125
1261;
127# ###########################################################################
128# End Percona::Test::Mock::AgentLogger package
129# ###########################################################################
1300
=== removed file 'lib/Percona/Test/Mock/UserAgent.pm'
--- lib/Percona/Test/Mock/UserAgent.pm 2013-03-21 19:50:49 +0000
+++ lib/Percona/Test/Mock/UserAgent.pm 1970-01-01 00:00:00 +0000
@@ -1,71 +0,0 @@
1# This program is copyright 2012-2013 Percona Inc.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::Test::Mock::UserAgent package
19# ###########################################################################
20{
21package Percona::Test::Mock::UserAgent;
22
23sub new {
24 my ($class, %args) = @_;
25 my $self = {
26 encode => $args{encode} || sub { return $_[0] },
27 decode => $args{decode} || sub { return $_[0] },
28 requests => [],
29 request_objs => [],
30 responses => {
31 get => [],
32 post => [],
33 put => [],
34 },
35 content => {
36 post => [],
37 put => [],
38 },
39 };
40 return bless $self, $class;
41}
42
43sub request {
44 my ($self, $req) = @_;
45 if ( scalar @{$self->{request_objs}} > 10 ) {
46 $self->{request_objs} = [];
47 }
48 push @{$self->{request_objs}}, $req;
49 my $type = lc($req->method);
50 push @{$self->{requests}}, uc($type) . ' ' . $req->uri;
51 if ( $type eq 'post' || $type eq 'put' ) {
52 push @{$self->{content}->{$type}}, $req->content;
53 }
54 my $r = shift @{$self->{responses}->{$type}};
55 my $c = $r->{content} ? $self->{encode}->($r->{content}) : '';
56 my $h = HTTP::Headers->new;
57 $h->header(%{$r->{headers}}) if exists $r->{headers};
58 my $res = HTTP::Response->new(
59 $r->{code} || 200,
60 '',
61 $h,
62 $c,
63 );
64 return $res;
65}
66
671;
68}
69# ###########################################################################
70# End Percona::Test::Mock::UserAgent package
71# ###########################################################################
720
=== modified file 'lib/Percona/Toolkit.pm'
--- lib/Percona/Toolkit.pm 2014-02-20 03:47:43 +0000
+++ lib/Percona/Toolkit.pm 2014-05-30 01:02:37 +0000
@@ -18,7 +18,7 @@
18# ###########################################################################18# ###########################################################################
19package Percona::Toolkit;19package Percona::Toolkit;
2020
21our $VERSION = '2.2.7';21our $VERSION = '2.2.8';
2222
23use strict;23use strict;
24use warnings FATAL => 'all';24use warnings FATAL => 'all';
2525
=== removed directory 'lib/Percona/WebAPI'
=== removed file 'lib/Percona/WebAPI/Client.pm'
--- lib/Percona/WebAPI/Client.pm 2013-09-19 04:05:48 +0000
+++ lib/Percona/WebAPI/Client.pm 1970-01-01 00:00:00 +0000
@@ -1,321 +0,0 @@
1# This program is copyright 2012 codenode LLC, 2012-2013 Percona Ireland Ltd.
2#
3# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
4# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
5# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
6#
7# This program is free software; you can redistribute it and/or modify it under
8# the terms of the GNU General Public License as published by the Free Software
9# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
10# systems, you can issue `man perlgpl' or `man perlartistic' to read these
11# licenses.
12#
13# You should have received a copy of the GNU General Public License along with
14# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15# Place, Suite 330, Boston, MA 02111-1307 USA.
16# ###########################################################################
17# Percona::WebAPI::Client package
18# ###########################################################################
19{
20package Percona::WebAPI::Client;
21
22our $VERSION = '0.01';
23
24use strict;
25use warnings FATAL => 'all';
26use English qw(-no_match_vars);
27use constant PTDEBUG => $ENV{PTDEBUG} || 0;
28
29eval {
30 require LWP;
31 require JSON;
32};
33
34use Scalar::Util qw(blessed);
35
36use Lmo;
37use Percona::Toolkit;
38use Percona::WebAPI::Representation;
39use Percona::WebAPI::Exception::Request;
40use Percona::WebAPI::Exception::Resource;
41
42Percona::WebAPI::Representation->import(qw(as_json));
43Percona::Toolkit->import(qw(_d Dumper have_required_args));
44
45has 'api_key' => (
46 is => 'ro',
47 isa => 'Str',
48 required => 1,
49);
50
51has 'entry_link' => (
52 is => 'rw',
53 isa => 'Str',
54 required => 0,
55 default => sub { return 'https://cloud-api.percona.com' },
56);
57
58has 'ua' => (
59 is => 'rw',
60 isa => 'Object',
61 lazy => 1,
62 required => 0,
63 builder => '_build_ua',
64);
65
66has 'response' => (
67 is => 'rw',
68 isa => 'Object',
69 required => 0,
70 default => undef,
71);
72
73sub _build_ua {
74 my $self = shift;
75 my $ua = LWP::UserAgent->new;
76 $ua->agent("Percona::WebAPI::Client/$Percona::WebAPI::Client::VERSION");
77 $ua->default_header('Content-Type', 'application/json');
78 $ua->default_header('X-Percona-API-Key', $self->api_key);
79 return $ua;
80}
81
82sub get {
83 my ($self, %args) = @_;
84
85 have_required_args(\%args, qw(
86 link
87 )) or die;
88 my ($link) = $args{link};
89
90 # Get the resources at the link.
91 eval {
92 $self->_request(
93 method => 'GET',
94 link => $link,
95 );
96 };
97 if ( my $e = $EVAL_ERROR ) {
98 if (blessed($e) && $e->isa('Percona::WebAPI::Exception::Request')) {
99 die $e;
100 }
101 else {
102 die "Unknown error: $e";
103 }
104 }
105
106 # The resource should be represented as JSON, decode it.
107 my $resource = eval {
108 JSON::decode_json($self->response->content);
109 };
110 if ( $EVAL_ERROR ) {
111 warn sprintf "Error decoding resource: %s: %s",
112 $self->response->content,
113 $EVAL_ERROR;
114 return;
115 }
116
117 # If the server tells us the resource's type, create a new object
118 # of that type. Else, if there's no type, there's no resource, so
119 # we should have received links. This usually only happens for the
120 # entry link. The returned resource objects ref may be scalar or
121 # an arrayref; the caller should know.
122 my $resource_objects;
123 if ( my $type = $self->response->headers->{'x-percona-resource-type'} ) {
124 eval {
125 $type = "Percona::WebAPI::Resource::$type";
126 if ( ref $resource eq 'ARRAY' ) {
127 PTDEBUG && _d('Got a list of', $type, 'resources');
128 $resource_objects = [];
129 foreach my $attribs ( @$resource ) {
130 my $obj = $type->new(%$attribs);
131 push @$resource_objects, $obj;
132 }
133 }
134 else {
135 PTDEBUG && _d('Got a', $type, 'resource', Dumper($resource));
136 $resource_objects = $type->new(%$resource);
137 }
138 };
139 if ( my $e = $EVAL_ERROR ) {
140 die Percona::WebAPI::Exception::Resource->new(
141 type => $type,
142 link => $link,
143 data => (ref $resource eq 'ARRAY' ? $resource : [ $resource ]),
144 error => $e,
145 );
146 }
147 }
148 elsif ( exists $resource->{links} ) {
149 # Lie to the caller: this isn't an object, but the caller can
150 # treat it like one, e.g. my $links = $api->get(<entry links>);
151 # then access $links->{self}. A Links object couldn't have
152 # dynamic attribs anyway, so no use having a real Links obj.
153 $resource_objects = $resource->{links};
154 }
155 elsif ( exists $resource->{pong} ) {
156 PTDEBUG && _d("Ping pong!");
157 }
158 else {
159 warn "Did not get X-Percona-Resource-Type or links from $link\n";
160 }
161
162 return $resource_objects;
163}
164
165# For a successful POST, the server sets the Location header with
166# the URI of the newly created resource.
167sub post {
168 my $self = shift;
169 $self->_set(
170 @_,
171 method => 'POST',
172 );
173 return $self->response->header('Location');
174}
175
176sub put {
177 my $self = shift;
178 $self->_set(
179 @_,
180 method => 'PUT',
181 );
182 return $self->response->header('Location');
183}
184
185sub delete {
186 my ($self, %args) = @_;
187 have_required_args(\%args, qw(
188 link
189 )) or die;
190 my ($link) = $args{link};
191
192 eval {
193 $self->_request(
194 method => 'DELETE',
195 link => $link,
196 headers => { 'Content-Length' => 0 },
197 );
198 };
199 if ( my $e = $EVAL_ERROR ) {
200 if (blessed($e) && $e->isa('Percona::WebAPI::Exception::Request')) {
201 die $e;
202 }
203 else {
204 die "Unknown error: $e";
205 }
206 }
207
208 return;
209}
210
211# Low-level POST and PUT handler.
212sub _set {
213 my ($self, %args) = @_;
214 have_required_args(\%args, qw(
215 method
216 resources
217 link
218 )) or die;
219 my $method = $args{method};
220 my $res = $args{resources};
221 my $link = $args{link};
222
223 # Optional args
224 my $headers = $args{headers};
225
226 my $content = '';
227 if ( ref($res) eq 'ARRAY' ) {
228 PTDEBUG && _d('List of resources');
229 $content = '[' . join(",\n", map { as_json($_) } @$res) . ']';
230 }
231 elsif ( ref($res) ) {
232 PTDEBUG && _d('Resource object');
233 $content = as_json($res);
234 }
235 elsif ( $res !~ m/\n/ && -f $res ) {
236 PTDEBUG && _d('List of resources in file', $res);
237 $content = '[';
238 my $data = do {
239 local $INPUT_RECORD_SEPARATOR = undef;
240 open my $fh, '<', $res
241 or die "Error opening $res: $OS_ERROR";
242 <$fh>;
243 };
244 $data =~ s/,?\s*$/]/;
245 $content .= $data;
246 }
247 else {
248 PTDEBUG && _d('Resource text');
249 $content = $res;
250 }
251
252 eval {
253 $self->_request(
254 method => $method,
255 link => $link,
256 content => $content,
257 headers => $headers,
258 );
259 };
260 if ( my $e = $EVAL_ERROR ) {
261 if (blessed($e) && $e->isa('Percona::WebAPI::Exception::Request')) {
262 die $e;
263 }
264 else {
265 die "Unknown error: $e";
266 }
267 }
268
269 return;
270}
271
272# Low-level HTTP request handler for all methods. Sets $self->response
273# from the request. Returns nothing on success (HTTP status 2xx-3xx),
274# else throws an Percona::WebAPI::Exception::Request.
275sub _request {
276 my ($self, %args) = @_;
277
278 have_required_args(\%args, qw(
279 method
280 link
281 )) or die;
282 my $method = $args{method};
283 my $link = $args{link};
284
285 # Optional args
286 my $content = $args{content};
287 my $headers = $args{headers};
288
289 my $req = HTTP::Request->new($method => $link);
290 if ( $content ) {
291 $req->content($content);
292 }
293 if ( $headers ) {
294 map { $req->header($_ => $headers->{$_}) } keys %$headers;
295 }
296 PTDEBUG && _d('Request', $method, $link, Dumper($req));
297
298 my $response = $self->ua->request($req);
299 PTDEBUG && _d('Response', Dumper($response));
300
301 $self->response($response);
302
303 if ( !($response->code >= 200 && $response->code < 400) ) {
304 die Percona::WebAPI::Exception::Request->new(
305 method => $method,
306 url => $link,
307 content => $content,
308 status => $response->code,
309 error => "Failed to $method $link",
310 );
311 }
312
313 return;
314}
315
316no Lmo;
3171;
318}
319# ###########################################################################
320# End Percona::WebAPI::Client package
321# ###########################################################################
3220
=== removed directory 'lib/Percona/WebAPI/Exception'
=== removed file 'lib/Percona/WebAPI/Exception/Request.pm'
--- lib/Percona/WebAPI/Exception/Request.pm 2012-12-26 20:00:46 +0000
+++ lib/Percona/WebAPI/Exception/Request.pm 1970-01-01 00:00:00 +0000
@@ -1,69 +0,0 @@
1# This program is copyright 2012-2013 Percona Inc.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::WebAPI::Exception::Request package
19# ###########################################################################
20{
21package Percona::WebAPI::Exception::Request;
22
23use Lmo;
24use overload '""' => \&as_string;
25
26has 'method' => (
27 is => 'ro',
28 isa => 'Str',
29 required => 1,
30);
31
32has 'url' => (
33 is => 'ro',
34 isa => 'Str',
35 required => 1,
36);
37
38has 'content' => (
39 is => 'ro',
40 isa => 'Maybe[Str]',
41 required => 0,
42);
43
44has 'status' => (
45 is => 'ro',
46 isa => 'Int',
47 required => 1,
48);
49
50has 'error' => (
51 is => 'ro',
52 isa => 'Str',
53 required => 1,
54);
55
56sub as_string {
57 my $self = shift;
58 chomp(my $error = $self->error);
59 $error =~ s/\n/ /g;
60 return sprintf "%s\nRequest: %s %s %s\nStatus: %d\n",
61 $error, $self->method, $self->url, $self->content || '', $self->status;
62}
63
64no Lmo;
651;
66}
67# ###########################################################################
68# End Percona::WebAPI::Exception::Request package
69# ###########################################################################
700
=== removed file 'lib/Percona/WebAPI/Exception/Resource.pm'
--- lib/Percona/WebAPI/Exception/Resource.pm 2013-03-01 16:47:49 +0000
+++ lib/Percona/WebAPI/Exception/Resource.pm 1970-01-01 00:00:00 +0000
@@ -1,66 +0,0 @@
1# This program is copyright 2012-2013 Percona Inc.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::WebAPI::Exception::Resource package
19# ###########################################################################
20{
21package Percona::WebAPI::Exception::Resource;
22
23use Lmo;
24use overload '""' => \&as_string;
25use Data::Dumper;
26
27has 'type' => (
28 is => 'ro',
29 isa => 'Str',
30 required => 1,
31);
32
33has 'link' => (
34 is => 'ro',
35 isa => 'Str',
36 required => 1,
37);
38
39has 'data' => (
40 is => 'ro',
41 isa => 'ArrayRef',
42 required => 1,
43);
44
45has 'error' => (
46 is => 'ro',
47 isa => 'Str',
48 required => 1,
49);
50
51sub as_string {
52 my $self = shift;
53 chomp(my $error = $self->error);
54 local $Data::Dumper::Indent = 1;
55 local $Data::Dumper::Sortkeys = 1;
56 local $Data::Dumper::Quotekeys = 0;
57 return sprintf "Invalid %s resource from %s:\n\n%s\nError: %s\n\n",
58 $self->type, $self->link, Dumper($self->data), $error;
59}
60
61no Lmo;
621;
63}
64# ###########################################################################
65# End Percona::WebAPI::Exception::Resource package
66# ###########################################################################
670
=== removed file 'lib/Percona/WebAPI/Representation.pm'
--- lib/Percona/WebAPI/Representation.pm 2013-06-15 19:31:38 +0000
+++ lib/Percona/WebAPI/Representation.pm 1970-01-01 00:00:00 +0000
@@ -1,86 +0,0 @@
1# This program is copyright 2012-2013 Percona Inc.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::WebAPI::Representation package
19# ###########################################################################
20{
21package Percona::WebAPI::Representation;
22
23eval {
24 require JSON;
25};
26
27require Exporter;
28our @ISA = qw(Exporter);
29our @EXPORT_OK = qw(
30 as_hashref
31 as_json
32 as_config
33);
34
35sub as_hashref {
36 my ($resource, %args) = @_;
37
38 # Copy the object into a new hashref.
39 my $as_hashref = { %$resource };
40
41 # Delete the links because they're just for client-side use
42 # and the caller should be sending this object, not getting it.
43 # But sometimes for testing we want to keep the links.
44 if ( !defined $args{with_links} || !$args{with_links} ) {
45 delete $as_hashref->{links};
46 }
47
48 return $as_hashref;
49}
50
51sub as_json {
52 my ($resource, %args) = @_;
53
54 my $json = $args{json} || JSON->new;
55 $json->allow_blessed([]);
56 $json->convert_blessed([]);
57
58 my $text = $json->encode(
59 ref $resource eq 'ARRAY' ? $resource : as_hashref($resource, %args)
60 );
61 if ( $args{json} && $text ) { # for testing
62 chomp($text);
63 $text .= "\n";
64 }
65 return $text;
66}
67
68sub as_config {
69 my $resource = shift;
70 if ( !$resource->isa('Percona::WebAPI::Resource::Config') ) {
71 die "Only Config resources can be represented as config.\n";
72 }
73 my $as_hashref = as_hashref($resource);
74 my $options = $as_hashref->{options};
75 my $config = join("\n",
76 map { defined $options->{$_} ? "$_=$options->{$_}" : "$_" }
77 sort keys %$options
78 ) . "\n";
79 return $config;
80}
81
821;
83}
84# ###########################################################################
85# End Percona::WebAPI::Representation package
86# ###########################################################################
870
=== removed directory 'lib/Percona/WebAPI/Resource'
=== removed file 'lib/Percona/WebAPI/Resource/Agent.pm'
--- lib/Percona/WebAPI/Resource/Agent.pm 2013-06-15 02:59:14 +0000
+++ lib/Percona/WebAPI/Resource/Agent.pm 1970-01-01 00:00:00 +0000
@@ -1,77 +0,0 @@
1# This program is copyright 2012-2013 Percona Inc.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::WebAPI::Resource::Agent package
19# ###########################################################################
20{
21package Percona::WebAPI::Resource::Agent;
22
23use Lmo;
24
25has 'uuid' => (
26 is => 'ro',
27 isa => 'Str',
28 required => 0,
29);
30
31has 'username' => (
32 is => 'rw',
33 isa => 'Str',
34 required => 0,
35 default => sub { return $ENV{USER} || $ENV{LOGNAME} },
36);
37
38has 'hostname' => (
39 is => 'rw',
40 isa => 'Str',
41 required => 0,
42 default => sub {
43 chomp(my $hostname = `hostname`);
44 return $hostname;
45 },
46);
47
48has 'alias' => (
49 is => 'rw',
50 isa => 'Str',
51 required => 0,
52);
53
54has 'versions' => (
55 is => 'rw',
56 isa => 'Maybe[HashRef]',
57 required => 0,
58);
59
60has 'links' => (
61 is => 'rw',
62 isa => 'Maybe[HashRef]',
63 required => 0,
64 default => sub { return {} },
65);
66
67sub name {
68 my ($self) = @_;
69 return $self->alias || $self->hostname || $self->uuid || 'Unknown';
70}
71
72no Lmo;
731;
74}
75# ###########################################################################
76# End Percona::WebAPI::Resource::Agent package
77# ###########################################################################
780
=== removed file 'lib/Percona/WebAPI/Resource/Config.pm'
--- lib/Percona/WebAPI/Resource/Config.pm 2013-03-01 16:47:49 +0000
+++ lib/Percona/WebAPI/Resource/Config.pm 1970-01-01 00:00:00 +0000
@@ -1,55 +0,0 @@
1# This program is copyright 2012-2013 Percona Inc.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::WebAPI::Resource::Config package
19# ###########################################################################
20{
21package Percona::WebAPI::Resource::Config;
22
23use Lmo;
24
25has 'ts' => (
26 is => 'ro',
27 isa => 'Int',
28 required => 1,
29);
30
31has 'name' => (
32 is => 'ro',
33 isa => 'Str',
34 required => 1,
35);
36
37has 'options' => (
38 is => 'ro',
39 isa => 'HashRef',
40 required => 1,
41);
42
43has 'links' => (
44 is => 'rw',
45 isa => 'Maybe[HashRef]',
46 required => 0,
47 default => sub { return {} },
48);
49
50no Lmo;
511;
52}
53# ###########################################################################
54# End Percona::WebAPI::Resource::Config package
55# ###########################################################################
560
=== removed file 'lib/Percona/WebAPI/Resource/LogEntry.pm'
--- lib/Percona/WebAPI/Resource/LogEntry.pm 2013-06-10 00:15:16 +0000
+++ lib/Percona/WebAPI/Resource/LogEntry.pm 1970-01-01 00:00:00 +0000
@@ -1,66 +0,0 @@
1# This program is copyright 2013 Percona Inc.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::WebAPI::Resource::LogEntry package
19# ###########################################################################
20{
21package Percona::WebAPI::Resource::LogEntry;
22
23use Lmo;
24
25has 'pid' => (
26 is => 'ro',
27 isa => 'Int',
28 required => 1,
29);
30
31has 'service' => (
32 is => 'ro',
33 isa => 'Str',
34 required => 0,
35);
36
37has 'data_ts' => (
38 is => 'ro',
39 isa => 'Int',
40 required => 0,
41);
42
43has 'entry_ts' => (
44 is => 'ro',
45 isa => 'Str',
46 required => 1,
47);
48
49has 'log_level' => (
50 is => 'ro',
51 isa => 'Int',
52 required => 1,
53);
54
55has 'message' => (
56 is => 'ro',
57 isa => 'Str',
58 required => 1,
59);
60
61no Lmo;
621;
63}
64# ###########################################################################
65# End Percona::WebAPI::Resource::LogEntry package
66# ###########################################################################
670
=== removed file 'lib/Percona/WebAPI/Resource/Service.pm'
--- lib/Percona/WebAPI/Resource/Service.pm 2013-04-19 20:49:01 +0000
+++ lib/Percona/WebAPI/Resource/Service.pm 1970-01-01 00:00:00 +0000
@@ -1,94 +0,0 @@
1# This program is copyright 2012-2013 Percona Inc.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::WebAPI::Resource::Service package
19# ###########################################################################
20{
21package Percona::WebAPI::Resource::Service;
22
23use Lmo;
24
25has 'ts' => (
26 is => 'ro',
27 isa => 'Int',
28 required => 1,
29);
30
31has 'name' => (
32 is => 'ro',
33 isa => 'Str',
34 required => 1,
35);
36
37has 'tasks' => (
38 is => 'ro',
39 isa => 'ArrayRef[Percona::WebAPI::Resource::Task]',
40 required => 1,
41);
42
43has 'run_schedule' => (
44 is => 'ro',
45 isa => 'Str',
46 required => 0,
47);
48
49has 'spool_schedule' => (
50 is => 'ro',
51 isa => 'Str',
52 required => 0,
53);
54
55has 'meta' => (
56 is => 'ro',
57 isa => 'Bool',
58 required => 0,
59 default => sub { return 0 },
60);
61
62has 'run_once' => (
63 is => 'ro',
64 isa => 'Bool',
65 required => 0,
66 default => sub { return 0 },
67);
68
69has 'links' => (
70 is => 'rw',
71 isa => 'Maybe[HashRef]',
72 required => 0,
73 default => sub { return {} },
74);
75
76sub BUILDARGS {
77 my ($class, %args) = @_;
78 if ( ref $args{tasks} eq 'ARRAY' ) {
79 my @tasks;
80 foreach my $run_hashref ( @{$args{tasks}} ) {
81 my $task = Percona::WebAPI::Resource::Task->new(%$run_hashref);
82 push @tasks, $task;
83 }
84 $args{tasks} = \@tasks;
85 }
86 return $class->SUPER::BUILDARGS(%args);
87}
88
89no Lmo;
901;
91}
92# ###########################################################################
93# End Percona::WebAPI::Resource::Service package
94# ###########################################################################
950
=== removed file 'lib/Percona/WebAPI/Resource/Task.pm'
--- lib/Percona/WebAPI/Resource/Task.pm 2013-04-19 20:49:01 +0000
+++ lib/Percona/WebAPI/Resource/Task.pm 1970-01-01 00:00:00 +0000
@@ -1,62 +0,0 @@
1# This program is copyright 2012-2013 Percona Inc.
2# Feedback and improvements are welcome.
3#
4# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
5# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
6# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
7#
8# This program is free software; you can redistribute it and/or modify it under
9# the terms of the GNU General Public License as published by the Free Software
10# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
11# systems, you can issue `man perlgpl' or `man perlartistic' to read these
12# licenses.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16# Place, Suite 330, Boston, MA 02111-1307 USA.
17# ###########################################################################
18# Percona::WebAPI::Resource::Task package
19# ###########################################################################
20{
21package Percona::WebAPI::Resource::Task;
22
23use Lmo;
24
25has 'name' => (
26 is => 'ro',
27 isa => 'Str',
28 required => 1,
29);
30
31has 'number' => (
32 is => 'ro',
33 isa => 'Int',
34 required => 1,
35);
36
37has 'program' => (
38 is => 'ro',
39 isa => 'Maybe[Str]',
40 required => 0,
41);
42
43has 'query' => (
44 is => 'ro',
45 isa => 'Maybe[Str]',
46 required => 0,
47);
48
49has 'output' => (
50 is => 'ro',
51 isa => 'Maybe[Str]',
52 required => 0,
53);
54
55sub TO_JSON { return { %{ shift() } }; }
56
57no Lmo;
581;
59}
60# ###########################################################################
61# End Percona::WebAPI::Resource::Task package
62# ###########################################################################
630
=== removed directory 't/lib/Percona/WebAPI'
=== removed file 't/lib/Percona/WebAPI/Client.t'
--- t/lib/Percona/WebAPI/Client.t 2013-08-08 19:34:29 +0000
+++ t/lib/Percona/WebAPI/Client.t 1970-01-01 00:00:00 +0000
@@ -1,236 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13use JSON;
14use File::Temp qw(tempdir);
15
16use Percona::Test;
17use Percona::Test::Mock::UserAgent;
18use Percona::WebAPI::Client;
19use Percona::WebAPI::Resource::Agent;
20use Percona::WebAPI::Resource::Config;
21use Percona::WebAPI::Resource::Service;
22use Percona::WebAPI::Resource::Task;
23
24Percona::Toolkit->import(qw(Dumper have_required_args));
25Percona::WebAPI::Representation->import(qw(as_json as_hashref));
26
27# #############################################################################
28# Create a client with a mock user-agent.
29# #############################################################################
30
31my $json = JSON->new;
32$json->allow_blessed([]);
33$json->convert_blessed([]);
34
35my $ua = Percona::Test::Mock::UserAgent->new(
36 encode => sub { my $c = shift; return $json->encode($c || {}) },
37);
38
39my $client = eval {
40 Percona::WebAPI::Client->new(
41 api_key => '123',
42 ua => $ua,
43 );
44};
45
46is(
47 $EVAL_ERROR,
48 '',
49 'Create client'
50) or die;
51
52# #############################################################################
53# First thing a client should do is get the entry links.
54# #############################################################################
55
56my $return_links = { # what the server returns
57 agents => '/agents',
58};
59
60$ua->{responses}->{get} = [
61 {
62 content => {
63 links => $return_links,
64 }
65 },
66];
67
68my $links = $client->get(link => $client->entry_link);
69
70is_deeply(
71 $links,
72 $return_links,
73 "Get entry links"
74) or diag(Dumper($links));
75
76is_deeply(
77 $ua->{requests},
78 [
79 'GET https://cloud-api.percona.com',
80 ],
81 "1 request, 1 GET"
82) or diag(Dumper($ua->{requests}));
83
84
85# #############################################################################
86# Second, a new client will POST an Agent for itself. The entry links
87# should have an "agents" link. The server response is empty but the
88# URI for the new Agent resource is given by the Location header.
89# #############################################################################
90
91my $agent = Percona::WebAPI::Resource::Agent->new(
92 id => '123',
93 hostname => 'host',
94);
95
96$ua->{responses}->{post} = [
97 {
98 headers => { 'Location' => 'agents/5' },
99 content => '',
100 },
101];
102
103my $uri = $client->post(resources => $agent, link => $links->{agents});
104
105is(
106 $uri,
107 "agents/5",
108 "POST Agent, got Location URI"
109);
110
111# #############################################################################
112# After successfully creating the new Agent, the client should fetch
113# the new Agent resoruce which will have links to the next step: the
114# agent's config.
115# #############################################################################
116
117$return_links = {
118 self => 'agents/5',
119 config => 'agents/5/config',
120};
121
122my $content = {
123 %{ as_hashref($agent) },
124 links => $return_links,
125};
126
127$ua->{responses}->{get} = [
128 {
129 headers => { 'X-Percona-Resource-Type' => 'Agent' },
130 content => $content,
131 },
132];
133
134# Re-using $agent, i.e. updating it with the actual, newly created
135# Agent resource as returned by the server with links.
136$agent = $client->get(link => $uri);
137
138# Need to use with_links=>1 here because by as_hashref() removes
139# links by default because it's usually used to encode and send
140# resources, and clients never send links; but here we're using
141# it for testing.
142is_deeply(
143 as_hashref($agent, with_links => 1),
144 $content,
145 "GET Agent with links"
146) or diag(Dumper(as_hashref($agent, with_links => 1)));
147
148# #############################################################################
149# Now the agent can get its Config.
150# #############################################################################
151
152$return_links = {
153 self => 'agents/5/config',
154 services => 'agents/5/services',
155};
156
157my $return_config = Percona::WebAPI::Resource::Config->new(
158 ts => '100',
159 name => 'Default',
160 options => {},
161 links => $return_links,
162);
163
164$ua->{responses}->{get} = [
165 {
166 headers => { 'X-Percona-Resource-Type' => 'Config' },
167 content => as_hashref($return_config, with_links => 1),
168 },
169];
170
171my $config = $client->get(link => $agent->links->{config});
172
173is_deeply(
174 as_hashref($config, with_links => 1),
175 as_hashref($return_config, with_links => 1),
176 "GET Config"
177) or diag(Dumper(as_hashref($config, with_links => 1)));
178
179# #############################################################################
180# Once an agent is configured, i.e. successfully gets a Config resource,
181# its Config should have a services link which returns a list of Service
182# resources, each with their own links.
183# #############################################################################
184
185$return_links = {
186 'send_data' => '/query-monitor',
187};
188
189my $run0 = Percona::WebAPI::Resource::Task->new(
190 name => 'run-pqd',
191 number => '0',
192 program => 'pt-query-digest',
193 options => '--output json',
194 output => 'spool',
195);
196
197my $svc0 = Percona::WebAPI::Resource::Service->new(
198 ts => '123',
199 name => 'query-monitor',
200 run_schedule => '1 * * * *',
201 spool_schedule => '2 * * * *',
202 tasks => [ $run0 ],
203 links => $return_links,
204);
205
206$ua->{responses}->{get} = [
207 {
208 headers => { 'X-Percona-Resource-Type' => 'Service' },
209 content => [ as_hashref($svc0, with_links => 1) ],
210 },
211];
212
213my $services = $client->get(link => $config->links->{services});
214
215is(
216 scalar @$services,
217 1,
218 "Got 1 service"
219);
220
221is_deeply(
222 as_hashref($services->[0], with_links => 1),
223 as_hashref($svc0, with_links => 1),
224 "GET Services"
225) or diag(Dumper(as_hashref($services, with_links => 1)));
226
227is(
228 $services->[0]->links->{send_data},
229 "/query-monitor",
230 "send_data link for Service"
231);
232
233# #############################################################################
234# Done.
235# #############################################################################
236done_testing;
2370
=== removed file 't/lib/Percona/WebAPI/Representation.t'
--- t/lib/Percona/WebAPI/Representation.t 2013-03-01 16:47:49 +0000
+++ t/lib/Percona/WebAPI/Representation.t 1970-01-01 00:00:00 +0000
@@ -1,51 +0,0 @@
1#!/usr/bin/perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13
14use PerconaTest;
15use Percona::Toolkit;
16use Percona::WebAPI::Resource::Agent;
17use Percona::WebAPI::Resource::Config;
18use Percona::WebAPI::Representation;
19
20my $agent = Percona::WebAPI::Resource::Agent->new(
21 id => '123',
22 hostname => 'pt',
23 versions => {
24 Perl => '5.10.1',
25 },
26);
27
28is(
29 Percona::WebAPI::Representation::as_json($agent),
30 q/{"versions":{"Perl":"5.10.1"},"id":"123","hostname":"pt"}/,
31 "as_json"
32);
33
34my $config = Percona::WebAPI::Resource::Config->new(
35 ts => '100',
36 name => 'Default',
37 options => {
38 'check-interval' => 60,
39 },
40);
41
42is(
43 Percona::WebAPI::Representation::as_config($config),
44 "check-interval=60\n",
45 "as_config"
46);
47
48# #############################################################################
49# Done.
50# #############################################################################
51done_testing;
520
=== removed directory 't/pt-agent'
=== removed file 't/pt-agent/basics.t'
--- t/pt-agent/basics.t 2013-04-13 17:27:45 +0000
+++ t/pt-agent/basics.t 1970-01-01 00:00:00 +0000
@@ -1,101 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13
14use File::Temp qw(tempdir);
15
16use Percona::Test;
17use Sandbox;
18use Percona::Test::Mock::UserAgent;
19require "$trunk/bin/pt-agent";
20
21my $dp = new DSNParser(opts=>$dsn_opts);
22my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
23my $dbh = $sb->get_dbh_for('master');
24my $dsn = $sb->dsn_for('master');
25my $o = new OptionParser();
26$o->get_specs("$trunk/bin/pt-agent");
27$o->get_opts();
28my $cxn = Cxn->new(
29 dsn_string => $dsn,
30 OptionParser => $o,
31 DSNParser => $dp,
32);
33
34Percona::Toolkit->import(qw(Dumper));
35Percona::WebAPI::Representation->import(qw(as_hashref));
36
37# Running the agent is going to cause it to schedule the services,
38# i.e. write a real crontab. The test box/user shouldn't have a
39# crontab, so we'll warn and clobber it if there is one.
40my $crontab = `crontab -l 2>/dev/null`;
41if ( $crontab ) {
42 warn "Removing crontab: $crontab\n";
43 `crontab -r`;
44}
45
46my $tmp_lib = "/tmp/pt-agent";
47my $tmp_log = "/tmp/pt-agent.log";
48my $tmp_pid = "/tmp/pt-agent.pid";
49
50diag(`rm -rf $tmp_lib`) if -d $tmp_lib;
51unlink $tmp_log if -f $tmp_log;
52unlink $tmp_pid if -f $tmp_pid;
53
54my $config_file = pt_agent::get_config_file();
55unlink $config_file if -f $config_file;
56
57my $output;
58
59{
60 no strict;
61 no warnings;
62 local *pt_agent::start_agent = sub {
63 print "start_agent\n";
64 return {
65 agent => 0,
66 client => 0,
67 daemon => 0,
68 };
69 };
70 local *pt_agent::run_agent = sub {
71 print "run_agent\n";
72 };
73
74 $output = output(
75 sub {
76 pt_agent::main(
77 qw(--api-key 123)
78 );
79 },
80 stderr => 1,
81 );
82}
83
84like(
85 $output,
86 qr/start_agent\nrun_agent\n/,
87 "Starts and runs without a config file"
88);
89
90# #############################################################################
91# Done.
92# #############################################################################
93
94`crontab -r 2>/dev/null`;
95
96if ( -f $config_file ) {
97 unlink $config_file
98 or warn "Error removing $config_file: $OS_ERROR";
99}
100
101done_testing;
1020
=== removed file 't/pt-agent/get_services.t'
--- t/pt-agent/get_services.t 2013-06-17 04:01:30 +0000
+++ t/pt-agent/get_services.t 1970-01-01 00:00:00 +0000
@@ -1,423 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13
14use JSON;
15use File::Temp qw(tempdir);
16
17use Percona::Test;
18use Percona::Test::Mock::UserAgent;
19use Percona::Test::Mock::AgentLogger;
20require "$trunk/bin/pt-agent";
21
22Percona::Toolkit->import(qw(Dumper));
23Percona::WebAPI::Representation->import(qw(as_hashref));
24
25my @log;
26my $logger = Percona::Test::Mock::AgentLogger->new(log => \@log);
27pt_agent::_logger($logger);
28
29# Fake --lib and --spool dirs.
30my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1);
31output( sub {
32 pt_agent::init_lib_dir(lib_dir => $tmpdir);
33});
34
35# #############################################################################
36# Create mock client and Agent
37# #############################################################################
38
39# These aren't the real tests yet: to run_agent, first we need
40# a client and Agent, so create mock ones.
41
42my $output;
43my $json = JSON->new->canonical([1])->pretty;
44$json->allow_blessed([]);
45$json->convert_blessed([]);
46
47my $ua = Percona::Test::Mock::UserAgent->new(
48 encode => sub { my $c = shift; return $json->encode($c || {}) },
49);
50
51my $client = eval {
52 Percona::WebAPI::Client->new(
53 api_key => '123',
54 ua => $ua,
55 );
56};
57
58is(
59 $EVAL_ERROR,
60 '',
61 'Create mock client'
62) or die;
63
64my $agent = Percona::WebAPI::Resource::Agent->new(
65 uuid => '123',
66 hostname => 'host',
67 username => 'user',
68 links => {
69 self => '/agents/123',
70 config => '/agents/123/config',
71 },
72);
73
74my @cmds;
75my $exec_cmd = sub {
76 my $cmd = shift;
77 push @cmds, $cmd;
78 return 0;
79};
80
81# #############################################################################
82# Test get_services()
83# #############################################################################
84
85# query-history
86
87my $run0 = Percona::WebAPI::Resource::Task->new(
88 name => 'query-history',
89 number => '0',
90 program => 'pt-query-digest --output json',
91 output => 'spool',
92);
93
94my $qh = Percona::WebAPI::Resource::Service->new(
95 ts => '100',
96 name => 'query-history',
97 run_schedule => '1 * * * *',
98 spool_schedule => '2 * * * *',
99 tasks => [ $run0 ],
100 links => {
101 self => '/query-history',
102 data => '/query-history/data',
103 },
104);
105
106my $run1 = Percona::WebAPI::Resource::Task->new(
107 name => 'start-query-history',
108 number => '0',
109 program => 'echo "start-qh"',
110 output => 'spool',
111);
112
113my $start_qh = Percona::WebAPI::Resource::Service->new(
114 ts => '100',
115 name => 'start-query-history',
116 meta => 1,
117 tasks => [ $run1 ],
118 links => {
119 self => '/query-history',
120 data => '/query-history/data',
121 },
122);
123
124$ua->{responses}->{get} = [
125 {
126 headers => { 'X-Percona-Resource-Type' => 'Service' },
127 content => [
128 as_hashref($qh, with_links => 1),
129 as_hashref($start_qh, with_links => 1),
130 ],
131 },
132];
133
134my $services = {};
135my $success = 0;
136
137$output = output(
138 sub {
139 ($services, $success) = pt_agent::get_services(
140 # Required args
141 link => '/agents/123/services',
142 agent => $agent,
143 client => $client,
144 lib_dir => $tmpdir,
145 services => $services,
146 # Optional args, for testing
147 json => $json,
148 bin_dir => "$trunk/bin/",
149 exec_cmd => $exec_cmd,
150 );
151 },
152 stderr => 1,
153);
154
155is(
156 $success,
157 1,
158 "Success"
159);
160
161is(
162 ref $services,
163 'HASH',
164 "Return services as hashref"
165) or diag(Dumper($services));
166
167is(
168 scalar keys %$services,
169 2,
170 'Only 2 services'
171) or diag(Dumper($services));
172
173ok(
174 exists $services->{'query-history'},
175 "services hashref keyed on service name"
176) or diag(Dumper($services));
177
178isa_ok(
179 ref $services->{'query-history'},
180 'Percona::WebAPI::Resource::Service',
181 'services->{query-history}'
182);
183
184my $crontab = -f "$tmpdir/crontab" ? slurp_file("$tmpdir/crontab") : '';
185is(
186 $crontab,
187 "1 * * * * $trunk/bin/pt-agent --run-service query-history
1882 * * * * $trunk/bin/pt-agent --send-data query-history
189",
190 "crontab file"
191) or diag($output, `ls -l $tmpdir/*`, Dumper(\@log));
192
193is_deeply(
194 \@cmds,
195 [
196 "$trunk/bin/pt-agent --run-service start-query-history >> $tmpdir/logs/start-stop.log 2>&1",
197 "crontab $tmpdir/crontab > $tmpdir/crontab.err 2>&1",
198 ],
199 "Ran start-service and crontab"
200) or diag(Dumper(\@cmds), Dumper(\@log));
201
202ok(
203 -f "$tmpdir/services/query-history",
204 "Wrote --lib/services/query-history"
205);
206
207# #############################################################################
208# A more realistic transaction
209# #############################################################################
210
211# services/query-history should exist from the previous tests. For these
212# tests, get_services() should update the file, so we empty it and check
213# that it's re-created, i.e. updated.
214diag(`echo -n > $tmpdir/services/query-history`);
215is(
216 -s "$tmpdir/services/query-history",
217 0,
218 "Start: empty --lib/services/query-history"
219);
220
221# start-query-history
222
223my $task1 = Percona::WebAPI::Resource::Task->new(
224 name => 'disable-slow-query-log',
225 number => '0',
226 query => "SET GLOBAL slow_query_log=0",
227);
228
229my $task2 = Percona::WebAPI::Resource::Task->new(
230 name => 'set-slow-query-log-file',
231 number => '1',
232 query => "SET GLOBAL slow_query_log_file='/tmp/slow.log'",
233);
234
235my $task3 = Percona::WebAPI::Resource::Task->new(
236 name => 'set-long-query-time',
237 number => '2',
238 query => "SET GLOBAL long_query_time=0.01",
239);
240
241my $task4 = Percona::WebAPI::Resource::Task->new(
242 name => 'enable-slow-query-log',
243 number => '3',
244 query => "SET GLOBAL slow_query_log=1",
245);
246
247$start_qh = Percona::WebAPI::Resource::Service->new(
248 ts => '100',
249 name => 'start-query-history',
250 tasks => [ $task1, $task2, $task3, $task4 ],
251 meta => 1,
252 links => {
253 self => '/query-history',
254 data => '/query-history/data',
255 },
256);
257
258# stop-query-history
259
260my $task5 = Percona::WebAPI::Resource::Task->new(
261 name => 'disable-slow-query-log',
262 number => '0',
263 query => "SET GLOBAL slow_query_log=0",
264);
265
266my $stop_qh = Percona::WebAPI::Resource::Service->new(
267 ts => '100',
268 name => 'stop-query-history',
269 tasks => [ $task5 ],
270 meta => 1,
271 links => {
272 self => '/query-history',
273 data => '/query-history/data',
274 },
275);
276
277# We'll use query-history from the previous tests.
278
279$ua->{responses}->{get} = [
280 {
281 headers => { 'X-Percona-Resource-Type' => 'Service' },
282 content => [
283 as_hashref($start_qh, with_links => 1),
284 as_hashref($stop_qh, with_links => 1),
285 as_hashref($qh, with_links => 1), # from previous tests
286 ],
287 },
288];
289
290@log = ();
291@cmds = ();
292$services = {};
293$success = 0;
294
295$output = output(
296 sub {
297 ($services, $success) = pt_agent::get_services(
298 # Required args
299 link => '/agents/123/services',
300 agent => $agent,
301 client => $client,
302 lib_dir => $tmpdir,
303 services => $services,
304 # Optional args, for testing
305 json => $json,
306 bin_dir => "$trunk/bin/",
307 exec_cmd => $exec_cmd,
308 );
309 },
310 stderr => 1,
311);
312
313is_deeply(
314 \@cmds,
315 [
316 "$trunk/bin/pt-agent --run-service start-query-history >> $tmpdir/logs/start-stop.log 2>&1",
317 "crontab $tmpdir/crontab > $tmpdir/crontab.err 2>&1",
318 ],
319 "Start: ran start-query-history"
320) or diag(Dumper(\@cmds), $output);
321
322ok(
323 -f "$tmpdir/services/start-query-history",
324 "Start: added --lib/services/start-query-history"
325) or diag($output);
326
327ok(
328 -f "$tmpdir/services/stop-query-history",
329 "Start: added --lib/services/stop-query-history"
330) or diag($output);
331
332my $contents = slurp_file("$tmpdir/services/query-history");
333like(
334 $contents,
335 qr/query-history/,
336 "Start: updated --lib/services/query-history"
337) or diag($output);
338
339$crontab = slurp_file("$tmpdir/crontab");
340is(
341 $crontab,
342 "1 * * * * $trunk/bin/pt-agent --run-service query-history
3432 * * * * $trunk/bin/pt-agent --send-data query-history
344",
345 "Start: only scheduled query-history"
346) or diag($output);
347
348# #############################################################################
349# Update and restart a service
350# #############################################################################
351
352# pt-agent should remove a service's --lib/meta/ files when restarting,
353# so create one and check that it's removed.
354diag(`touch $tmpdir/meta/query-history.foo`);
355ok(
356 -f "$tmpdir/meta/query-history.foo",
357 "Restart: meta file exists"
358);
359
360$qh = Percona::WebAPI::Resource::Service->new(
361 ts => '200', # was 100
362 name => 'query-history',
363 run_schedule => '1 * * * *',
364 spool_schedule => '2 * * * *',
365 tasks => [ $run0 ],
366 links => {
367 self => '/query-history',
368 data => '/query-history/data',
369 },
370);
371
372$ua->{responses}->{get} = [
373 {
374 headers => { 'X-Percona-Resource-Type' => 'Service' },
375 content => [
376 as_hashref($start_qh, with_links => 1), # has not changed
377 as_hashref($stop_qh, with_links => 1), # has not changed
378 as_hashref($qh, with_links => 1),
379 ],
380 },
381];
382
383@log = ();
384@cmds = ();
385$success = 0;
386
387$output = output(
388 sub {
389 ($services, $success) = pt_agent::get_services(
390 # Required args
391 link => '/agents/123/services',
392 agent => $agent,
393 client => $client,
394 lib_dir => $tmpdir,
395 services => $services, # retval from previous call
396 # Optional args, for testing
397 json => $json,
398 bin_dir => "$trunk/bin/",
399 exec_cmd => $exec_cmd,
400 );
401 },
402 stderr => 1,
403);
404
405is_deeply(
406 \@cmds,
407 [
408 "$trunk/bin/pt-agent --run-service stop-query-history >> $tmpdir/logs/start-stop.log 2>&1",
409 "$trunk/bin/pt-agent --run-service start-query-history >> $tmpdir/logs/start-stop.log 2>&1",
410 "crontab $tmpdir/crontab > $tmpdir/crontab.err 2>&1",
411 ],
412 "Restart: ran stop-query-history then start-query-history"
413) or diag(Dumper(\@cmds), $output);
414
415ok(
416 !-f "$tmpdir/meta/query-history.foo",
417 "Restart: meta file removed"
418) or diag($output);
419
420# #############################################################################
421# Done.
422# #############################################################################
423done_testing;
4240
=== removed file 't/pt-agent/init_agent.t'
--- t/pt-agent/init_agent.t 2013-09-23 19:07:34 +0000
+++ t/pt-agent/init_agent.t 1970-01-01 00:00:00 +0000
@@ -1,333 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13use JSON;
14use File::Temp qw(tempdir);
15
16use Percona::Test;
17use Percona::Test::Mock::UserAgent;
18use Percona::Test::Mock::AgentLogger;
19require "$trunk/bin/pt-agent";
20
21Percona::Toolkit->import(qw(Dumper));
22Percona::WebAPI::Representation->import(qw(as_hashref));
23
24my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1);
25
26my $json = JSON->new->canonical([1])->pretty;
27$json->allow_blessed([]);
28$json->convert_blessed([]);
29
30my @log;
31my $logger = Percona::Test::Mock::AgentLogger->new(log => \@log);
32pt_agent::_logger($logger);
33
34my $ua = Percona::Test::Mock::UserAgent->new(
35 encode => sub { my $c = shift; return $json->encode($c || {}) },
36);
37
38my $client = eval {
39 Percona::WebAPI::Client->new(
40 api_key => '123',
41 ua => $ua,
42 );
43};
44
45is(
46 $EVAL_ERROR,
47 '',
48 'Create Client with mock user agent'
49) or die;
50
51my @ok;
52my $oktorun = sub {
53 return shift @ok;
54};
55
56my @wait;
57my $interval = sub {
58 my $t = shift;
59 push @wait, $t;
60};
61
62# #############################################################################
63# Init a new agent, i.e. create it.
64# #############################################################################
65
66my $post_agent = Percona::WebAPI::Resource::Agent->new(
67 uuid => '123',
68 hostname => 'host1',
69 username => 'name1',
70 versions => {
71 },
72 links => {
73 self => '/agents/123',
74 config => '/agents/123/config',
75 },
76);
77
78my $return_agent = Percona::WebAPI::Resource::Agent->new(
79 uuid => '123',
80 hostname => 'host2',
81 username => 'name2',
82 versions => {
83 },
84 links => {
85 self => '/agents/123',
86 config => '/agents/123/config',
87 },
88);
89
90$ua->{responses}->{post} = [
91 {
92 headers => { 'Location' => '/agents/123' },
93 },
94];
95
96$ua->{responses}->{get} = [
97 {
98 headers => { 'X-Percona-Resource-Type' => 'Agent' },
99 content => as_hashref($return_agent, with_links =>1 ),
100 },
101];
102
103my $got_agent;
104my $output = output(
105 sub {
106 ($got_agent) = pt_agent::init_agent(
107 agent => $post_agent,
108 action => 'post',
109 link => "/agents",
110 client => $client,
111 interval => $interval,
112 tries => 4,
113 );
114 },
115 stderr => 1,
116);
117
118is(
119 $got_agent->hostname,
120 'host2',
121 'Got and returned Agent'
122) or diag($output, Dumper(as_hashref($got_agent, with_links => 1)));
123
124is(
125 scalar @wait,
126 0,
127 "Client did not wait (new Agent)"
128) or diag($output);
129
130# #############################################################################
131# Repeat this test but this time fake an error, so the tool isn't able
132# to create the Agent first time, so it should wait (call interval),
133# and try again.
134# #############################################################################
135
136$return_agent->{id} = '456';
137$return_agent->{links} = {
138 self => '/agents/456',
139 config => '/agents/456/config',
140};
141
142$ua->{responses}->{post} = [
143 { # 1, the fake error
144 code => 500,
145 },
146 # 2, code should call interval
147 { # 3, code should try again, then receive this
148 code => 200,
149 headers => { 'Location' => '/agents/456' },
150 },
151];
152 # 4, code will GET the new Agent
153$ua->{responses}->{get} = [
154 {
155 headers => { 'X-Percona-Resource-Type' => 'Agent' },
156 content => as_hashref($return_agent, with_links =>1 ),
157 },
158];
159
160@ok = qw(1 1 0);
161@wait = ();
162@log = ();
163$ua->{requests} = [];
164
165$output = output(
166 sub {
167 ($got_agent) = pt_agent::init_agent(
168 agent => $post_agent,
169 action => 'post',
170 link => "/agents",
171 client => $client,
172 interval => $interval,
173 tries => 5,
174 oktorun => $oktorun,
175 );
176 },
177 stderr => 1,
178);
179
180is(
181 ($got_agent ? $got_agent->hostname : ''),
182 'host2',
183 'Got and returned Agent after error'
184) or diag($output, Dumper($got_agent));
185
186is(
187 scalar @wait,
188 1,
189 "Client waited after error"
190);
191
192is_deeply(
193 $ua->{requests},
194 [
195 'POST /agents', # first attempt, 500 error
196 'POST /agents', # second attemp, 200 OK
197 'GET /agents/456', # GET new Agent
198 ],
199 "POST POST GET new Agent after error"
200) or diag(Dumper($ua->{requests}));
201
202like(
203 $log[1],
204 qr{WARNING Failed to POST /agents},
205 "POST /agents failure logged after error"
206) or diag(Dumper($ua->{requests}), Dumper(\@log));
207
208# #############################################################################
209# Init an existing agent, i.e. update it.
210# #############################################################################
211
212my $put_agent = Percona::WebAPI::Resource::Agent->new(
213 uuid => '123',
214 hostname => 'host3',
215 username => 'name3',
216 versions => {
217 },
218 links => {
219 self => '/agents/123',
220 config => '/agents/123/config',
221 },
222);
223
224$ua->{responses}->{put} = [
225 {
226 code => 200,
227 headers => {
228 Location => '/agents/123',
229 },
230 },
231];
232$ua->{responses}->{get} = [
233 {
234 code => 200,
235 headers => { 'X-Percona-Resource-Type' => 'Agent' },
236 content => as_hashref($return_agent, with_links =>1 ),
237 }
238];
239
240@wait = ();
241$ua->{requests} = [];
242
243$output = output(
244 sub {
245 ($got_agent) = pt_agent::init_agent(
246 agent => $put_agent,
247 action => 'put',
248 link => "/agents/123",
249 client => $client,
250 interval => $interval,
251 tries => 4,
252 );
253 },
254 stderr => 1,
255);
256
257is(
258 $got_agent->hostname,
259 'host2',
260 'PUT Agent'
261) or diag($output, Dumper(as_hashref($got_agent, with_links => 1)));
262
263is(
264 scalar @wait,
265 0,
266 "Client did not wait (saved Agent)"
267);
268
269is_deeply(
270 $ua->{requests},
271 [
272 'PUT /agents/123',
273 'GET /agents/123',
274 ],
275 "PUT then GET Agent"
276) or diag(Dumper($ua->{requests}));
277
278# #############################################################################
279# Status 403 (too many agents) should abort further attempts.
280# #############################################################################
281
282$ua->{responses}->{post} = [
283 { # 1, the fake error
284 code => 403,
285 },
286];
287
288@ok = qw(1 1 0);
289@wait = ();
290@log = ();
291$ua->{requests} = [];
292
293$output = output(
294 sub {
295 ($got_agent) = pt_agent::init_agent(
296 agent => $post_agent,
297 action => 'post',
298 link => "/agents",
299 client => $client,
300 interval => $interval,
301 tries => 3,
302 oktorun => $oktorun,
303 );
304 },
305 stderr => 1,
306);
307
308is(
309 scalar @wait,
310 2,
311 "Too many agents (403): waits"
312);
313
314is_deeply(
315 $ua->{requests},
316 [
317 'POST /agents',
318 'POST /agents',
319 ],
320 "Too many agents (403): tries"
321) or diag(Dumper($ua->{requests}));
322
323my $n = grep { $_ =~ m/too many agents/ } @log;
324is(
325 $n,
326 1,
327 "Too many agents (403): does not repeat warning"
328) or diag(Dumper(\@log));
329
330# #############################################################################
331# Done.
332# #############################################################################
333done_testing;
3340
=== removed file 't/pt-agent/make_new_crontab.t'
--- t/pt-agent/make_new_crontab.t 2013-04-19 20:49:01 +0000
+++ t/pt-agent/make_new_crontab.t 1970-01-01 00:00:00 +0000
@@ -1,151 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13use JSON;
14use File::Temp qw(tempfile);
15
16use Percona::Test;
17require "$trunk/bin/pt-agent";
18
19Percona::Toolkit->import(qw(have_required_args Dumper));
20
21my $sample = "t/pt-agent/samples";
22
23sub test_make_new_crontab {
24 my (%args) = @_;
25 have_required_args(\%args, qw(
26 file
27 services
28 )) or die;
29 my $file = $args{file};
30 my $services = $args{services};
31
32 my $crontab_list = slurp_file("$trunk/$sample/$file.in");
33
34 my $new_crontab = pt_agent::make_new_crontab(
35 services => $services,
36 crontab_list => $crontab_list,
37 bin_dir => '',
38 );
39
40 ok(
41 no_diff(
42 $new_crontab,
43 "$sample/$file.out",
44 cmd_output => 1,
45 ),
46 $args{name} || $file,
47 ) or diag($new_crontab);
48}
49
50my $run0 = Percona::WebAPI::Resource::Task->new(
51 name => 'query-history',
52 number => '0',
53 program => 'pt-query-digest',
54 options => '--output json',
55 output => 'spool',
56);
57
58my $svc0 = Percona::WebAPI::Resource::Service->new(
59 ts => '100',
60 name => 'query-history',
61 run_schedule => '* 8 * * 1,2,3,4,5',
62 spool_schedule => '* 9 * * 1,2,3,4,5',
63 tasks => [ $run0 ],
64);
65
66# Empty crontab, add the service.
67test_make_new_crontab(
68 file => "crontab001",
69 services => [ $svc0 ],
70);
71
72# Crontab has another line, add the service to it.
73test_make_new_crontab(
74 file => "crontab002",
75 services => [ $svc0 ],
76);
77
78# Crontab has another line and an old service, remove the old service
79# and add the current service.
80test_make_new_crontab(
81 file => "crontab003",
82 services => [ $svc0 ],
83);
84
85# Crontab has old service, remove it and add only new service.
86test_make_new_crontab(
87 file => "crontab004",
88 services => [ $svc0 ],
89);
90
91# #############################################################################
92# Use real crontab.
93# #############################################################################
94
95# The previous tests pass in a crontab file to make testing easier.
96# Now test that make_new_crontab() will run `crontab -l' if not given
97# input. To test this, we add a fake line to our crontab. If
98# make_new_crontab() really runs `crontab -l', then this fake line
99# will be in the new crontab it returns.
100
101my $crontab = `crontab -l 2>/dev/null`;
102SKIP: {
103 skip 'Crontab is not empty', 3 if $crontab;
104
105 # On most systems[1], crontab lines must end with a newline,
106 # else an error like this happens:
107 # "/tmp/new_crontab_file":1: premature EOF
108 # errors in crontab file, can't install.
109 # [1] Ubuntu 10 and Mac OS X work without the newline.
110 my ($fh, $file) = tempfile();
111 print {$fh} "* 0 * * * date > /dev/null\n";
112 close $fh or warn "Cannot close $file: $OS_ERROR";
113 my $output = `crontab $file 2>&1`;
114
115 $crontab = `crontab -l 2>&1`;
116
117 is(
118 $crontab,
119 "* 0 * * * date > /dev/null\n",
120 "Set other crontab line"
121 ) or diag($output);
122
123 unlink $file or warn "Cannot remove $file: $OS_ERROR";
124
125 my $new_crontab = pt_agent::make_new_crontab(
126 services => [ $svc0 ],
127 bin_dir => '',
128 );
129
130 is(
131 $new_crontab,
132 "* 0 * * * date > /dev/null
133* 8 * * 1,2,3,4,5 pt-agent --run-service query-history
134* 9 * * 1,2,3,4,5 pt-agent --send-data query-history
135",
136 "Runs crontab -l by default"
137 );
138
139 system("crontab -r 2>/dev/null");
140 $crontab = `crontab -l 2>/dev/null`;
141 is(
142 $crontab,
143 "",
144 "Removed crontab"
145 );
146};
147
148# #############################################################################
149# Done.
150# #############################################################################
151done_testing;
1520
=== removed file 't/pt-agent/replace_special_vars.t'
--- t/pt-agent/replace_special_vars.t 2013-06-17 00:28:18 +0000
+++ t/pt-agent/replace_special_vars.t 1970-01-01 00:00:00 +0000
@@ -1,73 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13use JSON;
14use File::Temp qw(tempfile);
15
16use Percona::Test;
17use Percona::Test::Mock::AgentLogger;
18require "$trunk/bin/pt-agent";
19
20Percona::Toolkit->import(qw(have_required_args Dumper));
21
22my @log;
23my $logger = Percona::Test::Mock::AgentLogger->new(log => \@log);
24pt_agent::_logger($logger);
25
26my @output_files = ();
27my $store = {};
28
29sub test_replace {
30 my (%args) = @_;
31 have_required_args(\%args, qw(
32 cmd
33 expect
34 )) or die;
35 my $cmd = $args{cmd};
36 my $expect = $args{expect};
37
38 my $new_cmd = pt_agent::replace_special_vars(
39 cmd => $cmd,
40 output_files => \@output_files,
41 service => 'service-name',
42 lib_dir => '/var/lib/pt-agent',
43 meta_dir => '/var/lib/pt-agent/meta',
44 stage_dir => '/var/spool/.tmp',
45 spool_dir => '/var/spool',
46 bin_dir => $trunk,
47 ts => '123',
48 store => $store,
49 );
50
51 is(
52 $new_cmd,
53 $expect,
54 $cmd,
55 );
56};
57
58@output_files = qw(zero one two);
59test_replace(
60 cmd => "pt-query-digest __RUN_0_OUTPUT__",
61 expect => "pt-query-digest zero",
62);
63
64$store->{slow_query_log_file} = 'slow.log';
65test_replace(
66 cmd => "echo '__STORE_slow_query_log_file__' > /var/spool/pt-agent/.tmp/1371269644.rotate-slow-query-log-all-5.1.slow_query_log_file",
67 expect => "echo 'slow.log' > /var/spool/pt-agent/.tmp/1371269644.rotate-slow-query-log-all-5.1.slow_query_log_file",
68);
69
70# #############################################################################
71# Done.
72# #############################################################################
73done_testing;
740
=== removed file 't/pt-agent/run_agent.t'
--- t/pt-agent/run_agent.t 2013-06-17 04:01:30 +0000
+++ t/pt-agent/run_agent.t 1970-01-01 00:00:00 +0000
@@ -1,527 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13
14plan skip_all => "Need to make start-service testable";
15
16use JSON;
17use File::Temp qw(tempdir);
18
19use Percona::Test;
20use Sandbox;
21use Percona::Test::Mock::UserAgent;
22use Percona::Test::Mock::AgentLogger;
23require "$trunk/bin/pt-agent";
24
25my $dp = new DSNParser(opts=>$dsn_opts);
26my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
27my $dbh = $sb->get_dbh_for('master');
28my $dsn = $sb->dsn_for('master');
29my $o = new OptionParser();
30$o->get_specs("$trunk/bin/pt-agent");
31$o->get_opts();
32my $cxn = Cxn->new(
33 dsn_string => $dsn,
34 OptionParser => $o,
35 DSNParser => $dp,
36);
37
38Percona::Toolkit->import(qw(Dumper));
39Percona::WebAPI::Representation->import(qw(as_hashref));
40
41# Running the agent is going to cause it to schedule the services,
42# i.e. write a real crontab. The test box/user shouldn't have a
43# crontab, so we'll warn and clobber it if there is one.
44my $crontab = `crontab -l 2>/dev/null`;
45if ( $crontab ) {
46 warn "Removing crontab: $crontab\n";
47 `crontab -r`;
48}
49
50# Fake --lib and --spool dirs.
51my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX"); #, CLEANUP => 1);
52mkdir "$tmpdir/spool" or die "Error making $tmpdir/spool: $OS_ERROR";
53
54my @log;
55my $logger = Percona::Test::Mock::AgentLogger->new(log => \@log);
56pt_agent::_logger($logger);
57
58# #############################################################################
59# Create mock client and Agent
60# #############################################################################
61
62# These aren't the real tests yet: to run_agent, first we need
63# a client and Agent, so create mock ones.
64
65my $output;
66my $json = JSON->new->canonical([1])->pretty;
67$json->allow_blessed([]);
68$json->convert_blessed([]);
69
70my $ua = Percona::Test::Mock::UserAgent->new(
71 encode => sub { my $c = shift; return $json->encode($c || {}) },
72);
73
74my $client = eval {
75 Percona::WebAPI::Client->new(
76 api_key => '123',
77 ua => $ua,
78 );
79};
80
81is(
82 $EVAL_ERROR,
83 '',
84 'Create mock client'
85) or die;
86
87my $agent = Percona::WebAPI::Resource::Agent->new(
88 uuid => '123',
89 hostname => 'host',
90 username => 'user',
91 links => {
92 self => '/agents/123',
93 config => '/agents/123/config',
94 },
95);
96
97my $daemon = Daemon->new(
98 daemonzie => 0,
99);
100
101my @wait;
102my $interval = sub {
103 my $t = shift;
104 push @wait, $t;
105 print "interval=" . (defined $t ? $t : 'undef') . "\n";
106};
107
108# #############################################################################
109# Test run_agent
110# #############################################################################
111
112my $config = Percona::WebAPI::Resource::Config->new(
113 ts => 1363720060,
114 name => 'Default',
115 options => {
116 'lib' => $tmpdir, # required
117 'spool' => "$tmpdir/spool", # required
118 'check-interval' => "11",
119 },
120 links => {
121 self => '/agents/123/config',
122 services => '/agents/123/services',
123 },
124);
125
126my $run0 = Percona::WebAPI::Resource::Task->new(
127 name => 'query-history',
128 number => '0',
129 program => 'pt-query-digest',
130 options => '--output json',
131 output => 'spool',
132);
133
134my $svc0 = Percona::WebAPI::Resource::Service->new(
135 ts => 100,
136 name => 'query-history',
137 run_schedule => '1 * * * *',
138 spool_schedule => '2 * * * *',
139 tasks => [ $run0 ],
140 links => {
141 self => '/query-history',
142 data => '/query-history/data',
143 },
144);
145
146my $run1 = Percona::WebAPI::Resource::Task->new(
147 name => 'start-query-history',
148 number => '0',
149 program => 'echo "start-qh"',
150);
151
152my $start_qh = Percona::WebAPI::Resource::Service->new(
153 ts => '100',
154 name => 'start-query-history',
155 meta => 1,
156 tasks => [ $run1 ],
157 links => {
158 self => '/query-history',
159 data => '/query-history/data',
160 },
161);
162
163$ua->{responses}->{get} = [
164 {
165 headers => { 'X-Percona-Resource-Type' => 'Config' },
166 content => as_hashref($config, with_links => 1),
167 },
168 {
169 headers => { 'X-Percona-Resource-Type' => 'Service' },
170 content => [
171 as_hashref($start_qh, with_links => 1),
172 as_hashref($svc0, with_links => 1),
173 ],
174 },
175];
176
177my $safeguards = Safeguards->new(
178 disk_bytes_free => 1024,
179 disk_pct_free => 1,
180);
181
182# The only thing pt-agent must have is the API key in the config file,
183# everything else relies on defaults until the first Config is gotten
184# from Percona.
185my $config_file = pt_agent::get_config_file();
186unlink $config_file if -f $config_file;
187
188like(
189 $config_file,
190 qr/$ENV{LOGNAME}\/\.pt-agent.conf$/,
191 "Default config file is ~/.pt-agent.config"
192);
193
194pt_agent::write_config(
195 config => $config
196);
197
198diag(`echo 'api-key=123' >> $config_file`);
199
200is(
201 `cat $config_file`,
202 "check-interval=11\nlib=$tmpdir\nspool=$tmpdir/spool\napi-key=123\n",
203 "Write Config to config file"
204);
205
206pt_agent::save_agent(
207 agent => $agent,
208 lib_dir => $tmpdir,
209);
210
211my @ok_code = (); # callbacks
212my @oktorun = (
213 1, # 1st main loop check
214 0, # 2nd main loop check
215);
216my $oktorun = sub {
217 my $ok = shift @oktorun;
218 print "oktorun=" . (defined $ok ? $ok : 'undef') . "\n";
219 my $code = shift @ok_code;
220 $code->() if $code;
221 return $ok
222};
223
224@wait = ();
225
226$output = output(
227 sub {
228 pt_agent::run_agent(
229 # Required args
230 agent => $agent,
231 client => $client,
232 daemon => $daemon,
233 interval => $interval,
234 lib_dir => $tmpdir,
235 safeguards => $safeguards,
236 Cxn => $cxn,
237 # Optional args, for testing
238 oktorun => $oktorun,
239 json => $json,
240 bin_dir => "$trunk/bin",
241 );
242 },
243 stderr => 1,
244);
245
246is(
247 scalar @wait,
248 1,
249 "Called interval once"
250);
251
252is(
253 $wait[0],
254 11,
255 "... used Config->options->check-interval"
256);
257
258ok(
259 -f "$tmpdir/services/query-history",
260 "Created services/query-history"
261) or diag($output);
262
263chomp(my $n_files = `ls -1 $tmpdir/services| wc -l | awk '{print \$1}'`);
264is(
265 $n_files,
266 2,
267 "... created services/query-history and services/start-query-history"
268);
269
270ok(
271 no_diff(
272 "cat $tmpdir/services/query-history",
273 "t/pt-agent/samples/service001",
274 ),
275 "query-history service file"
276);
277
278$crontab = `crontab -l 2>/dev/null`;
279like(
280 $crontab,
281 qr/pt-agent --run-service query-history$/m,
282 "Scheduled --run-service with crontab"
283) or diag(Dumper(\@log));
284
285like(
286 $crontab,
287 qr/pt-agent --send-data query-history$/m,
288 "Scheduled --send-data with crontab"
289) or diag(Dumper(\@log));
290exit;
291# #############################################################################
292# Run run_agent again, like the agent had been stopped and restarted.
293# #############################################################################
294
295$ua->{responses}->{get} = [
296 # First check, fail
297 {
298 code => 500,
299 },
300 # interval
301 # 2nd check, init with latest Config and Services
302 {
303 headers => { 'X-Percona-Resource-Type' => 'Config' },
304 content => as_hashref($config, with_links => 1),
305 },
306 {
307 headers => { 'X-Percona-Resource-Type' => 'Service' },
308 content => [ as_hashref($svc0, with_links => 1) ],
309 },
310 # interval
311 # 3rd check, same Config and Services so nothing to do
312 {
313 headers => { 'X-Percona-Resource-Type' => 'Config' },
314 content => as_hashref($config, with_links => 1),
315 },
316 {
317 headers => { 'X-Percona-Resource-Type' => 'Service' },
318 content => [ as_hashref($svc0, with_links => 1) ],
319 },
320 # interval, oktorun=0
321];
322
323@oktorun = (
324 1, # 1st main loop check
325 # First check, error 500
326 1, # 2nd main loop check
327 # Init with latest Config and Services
328 1, # 3rd main loop check
329 # Same Config and services
330 0, # 4th main loop check
331);
332
333# Before the 3rd check, remove the config file (~/.pt-agent.conf) and
334# query-history service file. When the tool re-GETs these, they'll be
335# the same so it won't recreate them. A bug here will cause these files to
336# exist again after running.
337$ok_code[2] = sub {
338 unlink "$config_file";
339 unlink "$tmpdir/services/query-history";
340 Percona::Test::wait_until(sub { ! -f "$config_file" });
341 Percona::Test::wait_until(sub { ! -f "$tmpdir/services/query-history" });
342};
343
344@wait = ();
345
346$output = output(
347 sub {
348 pt_agent::run_agent(
349 # Required args
350 agent => $agent,
351 client => $client,
352 daemon => $daemon,
353 interval => $interval,
354 lib_dir => $tmpdir,
355 Cxn => $cxn,
356 # Optional args, for testing
357 oktorun => $oktorun,
358 json => $json,
359 );
360 },
361 stderr => 1,
362);
363
364is_deeply(
365 \@wait,
366 [ 60, 11, 11 ],
367 "Got Config after error"
368) or diag(Dumper(\@wait));
369
370ok(
371 ! -f "$config_file",
372 "No Config diff, no config file change"
373);
374
375ok(
376 ! -f "$tmpdir/services/query-history",
377 "No Service diff, no service file changes"
378);
379
380my $new_crontab = `crontab -l 2>/dev/null`;
381is(
382 $new_crontab,
383 $crontab,
384 "Crontab is the same"
385);
386
387# #############################################################################
388# Test a run_once_on_start service
389# #############################################################################
390
391diag(`rm -f $tmpdir/* >/dev/null 2>&1`);
392diag(`rm -rf $tmpdir/services/*`);
393diag(`rm -rf $tmpdir/spool/*`);
394
395# When pt-agent manually runs --run-service test-run-at-start, it's going
396# to need an API key because it doesn't call its own run_service(), it runs
397# another instance of itself with system(). So put the fake API key in
398# the default config file.
399unlink $config_file if -f $config_file;
400diag(`echo "api-key=123" > $config_file`);
401
402$config = Percona::WebAPI::Resource::Config->new(
403 ts => 1363720060,
404 name => 'Test run_once_on_start',
405 options => {
406 'check-interval' => "15",
407 'lib' => $tmpdir,
408 'spool' => "$tmpdir/spool",
409 'pid' => "$tmpdir/pid",
410 'log' => "$tmpdir/log"
411 },
412 links => {
413 self => '/agents/123/config',
414 services => '/agents/123/services',
415 },
416);
417
418$run0 = Percona::WebAPI::Resource::Task->new(
419 name => 'run-at-start',
420 number => '0',
421 program => 'date',
422 output => 'spool',
423);
424
425$svc0 = Percona::WebAPI::Resource::Service->new(
426 ts => 100,
427 name => 'test-run-at-start',
428 run_schedule => '0 0 1 1 *',
429 run_once => 1, # here's the magic
430 tasks => [ $run0 ],
431 links => {
432 self => '/query-history',
433 data => '/query-history/data',
434 },
435);
436
437$ua->{responses}->{get} = [
438 {
439 headers => { 'X-Percona-Resource-Type' => 'Config' },
440 content => as_hashref($config, with_links => 1),
441 },
442 {
443 headers => { 'X-Percona-Resource-Type' => 'Service' },
444 content => [ as_hashref($svc0, with_links => 1) ],
445 },
446 {
447 headers => { 'X-Percona-Resource-Type' => 'Config' },
448 content => as_hashref($config, with_links => 1),
449 },
450 {
451 headers => { 'X-Percona-Resource-Type' => 'Service' },
452 content => [ as_hashref($svc0, with_links => 1) ],
453 },
454];
455
456@wait = ();
457@ok_code = (); # callbacks
458@oktorun = (
459 1, # 1st main loop check
460 # Run once
461 1, # 2nd main loop check
462 # Don't run it again
463 0, # 3d main loop check
464);
465
466$output = output(
467 sub {
468 pt_agent::run_agent(
469 # Required args
470 agent => $agent,
471 client => $client,
472 daemon => $daemon,
473 interval => $interval,
474 lib_dir => $tmpdir,
475 Cxn => $cxn,
476 # Optional args, for testing
477 oktorun => $oktorun,
478 json => $json,
479 bin_dir => "$trunk/bin/",
480 );
481 },
482 stderr => 1,
483);
484
485Percona::Test::wait_for_files("$tmpdir/spool/test-run-at-start/test-run-at-start");
486
487like(
488 $output,
489 qr/Starting test-run-at-start service/,
490 "Ran service on start"
491);
492
493my @runs = $output =~ m/Starting test-run-at-start service/g;
494
495is(
496 scalar @runs,
497 1,
498 "... only ran it once"
499);
500
501chomp($output = `cat $tmpdir/spool/test-run-at-start/test-run-at-start 2>/dev/null`);
502ok(
503 $output,
504 "... service ran at start"
505) or diag($output);
506
507chomp($output = `crontab -l`);
508unlike(
509 $output,
510 qr/--run-service test-run-at-start/,
511 "... service was not scheduled"
512);
513
514# #############################################################################
515# Done.
516# #############################################################################
517
518# This shouldn't cause an error, but if it does, let it show up
519# in the results as an error.
520`crontab -r`;
521
522if ( -f $config_file ) {
523 unlink $config_file
524 or warn "Error removing $config_file: $OS_ERROR";
525}
526
527done_testing;
5280
=== removed file 't/pt-agent/run_service.t'
--- t/pt-agent/run_service.t 2013-06-17 00:28:18 +0000
+++ t/pt-agent/run_service.t 1970-01-01 00:00:00 +0000
@@ -1,503 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13use JSON;
14use File::Temp qw(tempdir);
15
16$ENV{PTTEST_PRETTY_JSON} = 1;
17
18use Percona::Test;
19use Sandbox;
20use Percona::Test::Mock::UserAgent;
21use Percona::Test::Mock::AgentLogger;
22require "$trunk/bin/pt-agent";
23
24my $dp = new DSNParser(opts=>$dsn_opts);
25my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
26my $dbh = $sb->get_dbh_for('master');
27my $dsn = $sb->dsn_for('master');
28my $o = new OptionParser();
29$o->get_specs("$trunk/bin/pt-agent");
30$o->get_opts();
31
32Percona::Toolkit->import(qw(Dumper have_required_args));
33Percona::WebAPI::Representation->import(qw(as_hashref));
34
35my @log;
36my $logger = Percona::Test::Mock::AgentLogger->new(log => \@log);
37pt_agent::_logger($logger);
38
39my $sample = "t/pt-agent/samples";
40
41# Create fake spool and lib dirs. Service-related subs in pt-agent
42# automatically add "/services" to the lib dir, but the spool dir is
43# used as-is.
44my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1);
45output(
46 sub { pt_agent::init_lib_dir(lib_dir => $tmpdir) }
47);
48my $spool_dir = "$tmpdir/spool";
49
50sub write_svc_files {
51 my (%args) = @_;
52 have_required_args(\%args, qw(
53 services
54 )) or die;
55 my $services = $args{services};
56
57 my $output = output(
58 sub {
59 pt_agent::write_services(
60 sorted_services => { added => $services },
61 lib_dir => $tmpdir,
62 );
63 },
64 stderr => 1,
65 die => 1,
66 );
67}
68
69# #############################################################################
70# Create mock client and Agent
71# #############################################################################
72
73my $json = JSON->new->canonical([1])->pretty;
74$json->allow_blessed([]);
75$json->convert_blessed([]);
76
77my $ua = Percona::Test::Mock::UserAgent->new(
78 encode => sub { my $c = shift; return $json->encode($c || {}) },
79);
80
81# Create cilent, get entry links
82my $links = {
83 agents => '/agents',
84 config => '/agents/1/config',
85 services => '/agents/1/services',
86 'query-history' => '/query-history',
87};
88
89$ua->{responses}->{get} = [
90 {
91 content => $links,
92 },
93];
94
95my $client = eval {
96 Percona::WebAPI::Client->new(
97 api_key => '123',
98 ua => $ua,
99 );
100};
101is(
102 $EVAL_ERROR,
103 '',
104 'Create mock client'
105) or die;
106
107my $agent = Percona::WebAPI::Resource::Agent->new(
108 uuid => '123',
109 hostname => 'prod1',
110 links => $links,
111);
112
113is_deeply(
114 as_hashref($agent),
115 {
116 uuid => '123',
117 hostname => 'prod1',
118 },
119 'Create mock Agent'
120) or die;
121
122# #############################################################################
123# Simple single task service using a program.
124# #############################################################################
125
126my $run0 = Percona::WebAPI::Resource::Task->new(
127 name => 'query-history',
128 number => '0',
129 program => "__BIN_DIR__/pt-query-digest --output json $trunk/t/lib/samples/slowlogs/slow008.txt",
130 output => 'spool',
131);
132
133my $svc0 = Percona::WebAPI::Resource::Service->new(
134 ts => 100,
135 name => 'query-history',
136 run_schedule => '1 * * * *',
137 spool_schedule => '2 * * * *',
138 tasks => [ $run0 ],
139);
140
141write_svc_files(
142 services => [ $svc0 ],
143);
144
145$ua->{responses}->{get} = [
146 {
147 headers => { 'X-Percona-Resource-Type' => 'Agent' },
148 content => as_hashref($agent, with_links => 1),
149 },
150];
151
152my $exit_status;
153my $output = output(
154 sub {
155 $exit_status = pt_agent::run_service(
156 api_key => '123',
157 service => 'query-history',
158 lib_dir => $tmpdir,
159 spool_dir => $spool_dir,
160 Cxn => '',
161 # for testing:
162 client => $client,
163 agent => $agent,
164 entry_links => $links,
165 prefix => '1',
166 json => $json,
167 bin_dir => "$trunk/bin",
168 );
169 },
170);
171
172ok(
173 no_diff(
174 "cat $tmpdir/spool/query-history/1.query-history.data",
175 "$sample/query-history/data001.json",
176 post_pipe => 'grep -v \'"name" :\'',
177 ),
178 "1 run: spool data (query-history/data001.json)"
179) or diag(
180 `ls -l $tmpdir/spool/query-history/`,
181 `cat $tmpdir/logs/query-history.run`,
182 Dumper(\@log)
183);
184
185chomp(my $n_files = `ls -1 $spool_dir/query-history/*.data | wc -l | awk '{print \$1}'`);
186is(
187 $n_files,
188 1,
189 "1 run: only wrote spool data"
190) or diag(`ls -l $spool_dir`);
191
192is(
193 $exit_status,
194 0,
195 "1 run: exit 0"
196);
197
198ok(
199 -f "$tmpdir/spool/query-history/1.query-history.meta",
200 "1 run: .meta file exists"
201);
202
203# #############################################################################
204# Service with two task, both using a program.
205# #############################################################################
206
207diag(`rm -rf $tmpdir/spool/* $tmpdir/services/*`);
208@log = ();
209
210# The result is the same as the previous single-run test, but instead of
211# having pqd read the slowlog directly, we have the first run cat the
212# log to a tmp file which pt-agent should auto-create. Then pqd in run1
213# references this tmp file.
214
215$run0 = Percona::WebAPI::Resource::Task->new(
216 name => 'cat-slow-log',
217 number => '0',
218 program => "cat $trunk/t/lib/samples/slowlogs/slow008.txt",
219 output => 'tmp',
220);
221
222my $run1 = Percona::WebAPI::Resource::Task->new(
223 name => 'query-history',
224 number => '1',
225 program => "__BIN_DIR__/pt-query-digest --output json __RUN_0_OUTPUT__",
226 output => 'spool',
227);
228
229$svc0 = Percona::WebAPI::Resource::Service->new(
230 ts => 100,
231 name => 'query-history',
232 run_schedule => '3 * * * *',
233 spool_schedule => '4 * * * *',
234 tasks => [ $run0, $run1 ],
235);
236
237write_svc_files(
238 services => [ $svc0 ],
239);
240
241$ua->{responses}->{get} = [
242 {
243 headers => { 'X-Percona-Resource-Type' => 'Agent' },
244 content => as_hashref($agent, with_links => 1),
245 },
246];
247
248$output = output(
249 sub {
250 $exit_status = pt_agent::run_service(
251 api_key => '123',
252 service => 'query-history',
253 spool_dir => $spool_dir,
254 lib_dir => $tmpdir,
255 Cxn => '',
256 # for testing:
257 client => $client,
258 agent => $agent,
259 entry_links => $links,
260 prefix => '2',
261 json => $json,
262 bin_dir => "$trunk/bin",
263 );
264 },
265);
266
267ok(
268 no_diff(
269 "cat $tmpdir/spool/query-history/2.query-history.data",
270 "$sample/query-history/data001.json",
271 post_pipe => 'grep -v \'"name" :\'',
272 ),
273 "2 runs: spool data (query-history/data001.json)"
274) or diag(
275 `ls -l $tmpdir/spool/query-history/`,
276 `cat $tmpdir/logs/query-history.run`,
277 Dumper(\@log)
278);
279
280chomp($n_files = `ls -1 $spool_dir/query-history/*.data | wc -l | awk '{print \$1}'`);
281is(
282 $n_files,
283 1,
284 "2 runs: only wrote spool data"
285) or diag(`ls -l $spool_dir`);
286
287is(
288 $exit_status,
289 0,
290 "2 runs: exit 0"
291);
292
293my @tmp_files = glob "$tmpdir/spool/.tmp/*";
294is_deeply(
295 \@tmp_files,
296 [],
297 "2 runs: temp file removed"
298);
299
300# #############################################################################
301# More realistc: 3 services, multiple tasks, using programs and queries.
302# #############################################################################
303
304SKIP: {
305 skip 'Cannot connect to sandbox master', 5 unless $dbh;
306 skip 'No HOME environment variable', 5 unless $ENV{HOME};
307
308 diag(`rm -rf $tmpdir/spool/* $tmpdir/services/*`);
309 @log = ();
310
311 my (undef, $old_genlog) = $dbh->selectrow_array("SHOW VARIABLES LIKE 'general_log_file'");
312
313 my $new_genlog = "$tmpdir/genlog";
314
315 # First service: set up
316 my $task00 = Percona::WebAPI::Resource::Task->new(
317 name => 'disable-gen-log',
318 number => '0',
319 query => "SET GLOBAL general_log=OFF",
320 );
321 my $task01 = Percona::WebAPI::Resource::Task->new(
322 name => 'set-gen-log-file',
323 number => '1',
324 query => "SET GLOBAL general_log_file='$new_genlog'",
325 );
326 my $task02 = Percona::WebAPI::Resource::Task->new(
327 name => 'enable-gen-log',
328 number => '2',
329 query => "SET GLOBAL general_log=ON",
330 );
331 my $svc0 = Percona::WebAPI::Resource::Service->new(
332 ts => 100,
333 name => 'enable-gen-log',
334 run_schedule => '1 * * * *',
335 spool_schedule => '2 * * * *',
336 tasks => [ $task00, $task01, $task02 ],
337 );
338
339 # Second service: the actual service
340 my $task10 = Percona::WebAPI::Resource::Task->new(
341 name => 'query-history',
342 number => '1',
343 program => "$trunk/bin/pt-query-digest --output json --type genlog $new_genlog",
344 output => 'spool',
345 );
346 my $svc1 = Percona::WebAPI::Resource::Service->new(
347 ts => 100,
348 name => 'query-history',
349 run_schedule => '3 * * * *',
350 spool_schedule => '4 * * * *',
351 tasks => [ $task10 ],
352 );
353
354 # Third service: tear down
355 my $task20 = Percona::WebAPI::Resource::Task->new(
356 name => 'disable-gen-log',
357 number => '0',
358 query => "SET GLOBAL general_log=OFF",
359 );
360 my $task21 = Percona::WebAPI::Resource::Task->new(
361 name => 'set-gen-log-file',
362 number => '1',
363 query => "SET GLOBAL general_log_file='$old_genlog'",
364 );
365 my $task22 = Percona::WebAPI::Resource::Task->new(
366 name => 'enable-gen-log',
367 number => '2',
368 query => "SET GLOBAL general_log=ON",
369 );
370 my $svc2 = Percona::WebAPI::Resource::Service->new(
371 ts => 100,
372 name => 'disable-gen-log',
373 run_schedule => '5 * * * *',
374 spool_schedule => '6 * * * *',
375 tasks => [ $task20, $task21, $task22 ],
376 );
377
378 write_svc_files(
379 services => [ $svc0, $svc1, $svc2 ],
380 );
381
382 $ua->{responses}->{get} = [
383 {
384 headers => { 'X-Percona-Resource-Type' => 'Agent' },
385 content => as_hashref($agent, with_links => 1),
386 },
387 {
388 headers => { 'X-Percona-Resource-Type' => 'Agent' },
389 content => as_hashref($agent, with_links => 1),
390 },
391 {
392 headers => { 'X-Percona-Resource-Type' => 'Agent' },
393 content => as_hashref($agent, with_links => 1),
394 },
395 ];
396
397 my $cxn = Cxn->new(
398 dsn_string => $dsn,
399 OptionParser => $o,
400 DSNParser => $dp,
401 );
402
403 # Run the first service.
404 $output = output(
405 sub {
406 $exit_status = pt_agent::run_service(
407 api_key => '123',
408 service => 'enable-gen-log',
409 spool_dir => $spool_dir,
410 lib_dir => $tmpdir,
411 Cxn => $cxn,
412 # for testing:
413 client => $client,
414 agent => $agent,
415 entry_links => $links,
416 prefix => '3',
417 json => $json,
418 bin_dir => "$trunk/bin",
419 );
420 },
421 );
422
423 my (undef, $genlog) = $dbh->selectrow_array(
424 "SHOW VARIABLES LIKE 'general_log_file'");
425 is(
426 $genlog,
427 $new_genlog,
428 "Task set MySQL var"
429 ) or diag($output);
430
431 # Pretend some time passes...
432
433 # The next service doesn't need MySQL, so it shouldn't connect to it.
434 # To check this, the genlog before running and after running should
435 # be identical.
436 `cp $new_genlog $tmpdir/genlog-before`;
437
438 # Run the second service.
439 $output = output(
440 sub {
441 $exit_status = pt_agent::run_service(
442 api_key => '123',
443 service => 'query-history',
444 spool_dir => $spool_dir,
445 lib_dir => $tmpdir,
446 Cxn => $cxn,
447 # for testing:
448 client => $client,
449 agent => $agent,
450 entry_links => $links,
451 prefix => '4',
452 json => $json,
453 bin_dir => "$trunk/bin",
454 );
455 },
456 );
457
458 `cp $new_genlog $tmpdir/genlog-after`;
459 my $diff = `diff $tmpdir/genlog-before $tmpdir/genlog-after`;
460 is(
461 $diff,
462 '',
463 "Tasks didn't need MySQL, didn't connect to MySQL"
464 ) or diag($output);
465
466 # Pretend more time passes...
467
468 # Run the third service.
469 $output = output(
470 sub {
471 $exit_status = pt_agent::run_service(
472 api_key => '123',
473 service => 'disable-gen-log',
474 spool_dir => $spool_dir,
475 lib_dir => $tmpdir,
476 Cxn => $cxn,
477 # for testing:
478 client => $client,
479 agent => $agent,
480 entry_links => $links,
481 prefix => '5',
482 json => $json,
483 bin_dir => "$trunk/bin",
484 );
485 },
486 );
487
488 (undef, $genlog) = $dbh->selectrow_array(
489 "SHOW VARIABLES LIKE 'general_log_file'");
490 is(
491 $genlog,
492 $old_genlog,
493 "Task restored MySQL var"
494 ) or diag($output);
495
496 $dbh->do("SET GLOBAL general_log=ON");
497 $dbh->do("SET GLOBAL general_log_file='$old_genlog'");
498}
499
500# #############################################################################
501# Done.
502# #############################################################################
503done_testing;
5040
=== removed directory 't/pt-agent/samples'
=== removed file 't/pt-agent/samples/crontab001.in'
=== removed file 't/pt-agent/samples/crontab001.out'
--- t/pt-agent/samples/crontab001.out 2013-03-19 22:35:37 +0000
+++ t/pt-agent/samples/crontab001.out 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
1* 8 * * 1,2,3,4,5 pt-agent --run-service query-history
2* 9 * * 1,2,3,4,5 pt-agent --send-data query-history
30
=== removed file 't/pt-agent/samples/crontab002.in'
--- t/pt-agent/samples/crontab002.in 2013-01-08 17:55:58 +0000
+++ t/pt-agent/samples/crontab002.in 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
117 3 * * 1 cmd
20
=== removed file 't/pt-agent/samples/crontab002.out'
--- t/pt-agent/samples/crontab002.out 2013-03-19 22:35:37 +0000
+++ t/pt-agent/samples/crontab002.out 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
117 3 * * 1 cmd
2* 8 * * 1,2,3,4,5 pt-agent --run-service query-history
3* 9 * * 1,2,3,4,5 pt-agent --send-data query-history
40
=== removed file 't/pt-agent/samples/crontab003.in'
--- t/pt-agent/samples/crontab003.in 2013-01-08 17:55:58 +0000
+++ t/pt-agent/samples/crontab003.in 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
117 3 * * 1 cmd
2* * * * 1 pt-agent --run-service old-service
3
40
=== removed file 't/pt-agent/samples/crontab003.out'
--- t/pt-agent/samples/crontab003.out 2013-03-19 22:35:37 +0000
+++ t/pt-agent/samples/crontab003.out 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
117 3 * * 1 cmd
2* 8 * * 1,2,3,4,5 pt-agent --run-service query-history
3* 9 * * 1,2,3,4,5 pt-agent --send-data query-history
40
=== removed file 't/pt-agent/samples/crontab004.in'
--- t/pt-agent/samples/crontab004.in 2013-01-30 20:25:21 +0000
+++ t/pt-agent/samples/crontab004.in 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
11 * * * * pt-agent --run-service foo
22 * * * * pt-agent --send-data foo
30
=== removed file 't/pt-agent/samples/crontab004.out'
--- t/pt-agent/samples/crontab004.out 2013-03-19 22:35:37 +0000
+++ t/pt-agent/samples/crontab004.out 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
1* 8 * * 1,2,3,4,5 pt-agent --run-service query-history
2* 9 * * 1,2,3,4,5 pt-agent --send-data query-history
30
=== removed directory 't/pt-agent/samples/query-history'
=== removed file 't/pt-agent/samples/query-history/data001.json'
--- t/pt-agent/samples/query-history/data001.json 2013-11-08 03:03:01 +0000
+++ t/pt-agent/samples/query-history/data001.json 1970-01-01 00:00:00 +0000
@@ -1,152 +0,0 @@
1
2{
3 "classes" : [
4 {
5 "attribute" : "fingerprint",
6 "checksum" : "C72BF45D68E35A6E",
7 "distillate" : "SELECT tbl",
8 "example" : {
9 "Query_time" : "0.018799",
10 "query" : "SELECT MIN(id),MAX(id) FROM tbl",
11 "ts" : null
12 },
13 "fingerprint" : "select min(id),max(id) from tbl",
14 "histograms" : {
15 "Query_time" : [
16 0,
17 0,
18 0,
19 0,
20 1,
21 0,
22 0,
23 0
24 ]
25 },
26 "metrics" : {
27 "Lock_time" : {
28 "avg" : "0.009453",
29 "max" : "0.009453",
30 "median" : "0.009453",
31 "min" : "0.009453",
32 "pct" : "0.333333",
33 "pct_95" : "0.009453",
34 "stddev" : "0.000000",
35 "sum" : "0.009453"
36 },
37 "Query_length" : {
38 "avg" : "31",
39 "max" : "31",
40 "median" : "31",
41 "min" : "31",
42 "pct" : "0",
43 "pct_95" : "31",
44 "stddev" : "0",
45 "sum" : "31"
46 },
47 "Query_time" : {
48 "avg" : "0.018799",
49 "max" : "0.018799",
50 "median" : "0.018799",
51 "min" : "0.018799",
52 "pct" : "0.333333",
53 "pct_95" : "0.018799",
54 "stddev" : "0.000000",
55 "sum" : "0.018799"
56 },
57 "Rows_examined" : {
58 "avg" : "0",
59 "max" : "0",
60 "median" : "0",
61 "min" : "0",
62 "pct" : "0",
63 "pct_95" : "0",
64 "stddev" : "0",
65 "sum" : "0"
66 },
67 "Rows_sent" : {
68 "avg" : "0",
69 "max" : "0",
70 "median" : "0",
71 "min" : "0",
72 "pct" : "0",
73 "pct_95" : "0",
74 "stddev" : "0",
75 "sum" : "0"
76 },
77 "db" : {
78 "value" : "db2"
79 },
80 "host" : {
81 "value" : ""
82 },
83 "user" : {
84 "value" : "meow"
85 }
86 },
87 "query_count" : 1,
88 "tables" : [
89 {
90 "create" : "SHOW CREATE TABLE `db2`.`tbl`\\G",
91 "status" : "SHOW TABLE STATUS FROM `db2` LIKE 'tbl'\\G"
92 }
93 ]
94 }
95 ],
96 "global" : {
97 "files" : [
98 {
99 "size" : 656
100 }
101 ],
102 "metrics" : {
103 "Lock_time" : {
104 "avg" : "0.003151",
105 "max" : "0.009453",
106 "median" : "0.000000",
107 "min" : "0.000000",
108 "pct_95" : "0.009171",
109 "stddev" : "0.004323",
110 "sum" : "0.009453"
111 },
112 "Query_length" : {
113 "avg" : "24",
114 "max" : "31",
115 "median" : "26",
116 "min" : "14",
117 "pct_95" : "30",
118 "stddev" : "6",
119 "sum" : "72"
120 },
121 "Query_time" : {
122 "avg" : "0.006567",
123 "max" : "0.018799",
124 "median" : "0.000882",
125 "min" : "0.000002",
126 "pct_95" : "0.018157",
127 "stddev" : "0.008359",
128 "sum" : "0.019700"
129 },
130 "Rows_examined" : {
131 "avg" : "0",
132 "max" : "0",
133 "median" : "0",
134 "min" : "0",
135 "pct_95" : "0",
136 "stddev" : "0",
137 "sum" : "0"
138 },
139 "Rows_sent" : {
140 "avg" : "0",
141 "max" : "0",
142 "median" : "0",
143 "min" : "0",
144 "pct_95" : "0",
145 "stddev" : "0",
146 "sum" : "0"
147 }
148 },
149 "query_count" : 3,
150 "unique_query_count" : 3
151 }
152}
1530
=== removed file 't/pt-agent/samples/query-history/data001.send'
--- t/pt-agent/samples/query-history/data001.send 2013-11-08 03:03:01 +0000
+++ t/pt-agent/samples/query-history/data001.send 1970-01-01 00:00:00 +0000
@@ -1,166 +0,0 @@
1--Ym91bmRhcnk
2Content-Disposition: form-data; name="agent"
3
4{
5 "hostname" : "prod1",
6 "uuid" : "123"
7}
8--Ym91bmRhcnk
9Content-Disposition: form-data; name="meta"
10
11
12--Ym91bmRhcnk
13Content-Disposition: form-data; name="data"
14
15{
16 "classes" : [
17 {
18 "attribute" : "fingerprint",
19 "checksum" : "C72BF45D68E35A6E",
20 "distillate" : "SELECT tbl",
21 "example" : {
22 "Query_time" : "0.018799",
23 "query" : "SELECT MIN(id),MAX(id) FROM tbl",
24 "ts" : null
25 },
26 "fingerprint" : "select min(id),max(id) from tbl",
27 "histograms" : {
28 "Query_time" : [
29 0,
30 0,
31 0,
32 0,
33 1,
34 0,
35 0,
36 0
37 ]
38 },
39 "metrics" : {
40 "Lock_time" : {
41 "avg" : "0.009453",
42 "max" : "0.009453",
43 "median" : "0.009453",
44 "min" : "0.009453",
45 "pct" : "0.333333",
46 "pct_95" : "0.009453",
47 "stddev" : "0.000000",
48 "sum" : "0.009453"
49 },
50 "Query_length" : {
51 "avg" : "31",
52 "max" : "31",
53 "median" : "31",
54 "min" : "31",
55 "pct" : "0",
56 "pct_95" : "31",
57 "stddev" : "0",
58 "sum" : "31"
59 },
60 "Query_time" : {
61 "avg" : "0.018799",
62 "max" : "0.018799",
63 "median" : "0.018799",
64 "min" : "0.018799",
65 "pct" : "0.333333",
66 "pct_95" : "0.018799",
67 "stddev" : "0.000000",
68 "sum" : "0.018799"
69 },
70 "Rows_examined" : {
71 "avg" : "0",
72 "max" : "0",
73 "median" : "0",
74 "min" : "0",
75 "pct" : "0",
76 "pct_95" : "0",
77 "stddev" : "0",
78 "sum" : "0"
79 },
80 "Rows_sent" : {
81 "avg" : "0",
82 "max" : "0",
83 "median" : "0",
84 "min" : "0",
85 "pct" : "0",
86 "pct_95" : "0",
87 "stddev" : "0",
88 "sum" : "0"
89 },
90 "db" : {
91 "value" : "db2"
92 },
93 "host" : {
94 "value" : ""
95 },
96 "user" : {
97 "value" : "meow"
98 }
99 },
100 "query_count" : 1,
101 "tables" : [
102 {
103 "create" : "SHOW CREATE TABLE `db2`.`tbl`\\G",
104 "status" : "SHOW TABLE STATUS FROM `db2` LIKE 'tbl'\\G"
105 }
106 ]
107 }
108 ],
109 "global" : {
110 "files" : [
111 {
112 "size" : 656
113 }
114 ],
115 "metrics" : {
116 "Lock_time" : {
117 "avg" : "0.003151",
118 "max" : "0.009453",
119 "median" : "0.000000",
120 "min" : "0.000000",
121 "pct_95" : "0.009171",
122 "stddev" : "0.004323",
123 "sum" : "0.009453"
124 },
125 "Query_length" : {
126 "avg" : "24",
127 "max" : "31",
128 "median" : "26",
129 "min" : "14",
130 "pct_95" : "30",
131 "stddev" : "6",
132 "sum" : "72"
133 },
134 "Query_time" : {
135 "avg" : "0.006567",
136 "max" : "0.018799",
137 "median" : "0.000882",
138 "min" : "0.000002",
139 "pct_95" : "0.018157",
140 "stddev" : "0.008359",
141 "sum" : "0.019700"
142 },
143 "Rows_examined" : {
144 "avg" : "0",
145 "max" : "0",
146 "median" : "0",
147 "min" : "0",
148 "pct_95" : "0",
149 "stddev" : "0",
150 "sum" : "0"
151 },
152 "Rows_sent" : {
153 "avg" : "0",
154 "max" : "0",
155 "median" : "0",
156 "min" : "0",
157 "pct_95" : "0",
158 "stddev" : "0",
159 "sum" : "0"
160 }
161 },
162 "query_count" : 3,
163 "unique_query_count" : 3
164 }
165}
166--Ym91bmRhcnk
1670
=== removed file 't/pt-agent/samples/service001'
--- t/pt-agent/samples/service001 2013-06-17 04:01:30 +0000
+++ t/pt-agent/samples/service001 1970-01-01 00:00:00 +0000
@@ -1,19 +0,0 @@
1{
2 "links" : {
3 "data" : "/query-history/data",
4 "self" : "/query-history"
5 },
6 "name" : "query-history",
7 "run_schedule" : "1 * * * *",
8 "spool_schedule" : "2 * * * *",
9 "tasks" : [
10 {
11 "name" : "query-history",
12 "number" : "0",
13 "options" : "--output json",
14 "output" : "spool",
15 "program" : "pt-query-digest"
16 }
17 ],
18 "ts" : 100
19}
200
=== removed file 't/pt-agent/samples/write_services001'
--- t/pt-agent/samples/write_services001 2013-06-17 00:28:18 +0000
+++ t/pt-agent/samples/write_services001 1970-01-01 00:00:00 +0000
@@ -1,19 +0,0 @@
1{
2 "links" : {
3 "data" : "/query-history/data",
4 "self" : "/query-history"
5 },
6 "name" : "query-history",
7 "run_schedule" : "1 * * * *",
8 "spool_schedule" : "2 * * * *",
9 "tasks" : [
10 {
11 "name" : "query-history",
12 "number" : "0",
13 "options" : "--report-format profile slow008.txt",
14 "output" : "spool",
15 "program" : "pt-query-digest"
16 }
17 ],
18 "ts" : 100
19}
200
=== removed file 't/pt-agent/schedule_services.t'
--- t/pt-agent/schedule_services.t 2013-06-17 00:28:18 +0000
+++ t/pt-agent/schedule_services.t 1970-01-01 00:00:00 +0000
@@ -1,200 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13use JSON;
14use File::Temp qw(tempfile tempdir);
15
16use Percona::Test;
17use Percona::Test::Mock::AgentLogger;
18require "$trunk/bin/pt-agent";
19
20my $crontab = `crontab -l 2>/dev/null`;
21if ( $crontab ) {
22 plan skip_all => 'Crontab is not empty';
23}
24
25Percona::Toolkit->import(qw(have_required_args Dumper));
26
27my $sample = "t/pt-agent/samples";
28my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1);
29
30my @log;
31my $logger = Percona::Test::Mock::AgentLogger->new(log => \@log);
32pt_agent::_logger($logger);
33
34# #############################################################################
35# Schedule a good crontab.
36# #############################################################################
37
38my $run0 = Percona::WebAPI::Resource::Task->new(
39 name => 'query-history',
40 number => '0',
41 program => 'pt-query-digest',
42 options => '--output json',
43 output => 'spool',
44);
45
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches