[Bps-public-commit] rt-extension-pagerduty branch master updated. bf6132aa64ae780645abb4462ee4047ce4be7b39

BPS Git Server git at git.bestpractical.com
Thu Feb 17 21:17:40 UTC 2022


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt-extension-pagerduty".

The branch, master has been updated
  discards  d589d7d308ece75ffcb3fa7a7195df350ab4c7fe (commit)
  discards  005fe6f48096eb4a8754cf6b6185cc2fb0752fda (commit)
  discards  a310d682d49747e0da63723b81c4c3d69a97bebe (commit)
  discards  ee73e8b496f6eb0bb4f8410039b9191983da7d43 (commit)
  discards  b4c51ed08532d1dbf359fdf06b6f1b6f2fa2e33f (commit)
  discards  dbcfe0e12a404e8580b6af18db160b3ca3a1031b (commit)
  discards  75ca1b055dc4981a3aea1b2c7e236dc3df1ca9e6 (commit)
       via  bf6132aa64ae780645abb4462ee4047ce4be7b39 (commit)
       via  20b50dac55af846da4007a2009f83ace3e23111f (commit)
       via  46d151018a141fa95e20dda1ab3153d420d257b0 (commit)
       via  414564362dcc26e23d59364d41825fd2e4f687fa (commit)

This update added new revisions after undoing existing revisions.  That is
to say, the old revision is not a strict subset of the new revision.  This
situation occurs when you --force push a change and generate a repository
containing something like this:

 * -- * -- B -- O -- O -- O (d589d7d308ece75ffcb3fa7a7195df350ab4c7fe)
            \
             N -- N -- N (bf6132aa64ae780645abb4462ee4047ce4be7b39)

When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit bf6132aa64ae780645abb4462ee4047ce4be7b39
Author: Brad Embree <brad at bestpractical.com>
Date:   Wed Feb 16 11:08:27 2022 -0800

    Prep for first release: version 0.01

