mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-14 15:33:49 +00:00
Update pt-agent to implement new specs.
This commit is contained in:
493
bin/pt-agent
493
bin/pt-agent
@@ -696,24 +696,26 @@ our @EXPORT_OK = qw(
|
|||||||
);
|
);
|
||||||
|
|
||||||
sub as_hashref {
|
sub as_hashref {
|
||||||
my $resource = shift;
|
my ($resource, %args) = @_;
|
||||||
|
|
||||||
my $as_hashref = { %$resource };
|
my $as_hashref = { %$resource };
|
||||||
|
|
||||||
|
if ( !defined $args{with_links} || !$args{with_links} ) {
|
||||||
delete $as_hashref->{links};
|
delete $as_hashref->{links};
|
||||||
|
}
|
||||||
|
|
||||||
return $as_hashref;
|
return $as_hashref;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub as_json {
|
sub as_json {
|
||||||
my $resource = shift;
|
my ($resource, %args) = @_;
|
||||||
|
|
||||||
my $json = JSON->new;
|
my $json = $args{json} || JSON->new;
|
||||||
$json->allow_blessed([]);
|
$json->allow_blessed([]);
|
||||||
$json->convert_blessed([]);
|
$json->convert_blessed([]);
|
||||||
|
|
||||||
return $json->encode(
|
return $json->encode(
|
||||||
ref $resource eq 'ARRAY' ? $resource : as_hashref($resource)
|
ref $resource eq 'ARRAY' ? $resource : as_hashref($resource, %args)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -774,20 +776,13 @@ has 'api_key' => (
|
|||||||
required => 1,
|
required => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
has 'base_url' => (
|
has 'entry_link' => (
|
||||||
is => 'rw',
|
is => 'rw',
|
||||||
isa => 'Str',
|
isa => 'Str',
|
||||||
default => sub { return 'https://api.tools.percona.com' },
|
default => sub { return 'https://api.tools.percona.com' },
|
||||||
required => 0,
|
required => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
has 'links' => (
|
|
||||||
is => 'rw',
|
|
||||||
isa => 'HashRef',
|
|
||||||
lazy => 1,
|
|
||||||
default => sub { return +{} },
|
|
||||||
);
|
|
||||||
|
|
||||||
has 'ua' => (
|
has 'ua' => (
|
||||||
is => 'rw',
|
is => 'rw',
|
||||||
isa => 'Object',
|
isa => 'Object',
|
||||||
@@ -807,45 +802,23 @@ sub _build_ua {
|
|||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $ua = LWP::UserAgent->new;
|
my $ua = LWP::UserAgent->new;
|
||||||
$ua->agent("Percona::WebAPI::Client/$Percona::WebAPI::Client::VERSION");
|
$ua->agent("Percona::WebAPI::Client/$Percona::WebAPI::Client::VERSION");
|
||||||
$ua->default_header('application/json');
|
$ua->default_header('Content-Type', 'application/json');
|
||||||
$ua->default_header('X-Percona-API-Key', $self->api_key);
|
$ua->default_header('X-Percona-API-Key', $self->api_key);
|
||||||
return $ua;
|
return $ua;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub BUILD {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
eval {
|
|
||||||
$self->get(
|
|
||||||
url => $self->base_url,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
if ( my $e = $EVAL_ERROR ) {
|
|
||||||
if (blessed($e) && $e->isa('Percona::WebAPI::Exception::Request')) {
|
|
||||||
die $e;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
die "Unknown error: $e";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get {
|
sub get {
|
||||||
my ($self, %args) = @_;
|
my ($self, %args) = @_;
|
||||||
|
|
||||||
have_required_args(\%args, qw(
|
have_required_args(\%args, qw(
|
||||||
url
|
link
|
||||||
)) or die;
|
)) or die;
|
||||||
my ($url) = $args{url};
|
my ($link) = $args{link};
|
||||||
|
|
||||||
my @resources;
|
|
||||||
|
|
||||||
eval {
|
eval {
|
||||||
$self->_request(
|
$self->_request(
|
||||||
method => 'GET',
|
method => 'GET',
|
||||||
url => $url,
|
link => $link,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
if ( my $e = $EVAL_ERROR ) {
|
if ( my $e = $EVAL_ERROR ) {
|
||||||
@@ -857,7 +830,7 @@ sub get {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my $res = eval {
|
my $resource = eval {
|
||||||
decode_json($self->response->content);
|
decode_json($self->response->content);
|
||||||
};
|
};
|
||||||
if ( $EVAL_ERROR ) {
|
if ( $EVAL_ERROR ) {
|
||||||
@@ -867,21 +840,21 @@ sub get {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $objs;
|
my $resource_objects;
|
||||||
if ( my $type = $self->response->headers->{'x-percona-resource-type'} ) {
|
if ( my $type = $self->response->headers->{'x-percona-resource-type'} ) {
|
||||||
eval {
|
eval {
|
||||||
my $type = "Percona::WebAPI::Resource::$type";
|
$type = "Percona::WebAPI::Resource::$type";
|
||||||
|
if ( ref $resource eq 'ARRAY' ) {
|
||||||
if ( ref $res eq 'ARRAY' ) {
|
|
||||||
PTDEBUG && _d('Got a list of', $type, 'resources');
|
PTDEBUG && _d('Got a list of', $type, 'resources');
|
||||||
foreach my $attribs ( @$res ) {
|
$resource_objects = [];
|
||||||
|
foreach my $attribs ( @$resource ) {
|
||||||
my $obj = $type->new(%$attribs);
|
my $obj = $type->new(%$attribs);
|
||||||
push @$objs, $obj;
|
push @$resource_objects, $obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PTDEBUG && _d('Got a', $type, 'resource');
|
PTDEBUG && _d('Got a', $type, 'resource', Dumper($resource));
|
||||||
$objs = $type->new(%$res);
|
$resource_objects = $type->new(%$resource);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ( $EVAL_ERROR ) {
|
if ( $EVAL_ERROR ) {
|
||||||
@@ -889,44 +862,46 @@ sub get {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ( $res ) {
|
elsif ( exists $resource->{links} ) {
|
||||||
$self->update_links($res);
|
$resource_objects = $resource->{links};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
warn "Did not get X-Percona-Resource-Type or content from $url\n";
|
warn "Did not get X-Percona-Resource-Type or links from $link\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $objs;
|
return $resource_objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub post {
|
sub post {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return $self->_set(
|
$self->_set(
|
||||||
@_,
|
@_,
|
||||||
method => 'POST',
|
method => 'POST',
|
||||||
);
|
);
|
||||||
|
return $self->response->header('Location');
|
||||||
}
|
}
|
||||||
|
|
||||||
sub put {
|
sub put {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return $self->_set(
|
$self->_set(
|
||||||
@_,
|
@_,
|
||||||
method => 'PUT',
|
method => 'PUT',
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub delete {
|
sub delete {
|
||||||
my ($self, %args) = @_;
|
my ($self, %args) = @_;
|
||||||
|
|
||||||
have_required_args(\%args, qw(
|
have_required_args(\%args, qw(
|
||||||
url
|
link
|
||||||
)) or die;
|
)) or die;
|
||||||
my ($url) = $args{url};
|
my ($link) = $args{link};
|
||||||
|
|
||||||
eval {
|
eval {
|
||||||
$self->_request(
|
$self->_request(
|
||||||
method => 'DELETE',
|
method => 'DELETE',
|
||||||
url => $url,
|
link => $link,
|
||||||
headers => { 'Content-Length' => 0 },
|
headers => { 'Content-Length' => 0 },
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -948,11 +923,11 @@ sub _set {
|
|||||||
have_required_args(\%args, qw(
|
have_required_args(\%args, qw(
|
||||||
method
|
method
|
||||||
resources
|
resources
|
||||||
url
|
link
|
||||||
)) or die;
|
)) or die;
|
||||||
my $method = $args{method};
|
my $method = $args{method};
|
||||||
my $res = $args{resources};
|
my $res = $args{resources};
|
||||||
my $url = $args{url};
|
my $link = $args{link};
|
||||||
|
|
||||||
my $content = '';
|
my $content = '';
|
||||||
if ( ref($res) eq 'ARRAY' ) {
|
if ( ref($res) eq 'ARRAY' ) {
|
||||||
@@ -983,7 +958,7 @@ sub _set {
|
|||||||
eval {
|
eval {
|
||||||
$self->_request(
|
$self->_request(
|
||||||
method => $method,
|
method => $method,
|
||||||
url => $url,
|
link => $link,
|
||||||
content => $content,
|
content => $content,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -996,18 +971,6 @@ sub _set {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my $response = eval {
|
|
||||||
decode_json($self->response->content);
|
|
||||||
};
|
|
||||||
if ( $EVAL_ERROR ) {
|
|
||||||
warn sprintf "Error decoding response to $method $url: %s: %s",
|
|
||||||
$self->response->content,
|
|
||||||
$EVAL_ERROR;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$self->update_links($response);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,10 +979,10 @@ sub _request {
|
|||||||
|
|
||||||
have_required_args(\%args, qw(
|
have_required_args(\%args, qw(
|
||||||
method
|
method
|
||||||
url
|
link
|
||||||
)) or die;
|
)) or die;
|
||||||
my $method = $args{method};
|
my $method = $args{method};
|
||||||
my $url = $args{url};
|
my $link = $args{link};
|
||||||
|
|
||||||
my @optional_args = (
|
my @optional_args = (
|
||||||
'content',
|
'content',
|
||||||
@@ -1027,12 +990,12 @@ sub _request {
|
|||||||
);
|
);
|
||||||
my ($content, $headers) = @args{@optional_args};
|
my ($content, $headers) = @args{@optional_args};
|
||||||
|
|
||||||
my $req = HTTP::Request->new($method => $url);
|
my $req = HTTP::Request->new($method => $link);
|
||||||
$req->content($content) if $content;
|
$req->content($content) if $content;
|
||||||
if ( uc($method) eq 'DELETE' ) {
|
if ( uc($method) eq 'DELETE' ) {
|
||||||
$self->ua->default_header('Content-Length' => 0);
|
$self->ua->default_header('Content-Length' => 0);
|
||||||
}
|
}
|
||||||
PTDEBUG && _d('Request', $method, $url, Dumper($req));
|
PTDEBUG && _d('Request', $method, $link, Dumper($req));
|
||||||
|
|
||||||
my $response = $self->ua->request($req);
|
my $response = $self->ua->request($req);
|
||||||
PTDEBUG && _d('Response', Dumper($response));
|
PTDEBUG && _d('Response', Dumper($response));
|
||||||
@@ -1044,10 +1007,10 @@ sub _request {
|
|||||||
if ( !($response->code >= 200 && $response->code < 400) ) {
|
if ( !($response->code >= 200 && $response->code < 400) ) {
|
||||||
die Percona::WebAPI::Exception::Request->new(
|
die Percona::WebAPI::Exception::Request->new(
|
||||||
method => $method,
|
method => $method,
|
||||||
url => $url,
|
url => $link,
|
||||||
content => $content,
|
content => $content,
|
||||||
status => $response->code,
|
status => $response->code,
|
||||||
error => "Failed to $method $url"
|
error => "Failed to $method $link",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1056,16 +1019,6 @@ sub _request {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub update_links {
|
|
||||||
my ($self, $links) = @_;
|
|
||||||
return unless $links && ref $links && scalar keys %$links;
|
|
||||||
while (my ($rel, $link) = each %$links) {
|
|
||||||
$self->links->{$rel} = $link;
|
|
||||||
}
|
|
||||||
PTDEBUG && _d('Updated links', Dumper($self->links));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
no Lmo;
|
no Lmo;
|
||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
@@ -1161,7 +1114,13 @@ has 'versions' => (
|
|||||||
is => 'ro',
|
is => 'ro',
|
||||||
isa => 'Maybe[HashRef]',
|
isa => 'Maybe[HashRef]',
|
||||||
required => 0,
|
required => 0,
|
||||||
default => undef,
|
);
|
||||||
|
|
||||||
|
has 'links' => (
|
||||||
|
is => 'rw',
|
||||||
|
isa => 'Maybe[HashRef]',
|
||||||
|
required => 0,
|
||||||
|
default => sub { return {} },
|
||||||
);
|
);
|
||||||
|
|
||||||
no Lmo;
|
no Lmo;
|
||||||
@@ -1184,12 +1143,31 @@ package Percona::WebAPI::Resource::Config;
|
|||||||
|
|
||||||
use Lmo;
|
use Lmo;
|
||||||
|
|
||||||
|
has 'id' => (
|
||||||
|
is => 'r0',
|
||||||
|
isa => 'Int',
|
||||||
|
required => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
has 'name' => (
|
||||||
|
is => 'ro',
|
||||||
|
isa => 'Str',
|
||||||
|
required => 1,
|
||||||
|
);
|
||||||
|
|
||||||
has 'options' => (
|
has 'options' => (
|
||||||
is => 'ro',
|
is => 'ro',
|
||||||
isa => 'HashRef',
|
isa => 'HashRef',
|
||||||
required => 1,
|
required => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
has 'links' => (
|
||||||
|
is => 'rw',
|
||||||
|
isa => 'Maybe[HashRef]',
|
||||||
|
required => 0,
|
||||||
|
default => sub { return {} },
|
||||||
|
);
|
||||||
|
|
||||||
no Lmo;
|
no Lmo;
|
||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
@@ -1234,6 +1212,13 @@ has 'spool_schedule' => (
|
|||||||
required => 0,
|
required => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
has 'links' => (
|
||||||
|
is => 'rw',
|
||||||
|
isa => 'Maybe[HashRef]',
|
||||||
|
required => 0,
|
||||||
|
default => sub { return {} },
|
||||||
|
);
|
||||||
|
|
||||||
sub BUILDARGS {
|
sub BUILDARGS {
|
||||||
my ($class, %args) = @_;
|
my ($class, %args) = @_;
|
||||||
if ( ref $args{runs} eq 'ARRAY' ) {
|
if ( ref $args{runs} eq 'ARRAY' ) {
|
||||||
@@ -1289,7 +1274,6 @@ has 'query' => (
|
|||||||
is => 'ro',
|
is => 'ro',
|
||||||
isa => 'Maybe[Str]',
|
isa => 'Maybe[Str]',
|
||||||
required => 0,
|
required => 0,
|
||||||
default => undef,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
has 'output' => (
|
has 'output' => (
|
||||||
@@ -4028,11 +4012,12 @@ use Time::Local qw(timegm timelocal);
|
|||||||
use Digest::MD5 qw(md5_hex);
|
use Digest::MD5 qw(md5_hex);
|
||||||
use B qw();
|
use B qw();
|
||||||
|
|
||||||
require Exporter;
|
BEGIN {
|
||||||
our @ISA = qw(Exporter);
|
require Exporter;
|
||||||
our %EXPORT_TAGS = ();
|
our @ISA = qw(Exporter);
|
||||||
our @EXPORT = ();
|
our %EXPORT_TAGS = ();
|
||||||
our @EXPORT_OK = qw(
|
our @EXPORT = ();
|
||||||
|
our @EXPORT_OK = qw(
|
||||||
micro_t
|
micro_t
|
||||||
percentage_of
|
percentage_of
|
||||||
secs_to_time
|
secs_to_time
|
||||||
@@ -4045,7 +4030,8 @@ our @EXPORT_OK = qw(
|
|||||||
make_checksum
|
make_checksum
|
||||||
crc32
|
crc32
|
||||||
encode_json
|
encode_json
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/;
|
our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/;
|
||||||
our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/;
|
our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/;
|
||||||
@@ -4424,7 +4410,7 @@ sub main {
|
|||||||
$o->usage_or_errors();
|
$o->usage_or_errors();
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# Check the API key and agent ID.
|
# Nothing works without an API key.
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
my $api_key = $o->get('api-key');
|
my $api_key = $o->get('api-key');
|
||||||
if ( !$api_key ) {
|
if ( !$api_key ) {
|
||||||
@@ -4433,13 +4419,6 @@ sub main {
|
|||||||
. "in a --config file or specify it with --api-key.");
|
. "in a --config file or specify it with --api-key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
my $agent_id = $o->get('agent-id');
|
|
||||||
if ( ($o->get('run-service') || $o->get('send-data')) && !$agent_id ) {
|
|
||||||
_err("No agent ID was found or specified. --run-service and "
|
|
||||||
. "--send-data require an agent ID. Run pt-agent without these "
|
|
||||||
. "options to create and configure the agent, then try again.");
|
|
||||||
}
|
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# --run-service
|
# --run-service
|
||||||
# This runs locally and offline, doesn't need a web API connection.
|
# This runs locally and offline, doesn't need a web API connection.
|
||||||
@@ -4455,37 +4434,8 @@ sub main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
# Connect to the Percona web API.
|
|
||||||
# ########################################################################
|
|
||||||
my ($client, $agent) = connect_to_percona(
|
|
||||||
api_key => $api_key,
|
|
||||||
agent_id => $agent_id, # optional
|
|
||||||
);
|
|
||||||
|
|
||||||
# ########################################################################
|
|
||||||
# --send-data
|
|
||||||
# ########################################################################
|
|
||||||
if ( my $service = $o->get('send-data') ) {
|
|
||||||
# TODO: rewrite Daemon to have args passed in so we can do
|
|
||||||
# a PID file check for spool procs. Or implement file locking.
|
|
||||||
send_data(
|
|
||||||
client => $client,
|
|
||||||
agent => $agent,
|
|
||||||
service => $service,
|
|
||||||
spool_dir => $o->get('spool'),
|
|
||||||
);
|
|
||||||
_info("Done checking spool, exit $exit_status");
|
|
||||||
exit $exit_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ########################################################################
|
|
||||||
# This is the main pt-agent daemon, a long-running and resilient
|
|
||||||
# process. Only internal errors should cause it to stop. Else,
|
|
||||||
# external errors, like Percona web API not responding, should be
|
|
||||||
# retried forever.
|
|
||||||
# ########################################################################
|
|
||||||
|
|
||||||
# Daemonize first so all output goes to the --log.
|
# Daemonize first so all output goes to the --log.
|
||||||
|
# ########################################################################
|
||||||
my $daemon;
|
my $daemon;
|
||||||
if ( $o->get('daemonize') ) {
|
if ( $o->get('daemonize') ) {
|
||||||
$daemon = new Daemon(o=>$o);
|
$daemon = new Daemon(o=>$o);
|
||||||
@@ -4497,6 +4447,45 @@ sub main {
|
|||||||
$daemon->make_PID_file();
|
$daemon->make_PID_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ########################################################################
|
||||||
|
# Connect to the Percona web API.
|
||||||
|
# ########################################################################
|
||||||
|
my ($client, $agent);
|
||||||
|
eval {
|
||||||
|
($client, $agent) = connect_to_percona(
|
||||||
|
api_key => $api_key,
|
||||||
|
lib_dir => $o->get('lib'),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if ( $EVAL_ERROR ) {
|
||||||
|
PTDEBUG && _d($EVAL_ERROR);
|
||||||
|
_err("Failed to connect to the Percona web API: $EVAL_ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
|
# ########################################################################
|
||||||
|
# --send-data and exit.
|
||||||
|
# ########################################################################
|
||||||
|
if ( my $service = $o->get('send-data') ) {
|
||||||
|
# TODO: rewrite Daemon to have args passed in so we can do
|
||||||
|
# a PID file check for spool procs. Or implement file locking.
|
||||||
|
send_data(
|
||||||
|
client => $client,
|
||||||
|
agent => $agent,
|
||||||
|
service => $service,
|
||||||
|
lib_dir => $->get('lib'),
|
||||||
|
spool_dir => $o->get('spool'),
|
||||||
|
);
|
||||||
|
_info("Done sending data for the $service service, exit $exit_status");
|
||||||
|
exit $exit_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ########################################################################
|
||||||
|
# This is the main pt-agent daemon, a long-running and resilient
|
||||||
|
# process. Only internal errors should cause it to stop. Else,
|
||||||
|
# external errors, like Percona web API not responding, should be
|
||||||
|
# retried forever.
|
||||||
|
# ########################################################################
|
||||||
|
|
||||||
# Check and init the config file.
|
# Check and init the config file.
|
||||||
my $config_file = get_config_file();
|
my $config_file = get_config_file();
|
||||||
_info("Config file: $config_file");
|
_info("Config file: $config_file");
|
||||||
@@ -4560,12 +4549,10 @@ sub connect_to_percona {
|
|||||||
|
|
||||||
have_required_args(\%args, qw(
|
have_required_args(\%args, qw(
|
||||||
api_key
|
api_key
|
||||||
|
lib_dir
|
||||||
)) or die;
|
)) or die;
|
||||||
my $api_key = $args{api_key};
|
my $api_key = $args{api_key};
|
||||||
my $interval = $args{interval};
|
my $lib_dir = $args{lib_dir};
|
||||||
|
|
||||||
# Optional args
|
|
||||||
my $agent_id = $args{agent_id};
|
|
||||||
|
|
||||||
# During initial connection and agent init, wait less time
|
# During initial connection and agent init, wait less time
|
||||||
# than --check-interval between errors.
|
# than --check-interval between errors.
|
||||||
@@ -4577,18 +4564,21 @@ sub connect_to_percona {
|
|||||||
sleep $init_interval;
|
sleep $init_interval;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Get a connected Percona Web API client.
|
# Connect to https://api.pws.percona.com and get entry links.
|
||||||
my $client = get_api_client(
|
# Don't return until successful.
|
||||||
|
my ($client, $entry_links) = get_api_client(
|
||||||
api_key => $api_key,
|
api_key => $api_key,
|
||||||
tries => undef,
|
tries => undef,
|
||||||
interval => $init_wait,
|
interval => $init_wait,
|
||||||
);
|
);
|
||||||
|
|
||||||
# Start or create the agent.
|
# Create a new or update an existing Agent resource.
|
||||||
|
# Don't return until successful.
|
||||||
my $agent = init_agent(
|
my $agent = init_agent(
|
||||||
client => $client,
|
client => $client,
|
||||||
interval => $init_wait,
|
interval => $init_wait,
|
||||||
agent_id => $agent_id, # optional
|
lib_dir => $lib_dir,
|
||||||
|
agents_link => $entry_links->{agents},
|
||||||
);
|
);
|
||||||
|
|
||||||
return $client, $agent;
|
return $client, $agent;
|
||||||
@@ -4607,15 +4597,17 @@ sub get_api_client {
|
|||||||
|
|
||||||
# Optional args
|
# Optional args
|
||||||
my $tries = $args{tries};
|
my $tries = $args{tries};
|
||||||
my $oktorun = $args{oktorun} || sub { return $oktorun };
|
my $_oktorun = $args{oktorun} || sub { return $oktorun };
|
||||||
|
|
||||||
my $client;
|
my $client = Percona::WebAPI::Client->new(
|
||||||
while ( $oktorun->() && !$client && (!defined $tries || $tries--) ) {
|
|
||||||
_info("Connecting to Percona Web Services");
|
|
||||||
eval {
|
|
||||||
$client = Percona::WebAPI::Client->new(
|
|
||||||
api_key => $api_key,
|
api_key => $api_key,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
my $entry_links;
|
||||||
|
while ( $_oktorun->() && !$entry_links && (!defined $tries || $tries--) ) {
|
||||||
|
_info("Connecting to Percona Web Services");
|
||||||
|
eval {
|
||||||
|
$entry_links = $client->get(link => $client->entry_link);
|
||||||
};
|
};
|
||||||
if ( $EVAL_ERROR ) {
|
if ( $EVAL_ERROR ) {
|
||||||
_warn($EVAL_ERROR);
|
_warn($EVAL_ERROR);
|
||||||
@@ -4626,7 +4618,7 @@ sub get_api_client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $client;
|
return $client, $entry_links;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize the agent, i.e. create and return an Agent resource.
|
# Initialize the agent, i.e. create and return an Agent resource.
|
||||||
@@ -4638,14 +4630,17 @@ sub init_agent {
|
|||||||
have_required_args(\%args, qw(
|
have_required_args(\%args, qw(
|
||||||
client
|
client
|
||||||
interval
|
interval
|
||||||
|
lib_dir
|
||||||
|
agents_link
|
||||||
)) or die;
|
)) or die;
|
||||||
my $client = $args{client};
|
my $client = $args{client};
|
||||||
my $interval = $args{interval};
|
my $interval = $args{interval};
|
||||||
|
my $lib_dir = $args{lib_dir};
|
||||||
|
my $agents_link = $args{agents_link};
|
||||||
|
|
||||||
# Optional args
|
# Optional args
|
||||||
my $agent_id = $args{agent_id};
|
|
||||||
my $versions = $args{versions};
|
my $versions = $args{versions};
|
||||||
my $oktorun = $args{oktorun} || sub { return $oktorun };
|
my $_oktorun = $args{oktorun} || sub { return $oktorun };
|
||||||
|
|
||||||
_info('Initializing agent');
|
_info('Initializing agent');
|
||||||
|
|
||||||
@@ -4653,42 +4648,64 @@ sub init_agent {
|
|||||||
# have changed, this can affect how services are implemented.
|
# have changed, this can affect how services are implemented.
|
||||||
$versions ||= get_versions();
|
$versions ||= get_versions();
|
||||||
|
|
||||||
# Make an Agent resource. If there's an agent_id, the existing Agent
|
# If there's a saved agent, then this is an existing agent being
|
||||||
# is updated (PUT); else, a new agent is created (POST).
|
# restarted. Else this is a new agent.
|
||||||
|
my $agent_file = $lib_dir . "/agent";
|
||||||
|
my $agent;
|
||||||
my $action;
|
my $action;
|
||||||
if ( $agent_id ) {
|
if ( -f $agent_file ) {
|
||||||
$action = 'put';
|
_info("Reading saved Agent from $agent_file");
|
||||||
|
my $agent_hashref = decode_json(slurp($agent_file));
|
||||||
|
$agent = Percona::WebAPI::Resource::Agent->new(%$agent_hashref);
|
||||||
|
$action = 'put'; # must be lc
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$action = 'post';
|
_info("Creating new Agent");
|
||||||
$agent_id = get_uuid();
|
$action = 'post'; # must be lc
|
||||||
}
|
$agent = Percona::WebAPI::Resource::Agent->new(
|
||||||
|
id => 0, # PWS will change this
|
||||||
my $agent = Percona::WebAPI::Resource::Agent->new(
|
|
||||||
id => $agent_id,
|
|
||||||
versions => $versions,
|
versions => $versions,
|
||||||
hostname => `hostname`,
|
hostname => `hostname`,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
while ( $oktorun->() ) {
|
# Try forever to create/update the Agent. The tool can't
|
||||||
_info($action eq 'put' ? "Updating agent $agent_id"
|
# do anything without an Agent, so we must succeed to proceed.
|
||||||
: "Creating new agent $agent_id");
|
my $new_agent_link; # Location header in POST response
|
||||||
|
while ( $_oktorun->() ) {
|
||||||
|
_info($action eq 'put' ? "Updating agent " . $agent->id
|
||||||
|
: "Creating new agent");
|
||||||
eval {
|
eval {
|
||||||
$client->$action(
|
$new_agent_link = $client->$action(
|
||||||
url => $client->links->{agents},
|
link => $agents_link,
|
||||||
resources => $agent,
|
resources => $agent,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
if ( $EVAL_ERROR ) {
|
last unless $EVAL_ERROR;
|
||||||
_warn($EVAL_ERROR);
|
_warn($EVAL_ERROR);
|
||||||
$interval->();
|
$interval->();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
_info("Initialized");
|
# If the Agent was new, POST will have returned a link to
|
||||||
last;
|
# the newly created and updated Agent resource.
|
||||||
|
if ( $new_agent_link ) {
|
||||||
|
$agent = $client->get(
|
||||||
|
link => $new_agent_link,
|
||||||
|
);
|
||||||
|
eval {
|
||||||
|
save_agent(
|
||||||
|
agent => $agent,
|
||||||
|
file => $agent_file,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if ( $EVAL_ERROR ) {
|
||||||
|
_warn("Error saving Agent to $agent_file: $EVAL_ERROR\n"
|
||||||
|
. "pt-agent will continue running and try to save "
|
||||||
|
. "the Agent later.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_info("Agent initialized and ready");
|
||||||
return $agent;
|
return $agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4726,9 +4743,9 @@ sub run_agent {
|
|||||||
eval {
|
eval {
|
||||||
_info('Getting config');
|
_info('Getting config');
|
||||||
|
|
||||||
# Get the agent's config from Percona.
|
# Get the agent's Config from Percona.
|
||||||
my $new_config = $client->get(
|
my $new_config = $client->get(
|
||||||
url => $client->links->{config},
|
link => $agent->links->{config},
|
||||||
);
|
);
|
||||||
|
|
||||||
# If the current and new configs are different,
|
# If the current and new configs are different,
|
||||||
@@ -4763,13 +4780,13 @@ sub run_agent {
|
|||||||
|
|
||||||
# Get services only if there's a current, running config.
|
# Get services only if there's a current, running config.
|
||||||
# Without one, we won't know how to implement services.
|
# Without one, we won't know how to implement services.
|
||||||
if ( $config ) {
|
if ( $config && $config->links->{services} ) {
|
||||||
eval {
|
eval {
|
||||||
_info('Getting services');
|
_info('Getting services');
|
||||||
|
|
||||||
# Get services from Percona.
|
# Get services from Percona.
|
||||||
my $new_services = $client->get(
|
my $new_services = $client->get(
|
||||||
url => $client->links->{services},
|
link => $config->links->{services},
|
||||||
);
|
);
|
||||||
|
|
||||||
# If the current and new services are different,
|
# If the current and new services are different,
|
||||||
@@ -4780,6 +4797,7 @@ sub run_agent {
|
|||||||
write_services(
|
write_services(
|
||||||
services => $new_services,
|
services => $new_services,
|
||||||
lib_dir => $lib_dir,
|
lib_dir => $lib_dir,
|
||||||
|
json => $args{json}, # optional, for testing
|
||||||
);
|
);
|
||||||
|
|
||||||
schedule_services(
|
schedule_services(
|
||||||
@@ -4862,6 +4880,9 @@ sub write_services {
|
|||||||
my $services = $args{services};
|
my $services = $args{services};
|
||||||
my $lib_dir = $args{lib_dir};
|
my $lib_dir = $args{lib_dir};
|
||||||
|
|
||||||
|
# Optional args
|
||||||
|
my $json = $args{json}; # for testing
|
||||||
|
|
||||||
$lib_dir .= '/services';
|
$lib_dir .= '/services';
|
||||||
|
|
||||||
_info("Writing services to $lib_dir");
|
_info("Writing services to $lib_dir");
|
||||||
@@ -4873,7 +4894,7 @@ sub write_services {
|
|||||||
my $action = -f $file ? 'Updated' : 'Created';
|
my $action = -f $file ? 'Updated' : 'Created';
|
||||||
open my $fh, '>', $file
|
open my $fh, '>', $file
|
||||||
or die "Error opening $file: $OS_ERROR";
|
or die "Error opening $file: $OS_ERROR";
|
||||||
print { $fh } as_json($service)
|
print { $fh } as_json($service, with_links => 1, json => $json)
|
||||||
or die "Error writing to $file: $OS_ERROR";
|
or die "Error writing to $file: $OS_ERROR";
|
||||||
close $fh
|
close $fh
|
||||||
or die "Error closing $file: $OS_ERROR";
|
or die "Error closing $file: $OS_ERROR";
|
||||||
@@ -4974,9 +4995,9 @@ sub make_new_crontab {
|
|||||||
return $new_crontab;
|
return $new_crontab;
|
||||||
}
|
}
|
||||||
|
|
||||||
# #################### #
|
# ########################## #
|
||||||
# Service process subs #
|
# --run-service process subs #
|
||||||
# #################### #
|
# ########################## #
|
||||||
|
|
||||||
sub run_service {
|
sub run_service {
|
||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
@@ -5116,9 +5137,9 @@ sub replace_special_vars {
|
|||||||
return $new_cmd;
|
return $new_cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
# ################## #
|
# ######################## #
|
||||||
# Spool process subs #
|
# --send-data process subs #
|
||||||
# ################## #
|
# ######################## #
|
||||||
|
|
||||||
# Send every file or directory in each service's directory in --spool/.
|
# Send every file or directory in each service's directory in --spool/.
|
||||||
# E.g. --spool/query-monitor should contain files with pt-query-digest
|
# E.g. --spool/query-monitor should contain files with pt-query-digest
|
||||||
@@ -5130,89 +5151,70 @@ sub send_data {
|
|||||||
client
|
client
|
||||||
agent
|
agent
|
||||||
service
|
service
|
||||||
|
lib_dir
|
||||||
spool_dir
|
spool_dir
|
||||||
)) or die;
|
)) or die;
|
||||||
my $client = $args{client};
|
my $client = $args{client};
|
||||||
my $agent = $args{agent};
|
my $agent = $args{agent};
|
||||||
my $service = $args{service};
|
my $service = $args{service};
|
||||||
|
my $lib_dir = $args{lib_dir};
|
||||||
my $spool_dir = $args{spool_dir};
|
my $spool_dir = $args{spool_dir};
|
||||||
|
|
||||||
# Iterate through the service dirs in --spool/.
|
my $service_dir = $spool_dir . '/' . $service;
|
||||||
chdir $spool_dir
|
my $service_file = $lib_dir . '/services/' . $service;
|
||||||
or die "Error changing dir to $spool_dir: $OS_ERROR";
|
|
||||||
opendir(my $spool_dh, $spool_dir)
|
|
||||||
or die "Error opening $spool_dir: $OS_ERROR";
|
|
||||||
_info("Checking spool directory $spool_dir");
|
|
||||||
SERVICE:
|
|
||||||
while ( my $service_dir = readdir($spool_dh) ) {
|
|
||||||
next unless -d $service_dir && $service_dir !~ m/^\./;
|
|
||||||
|
|
||||||
# Need a link for the service to know where to send the data.
|
# Re-create the Service resource object from the saved service file.
|
||||||
# TODO: should pt-agent rm the old service dir?
|
# TODO: test
|
||||||
if ( !$client->links->{$service_dir} ) {
|
if ( !-f $service_file ) {
|
||||||
_warn("Ignoring $service_dir because there is no link for "
|
_err("Cannot send data for the $service service because "
|
||||||
. "the service. If this agent no longer implements "
|
. "$service_file does not exist.");
|
||||||
. "the service, then remove $spool_dir/$service_dir/.");
|
|
||||||
next SERVICE;
|
|
||||||
}
|
}
|
||||||
|
$service = decode_json(slurp($service_file));
|
||||||
|
$service = Percona::WebAPI::Resource::Service->new(%$service);
|
||||||
|
|
||||||
# Iterate through the data files or dirs in this service's dir.
|
# Iterate through service's spool dir and send the data file therein.
|
||||||
opendir(my $service_dh, $service_dir);
|
# TODO: if the service dir doesn't exist?
|
||||||
if ( !$service_dh ) {
|
opendir(my $service_dh, $service_dir)
|
||||||
chomp $EVAL_ERROR;
|
or die "Error opening $service_dir: $OS_ERROR";
|
||||||
_warn("Error opening $service_dir: $OS_ERROR");
|
DATA_FILE:
|
||||||
next SERVICE;
|
|
||||||
}
|
|
||||||
DATA:
|
|
||||||
while ( my $file = readdir($service_dh) ) {
|
while ( my $file = readdir($service_dh) ) {
|
||||||
next unless -f "$service_dir/$file";
|
|
||||||
$file = "$service_dir/$file";
|
$file = "$service_dir/$file";
|
||||||
|
next unless -f $file;
|
||||||
|
|
||||||
# Send the data to Percona.
|
|
||||||
eval {
|
eval {
|
||||||
if ( -d $file ) {
|
# Send the file as-is. The --run-service process should
|
||||||
# TODO
|
# have written the data in a format that's ready to send.
|
||||||
}
|
|
||||||
else {
|
|
||||||
# The file is a file, yay. Just send it as-is.
|
|
||||||
send_file(
|
send_file(
|
||||||
client => $client,
|
client => $client,
|
||||||
agent => $agent,
|
agent => $agent,
|
||||||
file => $file,
|
file => $file,
|
||||||
url => $client->links->{$service_dir},
|
link => $service->links->{send_data},
|
||||||
);
|
);
|
||||||
# TODO: url should really be Service->links->self.
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if ( $EVAL_ERROR ) {
|
if ( $EVAL_ERROR ) {
|
||||||
chomp $EVAL_ERROR;
|
chomp $EVAL_ERROR;
|
||||||
_warn("Failed to send $file: $EVAL_ERROR");
|
_warn("Failed to send $file: $EVAL_ERROR");
|
||||||
next DATA;
|
next DATA_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove the data if sent successfully.
|
# Data file sent successfully; now remove it. Failure here
|
||||||
|
# is an error, not a warning, because if we can't remove the
|
||||||
|
# file then we risk re-sending it, and currently we have no
|
||||||
|
# way to determine if a file has been sent or not other than
|
||||||
|
# whether it exists or not.
|
||||||
eval {
|
eval {
|
||||||
if ( -d $file ) {
|
|
||||||
# TODO: rmtree
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
unlink $file or die $OS_ERROR;
|
unlink $file or die $OS_ERROR;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if ( $EVAL_ERROR ) {
|
if ( $EVAL_ERROR ) {
|
||||||
chomp $EVAL_ERROR;
|
chomp $EVAL_ERROR;
|
||||||
_warn("Sent $file but failed to remove it: $EVAL_ERROR");
|
_warn("Sent $file but failed to remove it: $EVAL_ERROR");
|
||||||
last SERVICE;
|
last DATA_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
_info("Sent and removed $file");
|
_info("Sent and removed $file");
|
||||||
} # DATA
|
}
|
||||||
closedir $service_dh
|
closedir $service_dh
|
||||||
or warn "Error closing $service_dir: $OS_ERROR";
|
or warn "Error closing $service_dir: $OS_ERROR";
|
||||||
} # SERVICE
|
|
||||||
|
|
||||||
closedir $spool_dh
|
|
||||||
or warn "Error closeing $spool_dir: $OS_ERROR";
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -5225,14 +5227,15 @@ sub send_file {
|
|||||||
client
|
client
|
||||||
agent
|
agent
|
||||||
file
|
file
|
||||||
url
|
link
|
||||||
)) or die;
|
)) or die;
|
||||||
my $client = $args{client};
|
my $client = $args{client};
|
||||||
my $agent = $args{agent};
|
my $agent = $args{agent};
|
||||||
my $file = $args{file};
|
my $file = $args{file};
|
||||||
my $url = $args{url};
|
my $link = $args{link};
|
||||||
|
|
||||||
_info("Sending $file to $url");
|
my $file_size = -s $file;
|
||||||
|
_info("Sending $file ($file_size bytes) to $link");
|
||||||
|
|
||||||
# Create a multi-part resource: first the Agent, so Percona knows
|
# Create a multi-part resource: first the Agent, so Percona knows
|
||||||
# from whom this data is coming, then the contents of the file as-is.
|
# from whom this data is coming, then the contents of the file as-is.
|
||||||
@@ -5249,7 +5252,7 @@ CONTENT
|
|||||||
chomp($resource); # remove trailing newline
|
chomp($resource); # remove trailing newline
|
||||||
|
|
||||||
$client->post(
|
$client->post(
|
||||||
url => $url,
|
link => $link,
|
||||||
resources => $resource,
|
resources => $resource,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -5284,6 +5287,24 @@ sub init_config_file {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub save_agent {
|
||||||
|
my (%args) = @_;
|
||||||
|
have_required_args(\%args, qw(
|
||||||
|
agent
|
||||||
|
file
|
||||||
|
)) or die;
|
||||||
|
my $agent = $args{agent};
|
||||||
|
my $file = $args{file};
|
||||||
|
_info("Saving Agent to $file");
|
||||||
|
open my $fh, '>', $file
|
||||||
|
or die "Error opening $file: $OS_ERROR";
|
||||||
|
print { $fh } as_json($agent)
|
||||||
|
or die "Error writing to $file: $OS_ERROR";
|
||||||
|
close $fh
|
||||||
|
or die "Error closing $file: $OS_ERROR";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sub slurp {
|
sub slurp {
|
||||||
my ($file) = @_;
|
my ($file) = @_;
|
||||||
return unless -f $file;
|
return unless -f $file;
|
||||||
@@ -5321,10 +5342,6 @@ sub _err {
|
|||||||
exit $exit_status;
|
exit $exit_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get_uuid {
|
|
||||||
return '123';
|
|
||||||
}
|
|
||||||
|
|
||||||
# TODO: use VersionCheck::get_versions().
|
# TODO: use VersionCheck::get_versions().
|
||||||
sub get_versions {
|
sub get_versions {
|
||||||
return {
|
return {
|
||||||
@@ -5400,12 +5417,6 @@ L<"--run-service"> and L<"--send-data"> are mutually exclusive.
|
|||||||
|
|
||||||
=over
|
=over
|
||||||
|
|
||||||
=item --agent-id
|
|
||||||
|
|
||||||
type: string
|
|
||||||
|
|
||||||
The agent's unique ID.
|
|
||||||
|
|
||||||
=item --api-key
|
=item --api-key
|
||||||
|
|
||||||
type: string
|
type: string
|
||||||
|
@@ -20,8 +20,6 @@
|
|||||||
{
|
{
|
||||||
package Percona::Test::Mock::UserAgent;
|
package Percona::Test::Mock::UserAgent;
|
||||||
|
|
||||||
use Percona::Toolkit qw(Dumper);
|
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my ($class, %args) = @_;
|
my ($class, %args) = @_;
|
||||||
my $self = {
|
my $self = {
|
||||||
|
@@ -295,7 +295,7 @@ sub _request {
|
|||||||
if ( !($response->code >= 200 && $response->code < 400) ) {
|
if ( !($response->code >= 200 && $response->code < 400) ) {
|
||||||
die Percona::WebAPI::Exception::Request->new(
|
die Percona::WebAPI::Exception::Request->new(
|
||||||
method => $method,
|
method => $method,
|
||||||
link => $link,
|
url => $link,
|
||||||
content => $content,
|
content => $content,
|
||||||
status => $response->code,
|
status => $response->code,
|
||||||
error => "Failed to $method $link",
|
error => "Failed to $method $link",
|
||||||
|
@@ -47,14 +47,14 @@ sub as_hashref {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub as_json {
|
sub as_json {
|
||||||
my $resource = shift;
|
my ($resource, %args) = @_;
|
||||||
|
|
||||||
my $json = JSON->new;
|
my $json = $args{json} || JSON->new;
|
||||||
$json->allow_blessed([]);
|
$json->allow_blessed([]);
|
||||||
$json->convert_blessed([]);
|
$json->convert_blessed([]);
|
||||||
|
|
||||||
return $json->encode(
|
return $json->encode(
|
||||||
ref $resource eq 'ARRAY' ? $resource : as_hashref($resource)
|
ref $resource eq 'ARRAY' ? $resource : as_hashref($resource, %args)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ use warnings FATAL => 'all';
|
|||||||
use English qw(-no_match_vars);
|
use English qw(-no_match_vars);
|
||||||
use Test::More;
|
use Test::More;
|
||||||
use JSON;
|
use JSON;
|
||||||
|
use File::Temp qw(tempdir);
|
||||||
|
|
||||||
use Percona::Test;
|
use Percona::Test;
|
||||||
use Percona::Test::Mock::UserAgent;
|
use Percona::Test::Mock::UserAgent;
|
||||||
@@ -19,26 +20,19 @@ require "$trunk/bin/pt-agent";
|
|||||||
Percona::Toolkit->import(qw(Dumper));
|
Percona::Toolkit->import(qw(Dumper));
|
||||||
Percona::WebAPI::Representation->import(qw(as_hashref));
|
Percona::WebAPI::Representation->import(qw(as_hashref));
|
||||||
|
|
||||||
|
my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1);
|
||||||
|
|
||||||
my $ua = Percona::Test::Mock::UserAgent->new(
|
my $ua = Percona::Test::Mock::UserAgent->new(
|
||||||
encode => sub { my $c = shift; return encode_json($c || {}) },
|
encode => sub { my $c = shift; return encode_json($c || {}) },
|
||||||
);
|
);
|
||||||
|
|
||||||
# When Percona::WebAPI::Client is created, it gets its base_url,
|
|
||||||
# to get the API's entry links.
|
|
||||||
$ua->{responses}->{get} = [
|
|
||||||
{
|
|
||||||
content => {
|
|
||||||
agents => '/agents',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
my $client = eval {
|
my $client = eval {
|
||||||
Percona::WebAPI::Client->new(
|
Percona::WebAPI::Client->new(
|
||||||
api_key => '123',
|
api_key => '123',
|
||||||
ua => $ua,
|
ua => $ua,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
is(
|
is(
|
||||||
$EVAL_ERROR,
|
$EVAL_ERROR,
|
||||||
'',
|
'',
|
||||||
@@ -49,17 +43,29 @@ is(
|
|||||||
# Init a new agent, i.e. create it.
|
# Init a new agent, i.e. create it.
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
|
||||||
# Since we're passing agent_id, the tool will call its get_uuid()
|
my $return_agent = {
|
||||||
# and POST an Agent resource to the fake ^ agents links. It then
|
id => '123',
|
||||||
# expects config and services links.
|
hostname => `hostname`,
|
||||||
|
versions => {
|
||||||
|
'Percona::WebAPI::Client' => "$Percona::WebAPI::Client::VERSION",
|
||||||
|
'Perl' => sprintf('%vd', $PERL_VERSION),
|
||||||
|
},
|
||||||
|
links => {
|
||||||
|
self => '/agents/123',
|
||||||
|
config => '/agents/123/config',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
$ua->{responses}->{post} = [
|
$ua->{responses}->{post} = [
|
||||||
{
|
{
|
||||||
content => {
|
headers => { 'Location' => '/agents/123' },
|
||||||
agents => '/agents',
|
|
||||||
config => '/agents/123/config',
|
|
||||||
services => '/agents/123/services',
|
|
||||||
},
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
$ua->{responses}->{get} = [
|
||||||
|
{
|
||||||
|
headers => { 'X-Percona-Resource-Type' => 'Agent' },
|
||||||
|
content => $return_agent,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -78,82 +84,90 @@ my $output = output(
|
|||||||
$agent = pt_agent::init_agent(
|
$agent = pt_agent::init_agent(
|
||||||
client => $client,
|
client => $client,
|
||||||
interval => $interval,
|
interval => $interval,
|
||||||
|
agents_link => "/agents",
|
||||||
|
lib_dir => $tmpdir,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
is_deeply(
|
is_deeply(
|
||||||
as_hashref($agent),
|
as_hashref($agent, with_links => 1),
|
||||||
{
|
$return_agent,
|
||||||
id => '123',
|
|
||||||
hostname => `hostname`,
|
|
||||||
versions => {
|
|
||||||
'Percona::WebAPI::Client' => "$Percona::WebAPI::Client::VERSION",
|
|
||||||
'Perl' => sprintf('%vd', $PERL_VERSION),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Create new Agent'
|
'Create new Agent'
|
||||||
) or diag(Dumper(as_hashref($agent)));
|
) or diag($output, Dumper(as_hashref($agent, with_links => 1)));
|
||||||
|
|
||||||
is(
|
is(
|
||||||
scalar @wait,
|
scalar @wait,
|
||||||
0,
|
0,
|
||||||
"Client did not wait (new Agent)"
|
"Client did not wait (new Agent)"
|
||||||
);
|
) or diag($output);
|
||||||
|
|
||||||
is_deeply(
|
# The tool should immediately write the Agent to --lib/agent.
|
||||||
$client->links,
|
ok(
|
||||||
{
|
-f "$tmpdir/agent",
|
||||||
agents => '/agents',
|
"Wrote Agent to --lib/agent"
|
||||||
config => '/agents/123/config',
|
) or diag($output);
|
||||||
services => '/agents/123/services',
|
|
||||||
},
|
# From above, we return an Agent with id=123. Check that this
|
||||||
"Client got new links"
|
# is what the tool actually wrote.
|
||||||
) or diag(Dumper($client->links));
|
$output = `cat $tmpdir/agent 2>/dev/null`;
|
||||||
|
like(
|
||||||
|
$output,
|
||||||
|
qr/"id":"123"/,
|
||||||
|
"Saved new Agent"
|
||||||
|
) or diag($output);
|
||||||
|
|
||||||
# Repeat this test but this time fake an error, so the tool isn't
|
# Repeat this test but this time fake an error, so the tool isn't
|
||||||
# able to create the Agent first time, so it should wait (call
|
# able to create the Agent first time, so it should wait (call
|
||||||
# interval), and try again.
|
# interval), and try again.
|
||||||
|
|
||||||
|
unlink "$tmpdir/agent" if -f "$tmpdir/agent";
|
||||||
|
|
||||||
|
$return_agent->{id} = '456';
|
||||||
|
$return_agent->{links} = {
|
||||||
|
self => '/agents/456',
|
||||||
|
config => '/agents/456/config',
|
||||||
|
};
|
||||||
|
|
||||||
$ua->{responses}->{post} = [
|
$ua->{responses}->{post} = [
|
||||||
{ # 1, the fake error
|
{ # 1, the fake error
|
||||||
code => 500,
|
code => 500,
|
||||||
},
|
},
|
||||||
# 2, code should call interval
|
# 2, code should call interval
|
||||||
{ # 3, code should try again, then receive this
|
{ # 3, code should try again, then receive this
|
||||||
content => {
|
code => 200,
|
||||||
agents => '/agents',
|
headers => { 'Location' => '/agents/456' },
|
||||||
config => '/agents/456/config',
|
|
||||||
services => '/agents/456/services',
|
|
||||||
},
|
},
|
||||||
|
];
|
||||||
|
# 4, code will GET the new Agent
|
||||||
|
$ua->{responses}->{get} = [
|
||||||
|
{
|
||||||
|
headers => { 'X-Percona-Resource-Type' => 'Agent' },
|
||||||
|
content => $return_agent,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@wait = ();
|
@wait = ();
|
||||||
|
$ua->{requests} = [];
|
||||||
|
|
||||||
$output = output(
|
$output = output(
|
||||||
sub {
|
sub {
|
||||||
$agent = pt_agent::init_agent(
|
$agent = pt_agent::init_agent(
|
||||||
client => $client,
|
client => $client,
|
||||||
interval => $interval,
|
interval => $interval,
|
||||||
|
agents_link => '/agents',
|
||||||
|
lib_dir => $tmpdir,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
is_deeply(
|
is_deeply(
|
||||||
as_hashref($agent),
|
as_hashref($agent, with_links => 1),
|
||||||
{
|
$return_agent,
|
||||||
id => '123',
|
|
||||||
hostname => `hostname`,
|
|
||||||
versions => {
|
|
||||||
'Percona::WebAPI::Client' => "$Percona::WebAPI::Client::VERSION",
|
|
||||||
'Perl' => sprintf '%vd', $PERL_VERSION,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Create new Agent after error'
|
'Create new Agent after error'
|
||||||
) or diag(Dumper(as_hashref($agent)));
|
) or diag(Dumper(as_hashref($agent, with_links => 1)));
|
||||||
|
|
||||||
is(
|
is(
|
||||||
scalar @wait,
|
scalar @wait,
|
||||||
@@ -161,38 +175,63 @@ is(
|
|||||||
"Client waited"
|
"Client waited"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
is_deeply(
|
||||||
|
$ua->{requests},
|
||||||
|
[
|
||||||
|
'POST /agents', # first attempt, 500 error
|
||||||
|
'POST /agents', # second attemp, 200 OK
|
||||||
|
'GET /agents/456', # GET new Agent
|
||||||
|
],
|
||||||
|
"POST POST GET new Agent"
|
||||||
|
) or diag(Dumper($ua->{requests}));
|
||||||
|
|
||||||
like(
|
like(
|
||||||
$output,
|
$output,
|
||||||
qr{WARNING Failed to POST /agents},
|
qr{WARNING Failed to POST /agents},
|
||||||
"POST /agents failure logged"
|
"POST /agents failure logged"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
-f "$tmpdir/agent",
|
||||||
|
"Wrote Agent to --lib/agent again"
|
||||||
|
);
|
||||||
|
|
||||||
|
$output = `cat $tmpdir/agent 2>/dev/null`;
|
||||||
|
like(
|
||||||
|
$output,
|
||||||
|
qr/"id":"456"/,
|
||||||
|
"Saved new Agent again"
|
||||||
|
) or diag($output);
|
||||||
|
|
||||||
|
# Do not remove lib/agent; the next test will use it.
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Init an existing agent, i.e. update it.
|
# Init an existing agent, i.e. update it.
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
|
||||||
# When agent_id is passed to init_agent(), the tool does PUT Agent
|
# If --lib/agent exists, the tool should create an Agent obj from it
|
||||||
# to tell Percona that the Agent has come online again, and to update
|
# then attempt to PUT it to the agents link. The previous tests should
|
||||||
# the agent's versions.
|
# have left an Agent file with id=456.
|
||||||
|
|
||||||
|
my $hashref = decode_json(pt_agent::slurp("$tmpdir/agent"));
|
||||||
|
my $saved_agent = Percona::WebAPI::Resource::Agent->new(%$hashref);
|
||||||
|
|
||||||
$ua->{responses}->{put} = [
|
$ua->{responses}->{put} = [
|
||||||
{
|
{
|
||||||
content => {
|
code => 200,
|
||||||
agents => '/agents',
|
|
||||||
config => '/agents/999/config',
|
|
||||||
services => '/agents/999/services',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@wait = ();
|
@wait = ();
|
||||||
|
$ua->{requests} = [];
|
||||||
|
|
||||||
$output = output(
|
$output = output(
|
||||||
sub {
|
sub {
|
||||||
$agent = pt_agent::init_agent(
|
$agent = pt_agent::init_agent(
|
||||||
client => $client,
|
client => $client,
|
||||||
interval => $interval,
|
interval => $interval,
|
||||||
agent_id => '999',
|
agents_link => '/agents',
|
||||||
|
lib_dir => $tmpdir,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
@@ -200,23 +239,30 @@ $output = output(
|
|||||||
|
|
||||||
is_deeply(
|
is_deeply(
|
||||||
as_hashref($agent),
|
as_hashref($agent),
|
||||||
{
|
as_hashref($saved_agent),
|
||||||
id => '999',
|
'Used saved Agent'
|
||||||
hostname => `hostname`,
|
) or diag($output, Dumper(as_hashref($agent)));
|
||||||
versions => {
|
|
||||||
'Percona::WebAPI::Client' => "$Percona::WebAPI::Client::VERSION",
|
like(
|
||||||
'Perl' => sprintf '%vd', $PERL_VERSION,
|
$output,
|
||||||
}
|
qr/Reading saved Agent from $tmpdir\/agent/,
|
||||||
},
|
"Reports reading saved Agent"
|
||||||
'Update old Agent'
|
) or diag($output);
|
||||||
) or diag(Dumper(as_hashref($agent)));
|
|
||||||
|
|
||||||
is(
|
is(
|
||||||
scalar @wait,
|
scalar @wait,
|
||||||
0,
|
0,
|
||||||
"Client did not wait (old Agent)"
|
"Client did not wait (saved Agent)"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
is_deeply(
|
||||||
|
$ua->{requests},
|
||||||
|
[
|
||||||
|
'PUT /agents',
|
||||||
|
],
|
||||||
|
"PUT saved Agent"
|
||||||
|
) or diag(Dumper($ua->{requests}));
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Done.
|
# Done.
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
@@ -33,10 +33,11 @@ if ( $crontab ) {
|
|||||||
# Create mock client and Agent
|
# Create mock client and Agent
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
|
||||||
# These aren't the real tests yet: to run_agent(), first we need
|
# These aren't the real tests yet: to run_agent, first we need
|
||||||
# a client and Agent, so create mock ones.
|
# a client and Agent, so create mock ones.
|
||||||
|
|
||||||
my $json = JSON->new;
|
my $output;
|
||||||
|
my $json = JSON->new->canonical([1])->pretty;
|
||||||
$json->allow_blessed([]);
|
$json->allow_blessed([]);
|
||||||
$json->convert_blessed([]);
|
$json->convert_blessed([]);
|
||||||
|
|
||||||
@@ -44,40 +45,28 @@ my $ua = Percona::Test::Mock::UserAgent->new(
|
|||||||
encode => sub { my $c = shift; return $json->encode($c || {}) },
|
encode => sub { my $c = shift; return $json->encode($c || {}) },
|
||||||
);
|
);
|
||||||
|
|
||||||
# Create cilent, get entry links
|
|
||||||
$ua->{responses}->{get} = [
|
|
||||||
{
|
|
||||||
content => {
|
|
||||||
agents => '/agents',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
my $links = {
|
|
||||||
agents => '/agents',
|
|
||||||
config => '/agents/1/config',
|
|
||||||
services => '/agents/1/services',
|
|
||||||
};
|
|
||||||
|
|
||||||
# Init agent, put Agent resource, return more links
|
|
||||||
$ua->{responses}->{put} = [
|
|
||||||
{
|
|
||||||
content => $links,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
my $client = eval {
|
my $client = eval {
|
||||||
Percona::WebAPI::Client->new(
|
Percona::WebAPI::Client->new(
|
||||||
api_key => '123',
|
api_key => '123',
|
||||||
ua => $ua,
|
ua => $ua,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
is(
|
is(
|
||||||
$EVAL_ERROR,
|
$EVAL_ERROR,
|
||||||
'',
|
'',
|
||||||
'Create mock client'
|
'Create mock client'
|
||||||
) or die;
|
) or die;
|
||||||
|
|
||||||
|
my $agent = Percona::WebAPI::Resource::Agent->new(
|
||||||
|
id => '123',
|
||||||
|
hostname => 'host',
|
||||||
|
links => {
|
||||||
|
self => '/agents/123',
|
||||||
|
config => '/agents/123/config',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
my @wait;
|
my @wait;
|
||||||
my $interval = sub {
|
my $interval = sub {
|
||||||
my $t = shift;
|
my $t = shift;
|
||||||
@@ -85,41 +74,8 @@ my $interval = sub {
|
|||||||
print "interval=" . (defined $t ? $t : 'undef') . "\n";
|
print "interval=" . (defined $t ? $t : 'undef') . "\n";
|
||||||
};
|
};
|
||||||
|
|
||||||
my $agent;
|
|
||||||
my $output = output(
|
|
||||||
sub {
|
|
||||||
$agent = pt_agent::init_agent(
|
|
||||||
client => $client,
|
|
||||||
interval => $interval,
|
|
||||||
agent_id => 1,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
stderr => 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
my $have_agent = 1;
|
|
||||||
|
|
||||||
is_deeply(
|
|
||||||
as_hashref($agent),
|
|
||||||
{
|
|
||||||
id => '1',
|
|
||||||
hostname => `hostname`,
|
|
||||||
versions => {
|
|
||||||
'Percona::WebAPI::Client' => "$Percona::WebAPI::Client::VERSION",
|
|
||||||
'Perl' => sprintf '%vd', $PERL_VERSION,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'Create mock Agent'
|
|
||||||
) or $have_agent = 0;
|
|
||||||
|
|
||||||
# Can't run_agent() without and agent.
|
|
||||||
if ( !$have_agent ) {
|
|
||||||
diag(Dumper(as_hashref($agent)));
|
|
||||||
die;
|
|
||||||
}
|
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Test run_agent()
|
# Test run_agent
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
|
||||||
# The agent does just basically 2 things: check for new config, and
|
# The agent does just basically 2 things: check for new config, and
|
||||||
@@ -130,9 +86,15 @@ if ( !$have_agent ) {
|
|||||||
# same config.
|
# same config.
|
||||||
|
|
||||||
my $config = Percona::WebAPI::Resource::Config->new(
|
my $config = Percona::WebAPI::Resource::Config->new(
|
||||||
|
id => '1',
|
||||||
|
name => 'Default',
|
||||||
options => {
|
options => {
|
||||||
'check-interval' => "60",
|
'check-interval' => "60",
|
||||||
},
|
},
|
||||||
|
links => {
|
||||||
|
self => '/agents/123/config',
|
||||||
|
services => '/agents/123/services',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
my $run0 = Percona::WebAPI::Resource::Run->new(
|
my $run0 = Percona::WebAPI::Resource::Run->new(
|
||||||
@@ -147,16 +109,19 @@ my $svc0 = Percona::WebAPI::Resource::Service->new(
|
|||||||
run_schedule => '1 * * * *',
|
run_schedule => '1 * * * *',
|
||||||
spool_schedule => '2 * * * *',
|
spool_schedule => '2 * * * *',
|
||||||
runs => [ $run0 ],
|
runs => [ $run0 ],
|
||||||
|
links => {
|
||||||
|
send_data => '/query-monitor',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
$ua->{responses}->{get} = [
|
$ua->{responses}->{get} = [
|
||||||
{
|
{
|
||||||
headers => { 'X-Percona-Resource-Type' => 'Config' },
|
headers => { 'X-Percona-Resource-Type' => 'Config' },
|
||||||
content => as_hashref($config),
|
content => as_hashref($config, with_links => 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers => { 'X-Percona-Resource-Type' => 'Service' },
|
headers => { 'X-Percona-Resource-Type' => 'Service' },
|
||||||
content => [ as_hashref($svc0) ],
|
content => [ as_hashref($svc0, with_links => 1) ],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -198,6 +163,7 @@ $output = output(
|
|||||||
config_file => $config_file,
|
config_file => $config_file,
|
||||||
lib_dir => $tmpdir,
|
lib_dir => $tmpdir,
|
||||||
oktorun => $oktorun, # optional, for testing
|
oktorun => $oktorun, # optional, for testing
|
||||||
|
json => $json, # optional, for testing
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
@@ -224,7 +190,7 @@ is(
|
|||||||
ok(
|
ok(
|
||||||
-f "$tmpdir/services/query-monitor",
|
-f "$tmpdir/services/query-monitor",
|
||||||
"Created services/query-monitor"
|
"Created services/query-monitor"
|
||||||
);
|
) or diag($output);
|
||||||
|
|
||||||
chomp(my $n_files = `ls -1 $tmpdir/services| wc -l | awk '{print \$1}'`);
|
chomp(my $n_files = `ls -1 $tmpdir/services| wc -l | awk '{print \$1}'`);
|
||||||
is(
|
is(
|
||||||
@@ -255,7 +221,7 @@ like(
|
|||||||
);
|
);
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Run run_agent() again, like the agent had been stopped and restarted.
|
# Run run_agent again, like the agent had been stopped and restarted.
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
|
||||||
$ua->{responses}->{get} = [
|
$ua->{responses}->{get} = [
|
||||||
@@ -311,6 +277,7 @@ $output = output(
|
|||||||
config_file => $config_file,
|
config_file => $config_file,
|
||||||
lib_dir => $tmpdir,
|
lib_dir => $tmpdir,
|
||||||
oktorun => $oktorun, # optional, for testing
|
oktorun => $oktorun, # optional, for testing
|
||||||
|
json => $json, # optional, for testing
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
|
@@ -1 +1,16 @@
|
|||||||
{"spool_schedule":"2 * * * *","runs":[{"number":"0","options":"--output json","output":"spool","program":"pt-query-digest"}],"run_schedule":"1 * * * *","name":"query-monitor"}
|
{
|
||||||
|
"links" : {
|
||||||
|
"send_data" : "/query-monitor"
|
||||||
|
},
|
||||||
|
"name" : "query-monitor",
|
||||||
|
"run_schedule" : "1 * * * *",
|
||||||
|
"runs" : [
|
||||||
|
{
|
||||||
|
"number" : "0",
|
||||||
|
"options" : "--output json",
|
||||||
|
"output" : "spool",
|
||||||
|
"program" : "pt-query-digest"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"spool_schedule" : "2 * * * *"
|
||||||
|
}
|
||||||
|
16
t/pt-agent/samples/write_services001
Normal file
16
t/pt-agent/samples/write_services001
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"links" : {
|
||||||
|
"send_data" : "/query-monitor"
|
||||||
|
},
|
||||||
|
"name" : "query-monitor",
|
||||||
|
"run_schedule" : "1 * * * *",
|
||||||
|
"runs" : [
|
||||||
|
{
|
||||||
|
"number" : "0",
|
||||||
|
"options" : "--report-format profile /Users/daniel/p/pt-agent/t/lib/samples/slowlogs/slow008.txt",
|
||||||
|
"output" : "spool",
|
||||||
|
"program" : "/Users/daniel/p/pt-agent/bin/pt-query-digest"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"spool_schedule" : "2 * * * *"
|
||||||
|
}
|
@@ -84,7 +84,11 @@ is_deeply(
|
|||||||
my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1);
|
my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1);
|
||||||
mkdir "$tmpdir/query-monitor"
|
mkdir "$tmpdir/query-monitor"
|
||||||
or die "Cannot mkdir $tmpdir/query-monitor: $OS_ERROR";
|
or die "Cannot mkdir $tmpdir/query-monitor: $OS_ERROR";
|
||||||
`cp $trunk/$sample/query-monitor/data001 $tmpdir/query-monitor`;
|
mkdir "$tmpdir/services"
|
||||||
|
or die "Cannot mkdir $tmpdir/services: $OS_ERROR";
|
||||||
|
|
||||||
|
`cp $trunk/$sample/query-monitor/data001 $tmpdir/query-monitor/`;
|
||||||
|
`cp $trunk/$sample/service001 $tmpdir/services/query-monitor`;
|
||||||
|
|
||||||
$ua->{responses}->{post} = [
|
$ua->{responses}->{post} = [
|
||||||
{
|
{
|
||||||
@@ -97,8 +101,9 @@ my $output = output(
|
|||||||
pt_agent::send_data(
|
pt_agent::send_data(
|
||||||
client => $client,
|
client => $client,
|
||||||
agent => $agent,
|
agent => $agent,
|
||||||
spool_dir => $tmpdir,
|
|
||||||
service => 'query-monitor',
|
service => 'query-monitor',
|
||||||
|
lib_dir => $tmpdir,
|
||||||
|
spool_dir => $tmpdir,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
stderr => 1,
|
stderr => 1,
|
||||||
@@ -108,7 +113,15 @@ is(
|
|||||||
scalar @{$client->ua->{content}->{post}},
|
scalar @{$client->ua->{content}->{post}},
|
||||||
1,
|
1,
|
||||||
"Only sent 1 resource"
|
"Only sent 1 resource"
|
||||||
) or diag(Dumper($client->ua->{content}->{post}));
|
) or diag($output, Dumper($client->ua->{content}->{post}));
|
||||||
|
|
||||||
|
is_deeply(
|
||||||
|
$ua->{requests},
|
||||||
|
[
|
||||||
|
'POST /query-monitor',
|
||||||
|
],
|
||||||
|
"POST to Service.links.send_data"
|
||||||
|
);
|
||||||
|
|
||||||
ok(
|
ok(
|
||||||
no_diff(
|
no_diff(
|
||||||
@@ -127,13 +140,4 @@ ok(
|
|||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Done.
|
# Done.
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
|
||||||
# pt_agent::send_data() does chdir and since it and this test
|
|
||||||
# are the same process, it has chdir'ed us into the temp dir
|
|
||||||
# that Perl is going to auto-remove, so we need to chdir back
|
|
||||||
# else we'll get this error: "cannot remove path when cwd is
|
|
||||||
# /tmp/pt-agent.16588.d1bFVw for /tmp/pt-agent.16588.d1bFVw:
|
|
||||||
# at /usr/share/perl5/File/Temp.pm line 902"
|
|
||||||
chdir($ENV{PWD} || $trunk);
|
|
||||||
|
|
||||||
done_testing;
|
done_testing;
|
||||||
|
91
t/pt-agent/write_services.t
Normal file
91
t/pt-agent/write_services.t
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
|
||||||
|
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
|
||||||
|
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
|
||||||
|
};
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings FATAL => 'all';
|
||||||
|
use English qw(-no_match_vars);
|
||||||
|
use Test::More;
|
||||||
|
use JSON;
|
||||||
|
use File::Temp qw(tempdir);
|
||||||
|
|
||||||
|
use Percona::Test;
|
||||||
|
use Percona::Test::Mock::UserAgent;
|
||||||
|
require "$trunk/bin/pt-agent";
|
||||||
|
|
||||||
|
Percona::Toolkit->import(qw(Dumper have_required_args));
|
||||||
|
Percona::WebAPI::Representation->import(qw(as_hashref));
|
||||||
|
|
||||||
|
my $json = JSON->new->canonical([1])->pretty;
|
||||||
|
my $sample = "t/pt-agent/samples";
|
||||||
|
my $tmpdir = tempdir("/tmp/pt-agent.$PID.XXXXXX", CLEANUP => 1);
|
||||||
|
|
||||||
|
mkdir "$tmpdir/services" or die "Error mkdir $tmpdir/services: $OS_ERROR";
|
||||||
|
|
||||||
|
sub test_write_services {
|
||||||
|
my (%args) = @_;
|
||||||
|
have_required_args(\%args, qw(
|
||||||
|
services
|
||||||
|
file
|
||||||
|
)) or die;
|
||||||
|
my $services = $args{services};
|
||||||
|
my $file = $args{file};
|
||||||
|
|
||||||
|
die "$trunk/$sample/$file does not exist"
|
||||||
|
unless -f "$trunk/$sample/$file";
|
||||||
|
|
||||||
|
my $output = output(
|
||||||
|
sub {
|
||||||
|
pt_agent::write_services(
|
||||||
|
services => $services,
|
||||||
|
lib_dir => $tmpdir,
|
||||||
|
json => $json,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
stderr => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach my $service ( @$services ) {
|
||||||
|
my $name = $service->name;
|
||||||
|
ok(
|
||||||
|
no_diff(
|
||||||
|
"cat $tmpdir/services/$name 2>/dev/null",
|
||||||
|
"$sample/$file",
|
||||||
|
),
|
||||||
|
"$file $name"
|
||||||
|
) or diag($output, `cat $tmpdir/services/$name`);
|
||||||
|
}
|
||||||
|
|
||||||
|
diag(`rm -rf $tmpdir/*`);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $run0 = Percona::WebAPI::Resource::Run->new(
|
||||||
|
number => '0',
|
||||||
|
program => "$trunk/bin/pt-query-digest",
|
||||||
|
options => "--report-format profile $trunk/t/lib/samples/slowlogs/slow008.txt",
|
||||||
|
output => 'spool',
|
||||||
|
);
|
||||||
|
|
||||||
|
my $svc0 = Percona::WebAPI::Resource::Service->new(
|
||||||
|
name => 'query-monitor',
|
||||||
|
run_schedule => '1 * * * *',
|
||||||
|
spool_schedule => '2 * * * *',
|
||||||
|
runs => [ $run0 ],
|
||||||
|
links => { send_data => '/query-monitor' },
|
||||||
|
);
|
||||||
|
|
||||||
|
# Key thing here is that the links are written because
|
||||||
|
# --send-data <service> requires them.
|
||||||
|
test_write_services(
|
||||||
|
services => [ $svc0 ],
|
||||||
|
file => "write_services001",
|
||||||
|
);
|
||||||
|
|
||||||
|
# #############################################################################
|
||||||
|
# Done.
|
||||||
|
# #############################################################################
|
||||||
|
done_testing;
|
Reference in New Issue
Block a user