diff --git a/Changes b/Changes
new file mode 100644
index 0000000..6a334cb
--- /dev/null
+++ b/Changes
@@ -0,0 +1,4 @@
+Revision history for RT-Extension-PagerDuty
+
+0.01 [Release Date]
+ - Initial version
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..1a45cc6
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,30 @@
+---
+abstract: 'RT-Extension-PagerDuty Extension'
+author:
+  - 'Best Practical Solutions, LLC <modules at bestpractical.com>'
+build_requires:
+  ExtUtils::MakeMaker: 6.59
+configure_requires:
+  ExtUtils::MakeMaker: 6.59
+distribution_type: module
+dynamic_config: 1
+generated_by: 'Module::Install version 1.19'
+license: gpl_2
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: RT-Extension-PagerDuty
+no_index:
+  directory:
+    - etc
+    - html
+    - inc
+requires:
+  perl: 5.10.1
+resources:
+  license: http://opensource.org/licenses/gpl-license.php
+  repository: https://github.com/bestpractical/rt-extension-pagerduty
+version: '0.01'
+x_module_install_rtx_version: '0.43'
+x_requires_rt: 5.0.0
+x_rt_too_new: 5.2.0
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..772fea1
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,12 @@
+use lib '.';
+use inc::Module::Install;
+
+RTx     'RT-Extension-PagerDuty';
+license 'gpl_2';
+repository 'https://github.com/bestpractical/rt-extension-pagerduty';
+
+requires_rt '5.0.0';
+rt_too_new '5.2.0';
+
+sign;
+WriteAll;
diff --git a/README b/README
new file mode 100644
index 0000000..59b7fa5
--- /dev/null
+++ b/README
@@ -0,0 +1,133 @@
+NAME
+    RT-Extension-PagerDuty - Two way integration with PagerDuty
+
+DESCRIPTION
+    This RT extension allows for two way integration with the PagerDuty
+    incident response platform.
+
+    On ticket creation in RT trigger an incident in PagerDuty. When a ticket
+    is acknowledged or resolved in RT update the incident in PagerDuty.
+
+    Configure a PagerDuty webhook to push noticications to RT from
+    PagerDuty. When a new incident is triggered in PagerDuty have it create
+    a ticket in RT. If an incident is acknowledged or resolved in PagerDuty
+    update the corresponding ticket in RT.
+
+RT VERSION
+    Works with RT 5.
+
+INSTALLATION
+    perl Makefile.PL
+    make
+    make install
+        May need root permissions
+
+    Edit your /opt/rt5/etc/RT_SiteConfig.pm
+        Add this line:
+
+            Plugin('RT::Extension::PagerDuty');
+
+    Clear your mason cache
+            rm -rf /opt/rt5/var/mason_data/obj
+
+    Restart your webserver
+
+CONFIGURATION
+    To define the interactions between RT and PagerDuty use the $PagerDuty
+    config option. This option takes the form of:
+
+            Set(
+                    $PagerDuty,
+                    {
+                            services => {
+                                    'PagerDuty Service ID' => {
+                        api_token => 'PagerDuty API Token',
+                        api_user => 'PagerDuty User',
+                        create_queue => 'General',
+                    }
+                },
+                queues => {
+                    'RT Queue Name' => {
+                        service => 'PagerDuty Service ID',
+                        acknowledged => 'open',
+                        resolved => 'resolved',
+                    }
+                }
+            }
+        );
+
+    The services section maps a PagerDuty service id to the token and user
+    to use for API access. The api_token and api_user values are required.
+    The optional create_queue value is the RT queue name where new RT
+    tickets should be created if a PagerDuty incident creates a new RT
+    ticket. create_queue defaults to the General queue if not specified. Use
+    '*' as the PagerDuty service id to apply to multiple PagerDuty services.
+
+    The queues section maps a RT queue name to the PagerDuty service where
+    it should trigger new incidents when a RT ticket is created. The service
+    value is required and must be a PagerDuty service id. The acknowledged
+    and resolved optional values indicate what RT ticket status to use when
+    an incident is acknowledged or resolved on PagerDuty. If not specified
+    they default to acknowledged => 'open' and resolved => 'resolved'. Use
+    '*' as the RT queue name to apply to multiple RT queues.
+
+CUSTOM FIELDS
+    This extension adds two ticket custom fields: PagerDuty ID and PagerDuty
+    URL.
+
+    When a RT ticket creates an incident on PagerDuty or an incident on
+    PagerDuty creates a RT ticket the custom fields are automatically filled
+    in. The PagerDuty URL links directly to the incident on PagerDuty.
+
+    If you would like to group the new custom fields in their own PagerDuty
+    group you can use the CustomFieldGroupings config option:
+
+        Set(
+            %CustomFieldGroupings,
+                'RT::Ticket' => [
+                'PagerDuty' => [ 'PagerDuty ID', 'PagerDuty URL' ],
+                ],  
+            );
+
+WEBHOOK USAGE
+    To call the webhook from PagerDuty:
+
+    1. Configure your web server to require Basic Authorization for the
+    location $WebPath/PagerDuty. This is *very important* because this
+    plugin does not do any authentication and must be used with HTTP Basic
+    Authorization.
+        For example:
+
+            <Location /PagerDuty/WebHook.html>
+                AuthType Basic
+                AuthName "PagerDuty WebHook"
+                AuthUserFile "/etc/apache2/pagerdutypass"
+                Require valid-user
+            </Location>
+
+        /etc/apache2/pagerdutypass could be generated by command htpasswd:
+
+            htpasswd -c /etc/apache2/pagerdutypass pagerduty
+
+    2. Go to the PagerDuty Service Integrations Webhooks
+    3. Configure a webhook, using:
+    https://user:pass@your.rt.example/PagerDuty/WebHook.html where user:pass
+    are the credentials acceptable to the Web server. Currently the only
+    event subscriptions supported are incident.acknowledged,
+    incident.resolved, and incident.triggered.
+    4. Ensure a RT user exists with the same user and pass.
+
+AUTHOR
+    Best Practical Solutions, LLC <modules at bestpractical.com>
+
+    All bugs should be reported via email to
+        bug-RT-Extension-PagerDuty at rt.cpan.org
+    or via the web at
+        http://rt.cpan.org/Public/Dist/Display.html?Name=RT-Extension-PagerDuty
+LICENSE AND COPYRIGHT
+    This software is Copyright (c) 2022 by BPS
+
+    This is free software, licensed under:
+
+      The GNU General Public License, Version 2, June 1991
+
commit 20b50dac55af846da4007a2009f83ace3e23111f
Author: Brad Embree <brad at bestpractical.com>
Date:   Wed Feb 16 11:07:54 2022 -0800

    Add PagerDuty webhook

diff --git a/html/PagerDuty/WebHook.html b/html/PagerDuty/WebHook.html
new file mode 100644
index 0000000..8b59a6c
--- /dev/null
+++ b/html/PagerDuty/WebHook.html
@@ -0,0 +1,102 @@
+<%init>
+use JSON;
+use Data::Dumper;
+
+# NOTE: This extension does NO authentication.  HTTP Basic authentication
+# must be handled by the Web server.
+
+if (!$ARGS{'POSTDATA'}) {
+    $m->abort(400);
+}
+
+# payload example: https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTkw-v3-overview#webhook-payload
+my $data;
+eval {
+    $data = decode_json($ARGS{'POSTDATA'});
+};
+
+if ($@) {
+    $m->abort(400);
+}
+
+RT->Logger->debug( 'PagerDuty webhook got data: ' . Dumper($data) );
+
+# this webhook only handles incident events
+if ( $data->{event}{data}{type} ne 'incident' ) {
+    $m->abort(400);
+}
+
+my $config = RT::Config->Get('PagerDuty') // {};
+my $service = $config->{services}{ $data->{event}{data}{service}{id} } // $config->{services}{'*'};
+
+unless ( defined $service ) {
+    RT->Logger->error( "PagerDuty webhook no service config found for id: " . $data->{event}{data}{service}{id} );
+    $m->abort(400);
+}
+
+# need to see if a ticket already exists for this incident
+my $tickets = RT::Tickets->new( RT->SystemUser );
+$tickets->LimitCustomField(
+    CUSTOMFIELD => 'PagerDuty ID',
+    OPERATOR => '=',
+    VALUE => $data->{event}{data}{id},
+);
+
+# if there is a ticket already we ignore the triggered event to avoid a loop between RT and PagerDuty
+# XXX - should we log an error if there is more than 1 ticket for this incident?
+my $pd_event = $data->{event}{event_type};
+if ( ( $tickets->Count > 0 ) && ( $pd_event ne 'incident.triggered' ) ) {
+    my $ticket = $tickets->First;
+    my $queue = $config->{queues}{ $ticket->QueueObj->Name } // $config->{queues}{'*'};
+
+    unless ( defined $queue ) {
+        RT->Logger->error( "PagerDuty webhook no queue config found for: " . $ticket->QueueObj->Name );
+        $m->abort(400);
+    }   
+
+    if ( $pd_event eq 'incident.acknowledged' ) {
+        if ( $ticket->LifecycleObj->IsInitial( $ticket->Status ) ) {
+            $ticket->SetStatus( $queue->{acknowledged} // 'open' );
+            $ticket->Comment( Content => 'acknowledged on PagerDuty' );
+        }
+    }
+    elsif ( $pd_event eq 'incident.resolved' ) {
+        my $resolved_status = $queue->{resolved} // 'resolved';
+        if ( $ticket->Status ne $resolved_status ) {
+            $ticket->SetStatus($resolved_status);
+            $ticket->Comment( Content => 'resolved on PagerDuty' );
+        }
+    }
+}
+# if there is not a ticket already we only handle the triggered event
+elsif ( ( $tickets->Count == 0 ) && ( $pd_event eq 'incident.triggered' ) ) {
+    my $queue = $service->{create_queue} // 'General';
+
+    my $Ticket = RT::Ticket->new( RT->SystemUser );
+    my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(
+        Type    => 'ticket',
+        Queue   => $queue,
+        Subject => 'PagerDuty Incident: ' . $data->{event}{data}{service}{summary} . ' - ' .  $data->{event}{data}{title}
+    );
+
+    if ( $id ) {
+        RT::Logger->debug('PagerDuty webhook created new ticket. Setting custom fields: ' . $data->{event}{data}{id} . ' - ' . $data->{event}{data}{html_url});
+        $tickets = RT::Tickets->new( RT->SystemUser );
+        $tickets->LimitId( VALUE => $id );
+        my $ticket = $tickets->First;
+        $ticket->AddCustomFieldValue(
+            Field => 'PagerDuty ID',
+            Value => $data->{event}{data}{id} // ''
+        );
+        $ticket->AddCustomFieldValue(
+            Field => 'PagerDuty URL',
+            Value => $data->{event}{data}{html_url} // ''
+        );
+    }
+    else {
+        # XXX: just log the error?
+        RT::Logger->error("PagerDuty webhook failed to create new ticket: $ErrMsg");
+    }
+}
+</%init>
+% $m->abort;
commit 46d151018a141fa95e20dda1ab3153d420d257b0
Author: Brad Embree <brad at bestpractical.com>
Date:   Wed Feb 16 11:07:04 2022 -0800

    Add RT::Extension::PagerDuty POD

diff --git a/lib/RT/Extension/PagerDuty.pm b/lib/RT/Extension/PagerDuty.pm
new file mode 100644
index 0000000..93baebe
--- /dev/null
+++ b/lib/RT/Extension/PagerDuty.pm
@@ -0,0 +1,180 @@
+use strict;
+use warnings;
+package RT::Extension::PagerDuty;
+
+our $VERSION = '0.01';
+
+=head1 NAME
+
+RT-Extension-PagerDuty - Two way integration with PagerDuty
+
+=head1 DESCRIPTION
+
+This RT extension allows for two way integration with the PagerDuty incident
+response platform.
+
+On ticket creation in RT trigger an incident in PagerDuty. When a ticket is
+acknowledged or resolved in RT update the incident in PagerDuty.
+
+Configure a PagerDuty webhook to push noticications to RT from PagerDuty. When
+a new incident is triggered in PagerDuty have it create a ticket in RT. If an
+incident is acknowledged or resolved in PagerDuty update the corresponding
+ticket in RT.
+
+=head1 RT VERSION
+
+Works with RT 5.
+
+=head1 INSTALLATION
+
+=over
+
+=item C<perl Makefile.PL>
+
+=item C<make>
+
+=item C<make install>
+
+May need root permissions
+
+=item Edit your F</opt/rt5/etc/RT_SiteConfig.pm>
+
+Add this line:
+
+    Plugin('RT::Extension::PagerDuty');
+
+=item Clear your mason cache
+
+    rm -rf /opt/rt5/var/mason_data/obj
+
+=item Restart your webserver
+
+=back
+
+=head1 CONFIGURATION
+
+To define the interactions between RT and PagerDuty use the C<$PagerDuty> config
+option. This option takes the form of:
+
+    Set(
+        $PagerDuty,
+        {
+            services => {
+                'PagerDuty Service ID' => {
+                    api_token => 'PagerDuty API Token',
+                    api_user => 'PagerDuty User',
+                    create_queue => 'General',
+                }
+            },
+            queues => {
+                'RT Queue Name' => {
+                    service => 'PagerDuty Service ID',
+                    acknowledged => 'open',
+                    resolved => 'resolved',
+                }
+            }
+        }
+    );
+
+The services section maps a PagerDuty service id to the token and user to use
+for API access. The C<api_token> and C<api_user> values are required. The
+optional C<create_queue> value is the RT queue name where new RT tickets should
+be created if a PagerDuty incident creates a new RT ticket. C<create_queue>
+defaults to the General queue if not specified. Use '*' as the PagerDuty service
+id to apply to multiple PagerDuty services.
+
+The queues section maps a RT queue name to the PagerDuty service where it should
+trigger new incidents when a RT ticket is created. The C<service> value is
+required and must be a PagerDuty service id. The C<acknowledged> and C<resolved>
+optional values indicate what RT ticket status to use when an incident is
+acknowledged or resolved on PagerDuty. If not specified they default to
+acknowledged => 'open' and resolved => 'resolved'. Use '*' as the RT queue name
+to apply to multiple RT queues.
+
+=head1 Scrips
+
+This extension will install three new Scrips that do not apply to any queues:
+C<On Acknowledge PagerDuty Acknowledge>, C<On Create PagerDuty Trigger>, and
+C<On Resolve PagerDuty Resolve>.
+
+Once you have added the configuration you can apply these Scrips to the queues
+you want to integrate with PagerDuty.
+
+=head1 CUSTOM FIELDS
+
+This extension adds two ticket custom fields: PagerDuty ID and PagerDuty URL.
+
+When a RT ticket creates an incident on PagerDuty or an incident on PagerDuty
+creates a RT ticket the custom fields are automatically filled in. The PagerDuty
+URL links directly to the incident on PagerDuty.
+
+If you would like to group the new custom fields in their own PagerDuty group
+you can use the CustomFieldGroupings config option:
+
+    Set(
+        %CustomFieldGroupings,
+        'RT::Ticket' => [
+            'PagerDuty' => [ 'PagerDuty ID', 'PagerDuty URL' ],
+        ],
+    );
+
+=head1 WEBHOOK USAGE
+
+To call the webhook from PagerDuty:
+
+=over 4
+
+=item 1. Configure your web server to require Basic Authorization for the
+location $WebPath/PagerDuty.  This is I<very important> because this plugin
+does not do any authentication and must be used with HTTP Basic Authorization.
+
+For example:
+
+    <Location /PagerDuty/WebHook.html>
+        AuthType Basic
+        AuthName "PagerDuty WebHook"
+        AuthUserFile "/etc/apache2/pagerdutypass"
+        Require valid-user
+    </Location>
+
+F</etc/apache2/pagerdutypass> could be generated by command C<htpasswd>:
+
+    htpasswd -c /etc/apache2/pagerdutypass pagerduty
+
+=item 2. Go to the PagerDuty Service Integrations Webhooks
+
+=item 3. Configure a webhook, using:  C<https://user:pass@your.rt.example/PagerDuty/WebHook.html>
+where user:pass are the credentials acceptable to the Web server. Currently the
+only event subscriptions supported are incident.acknowledged, incident.resolved,
+and incident.triggered.
+
+=item 4. Ensure a RT user exists with the same user and pass.
+
+=back
+
+=head1 AUTHOR
+
+Best Practical Solutions, LLC E<lt>modules at bestpractical.comE<gt>
+
+=for html <p>All bugs should be reported via email to <a
+href="mailto:bug-RT-Extension-PagerDuty at rt.cpan.org">bug-RT-Extension-PagerDuty at rt.cpan.org</a>
+or via the web at <a
+href="http://rt.cpan.org/Public/Dist/Display.html?Name=RT-Extension-PagerDuty">rt.cpan.org</a>.</p>
+
+=for text
+    All bugs should be reported via email to
+        bug-RT-Extension-PagerDuty at rt.cpan.org
+    or via the web at
+        http://rt.cpan.org/Public/Dist/Display.html?Name=RT-Extension-PagerDuty
+
+=head1 LICENSE AND COPYRIGHT
+
+This software is Copyright (c) 2022 by BPS
+
+This is free software, licensed under:
+
+  The GNU General Public License, Version 2, June 1991
+
+=cut
+
+1;
commit 414564362dcc26e23d59364d41825fd2e4f687fa
Author: Brad Embree <brad at bestpractical.com>
Date:   Tue Feb 15 09:15:23 2022 -0800

    Add Scrips and Custom Fields

diff --git a/etc/initialdata b/etc/initialdata
new file mode 100644
index 0000000..4c63d0c
--- /dev/null
+++ b/etc/initialdata
@@ -0,0 +1,102 @@
+use strict;
+use warnings;
+
+our @ScripActions = (
+    {
+        Name        => 'PagerDuty Trigger',
+        Description => 'Trigger an incident on PagerDuty',
+        ExecModule  => 'UpdatePagerDuty',
+        Argument    => 'trigger',
+    },
+    {
+        Name        => 'PagerDuty Acknowledge',
+        Description => 'Acknowledge an incident on PagerDuty',
+        ExecModule  => 'UpdatePagerDuty',
+        Argument    => 'acknowledge',
+    },
+    {
+        Name        => 'PagerDuty Resolve',
+        Description => 'Resolve an incident on PagerDuty',
+        ExecModule  => 'UpdatePagerDuty',
+        Argument    => 'resolve',
+    },
+);
+
+our @ScripConditions = (
+    {
+        Name        => 'On Acknowledge',
+        Description => 'When a ticket is changed from an initial status',
+        ExecModule  => 'OnAcknowledge',
+        Argument    => '',
+        ApplicableTransTypes => 'Status',
+    },
+);
+
+our @Scrips = (
+    {
+        Queue                  => 0,
+        Description            => 'On Create PagerDuty Trigger',
+        ScripCondition         => 'On Create',
+        ScripAction            => 'PagerDuty Trigger',
+        Template               => 'Blank',
+        CustomPrepareCode      => undef,
+        CustomCommitCode       => undef,
+        CustomIsApplicableCode => undef,
+    },
+    {
+        Queue                  => 0,
+        Description            => 'On Acknowledge PagerDuty Acknowledge',
+        ScripCondition         => 'On Acknowledge',
+        ScripAction            => 'PagerDuty Acknowledge',
+        Template               => 'Blank',
+        CustomPrepareCode      => undef,
+        CustomCommitCode       => undef,
+        CustomIsApplicableCode => undef,
+    },
+    {
+        Queue                  => 0,
+        Description            => 'On Resolve PagerDuty Resolve',
+        ScripCondition         => 'On Resolve',
+        ScripAction            => 'PagerDuty Resolve',
+        Template               => 'Blank',
+        CustomPrepareCode      => undef,
+        CustomCommitCode       => undef,
+        CustomIsApplicableCode => undef,
+    },
+);
+
+our @CustomFields = (
+    {
+        Name => 'PagerDuty ID',
+        Description => 'ID for the related incident on PagerDuty',
+        Type => 'FreeformSingle',
+        LookupType => 'RT::Queue-RT::Ticket',
+    },
+    {
+        Name => 'PagerDuty URL',
+        Description => 'URL to view the incident on PagerDuty',
+        Type => 'FreeformSingle',
+        LookupType => 'RT::Queue-RT::Ticket',
+    },
+);
+
+our @Final = (
+    sub {
+        my $scrips = RT::Scrips->new( RT->SystemUser );
+        $scrips->Limit(
+            FIELD => 'Description',
+            VALUE => 'PagerDuty',
+            OPERATOR => 'LIKE',
+        );
+        while ( my $scrip = $scrips->Next ) {
+            $scrip->RemoveFromObject( ObjectId => 0 );
+        }
+    },
+    sub {
+        my $cf = RT::CustomField->new( RT->SystemUser );
+        $cf->LoadByName( Name => 'PagerDuty URL' );
+        $cf->SetLinkValueTo('__CustomField__ ');
+    },
+);
+
+1;
diff --git a/lib/RT/Action/UpdatePagerDuty.pm b/lib/RT/Action/UpdatePagerDuty.pm
new file mode 100644
index 0000000..89ed03e
--- /dev/null
+++ b/lib/RT/Action/UpdatePagerDuty.pm
@@ -0,0 +1,166 @@
+package RT::Action::UpdatePagerDuty;
+
+use strict;
+use warnings;
+use base 'RT::Action';
+
+use HTTP::Request::Common qw(POST PUT);
+use LWP::UserAgent;
+
+sub Prepare {
+    my $self = shift;
+
+    return 1;
+}
+
+sub Commit {
+    my $self = shift;
+
+    my $config = RT::Config->Get('PagerDuty');
+    unless ( $config ) {
+        RT->Logger->error('PagerDuty config not set');
+        return 0;
+    }
+
+    my $service_id = $config->{queues}{ $self->TicketObj->QueueObj->Name }{service} // $config->{queues}{'*'}{service};
+    unless ( $service_id ) {
+        RT->Logger->error( 'PagerDuty no service id found for queue: ' . $self->TicketObj->QueueObj->Name );
+        return 0;
+    }
+
+    my $service = $config->{services}{$service_id} // $config->{services}{'*'};
+    unless ( $service ) {
+        RT->Logger->error("PagerDuty no service config found for id: $service_id");
+        return 0;
+    }
+
+    my $token = $service->{api_token};
+    my $user  = $service->{api_user};
+    unless ( $token && $user ) {
+        RT->Logger->error("PagerDuty service config missing token or user for service id: $service_id");
+        return 0;
+    }
+
+    my $arg = lc( $self->Argument );
+    if ( $arg eq 'trigger' ) {
+        return $self->_trigger( $service_id, $token, $user );
+    }
+    else {
+        return $self->_update( $arg, $token, $user);
+    }
+}
+
+sub _trigger {
+    my ( $self, $service_id, $token, $user ) = @_;
+
+    my $ua = LWP::UserAgent->new;
+    $ua->timeout(15);
+
+    my $id = $self->TicketObj->id;
+    my $tag = $self->TicketObj->QueueObj->SubjectTag;
+    my $desc = $self->TransactionObj->BriefDescription;
+    my $subject = $self->TransactionObj->ContentObj->Subject;
+    my $content = $self->TransactionObj->ContentObj->Content;
+
+    my %post_content = (
+        incident => {
+            type => "incident",
+            title => "[$tag #$id] $desc $subject",
+            service => {
+                id => $service_id,
+                type => "service_reference"
+            },
+            incident_key => "[$tag #$id]",
+            body => {
+                type => "incident_body",
+                details => $content
+            }
+        }
+    );
+
+    my $post_content = JSON::to_json(\%post_content);
+
+    RT->Logger->debug("PagerDuty POST: $post_content");
+
+    # https://developer.pagerduty.com/api-reference/b3A6Mjc0ODE0MA-create-an-incident
+    my $req = POST(
+        'https://api.pagerduty.com/incidents',
+        'Accept', 'application/vnd.pagerduty+json;version=2',
+        'Authorization', "Token token=$token",
+        'Content-Type', 'application/json',
+        'From', $user,
+        CONTENT => $post_content
+    );
+
+    my $resp = $ua->request($req);
+
+    RT->Logger->debug( 'PagerDuty got response: '  . $resp->status_line . ' ' . $resp->decoded_content() );
+
+    # update PagerDuty custom fields
+    # XXX - allow arbitrary PagerDuty custom fields and parse name for /PagerDuty\s*($field_name)/
+    #       where $field_name matches a field returned from the API
+    #       would want to make field names "prettier" so maybe map _ => - and uppercase first letter of each word?
+    my $return = JSON::from_json( $resp->decoded_content );
+    $self->TicketObj->AddCustomFieldValue(
+        Field => 'PagerDuty ID',
+        Value => $return->{incident}->{id} // ''
+    );
+    $self->TicketObj->AddCustomFieldValue(
+        Field => 'PagerDuty URL',
+        Value => $return->{incident}->{html_url} // ''
+    );
+
+    return 1;
+}
+
+sub _update {
+    my ( $self, $status, $token, $user ) = @_;
+
+    $status //= '';
+
+    # try to allow some flexibility in the status parameter
+    if ( $status =~ /^resolve/i ) {
+        $status = 'resolved';
+    }
+    elsif ( $status =~ /^ack/i ) {
+        $status = 'acknowledged';
+    }
+
+    my $ua = LWP::UserAgent->new;
+    $ua->timeout(15);
+
+    my $pd_id = $self->TicketObj->CustomFieldValuesAsString('PagerDuty ID');
+
+    # XXX - should it fail here? any way to show an error to user from here?
+    #       or do we just assume no pd id means no incident to worry about?
+    return 1 unless $pd_id;
+
+    my %content = (
+        incident => {
+            type => "incident",
+            status => $status
+        }
+    );
+
+    my $content = JSON::to_json(\%content);
+
+    RT->Logger->debug("PagerDuty PUT: $content");
+
+    # https://developer.pagerduty.com/api-reference/b3A6Mjc0ODE0Mg-update-an-incident
+    my $req = PUT(
+        'https://api.pagerduty.com/incidents/' . $pd_id,
+        'Accept', 'application/vnd.pagerduty+json;version=2',
+        'Authorization', "Token token=$token",
+        'Content-Type', 'application/json',
+        'From', $user,
+        CONTENT => $content
+    );
+
+    my $resp = $ua->request($req);
+
+    RT->Logger->debug( 'PagerDuty got response: '  . $resp->status_line . ' ' . $resp->decoded_content() );
+
+    return 1;
+}
+
+1;
diff --git a/lib/RT/Condition/OnAcknowledge.pm b/lib/RT/Condition/OnAcknowledge.pm
new file mode 100644
index 0000000..99db851
--- /dev/null
+++ b/lib/RT/Condition/OnAcknowledge.pm
@@ -0,0 +1,18 @@
+package RT::Condition::OnAcknowledge;
+
+use strict;
+use warnings;
+use base 'RT::Condition';
+
+sub IsApplicable {
+    my $self = shift;
+
+    my $txn = $self->TransactionObj;
+
+    # only applicable if status was changed from initial status
+    return 0 unless $self->TicketObj->LifecycleObj->IsInitial( $txn->OldValue );  
+
+    return 1;
+}
+
+1;
-----------------------------------------------------------------------

Summary of changes:
 etc/initialdata               | 43 +++++++++++++++++++++++++++----------------
 lib/RT/Extension/PagerDuty.pm | 27 ++++++++++++++++++---------
 2 files changed, 45 insertions(+), 25 deletions(-)


hooks/post-receive
-- 
rt-extension-pagerduty


More information about the Bps-public-commit mailing list