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

BPS Git Server git at git.bestpractical.com
Thu Mar 10 16:52:24 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  bf6132aa64ae780645abb4462ee4047ce4be7b39 (commit)
  discards  20b50dac55af846da4007a2009f83ace3e23111f (commit)
  discards  46d151018a141fa95e20dda1ab3153d420d257b0 (commit)
  discards  414564362dcc26e23d59364d41825fd2e4f687fa (commit)
       via  0826025822cf2a26d378ea110adb2b34b7669c89 (commit)
       via  598d2074042255a0afd1ac42db2904fb51b0873f (commit)
       via  1be5c5e17db207de6db9afee8cdcfc6ed4ad884e (commit)
       via  e7b0d4bac97bc9ed7f2dd17f2b16f596ce8b4308 (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 (bf6132aa64ae780645abb4462ee4047ce4be7b39)
            \
             N -- N -- N (0826025822cf2a26d378ea110adb2b34b7669c89)

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 0826025822cf2a26d378ea110adb2b34b7669c89
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..c259b47
--- /dev/null
+++ b/README
@@ -0,0 +1,151 @@
+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
+
+    make initdb
+        Only run this the first time you install this module.
+
+        If you run this twice, you may end up with duplicate data in your
+        database.
+
+        If you are upgrading this module, check for upgrading instructions
+        in case changes need to be made to your database.
+
+    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 an RT queue name to the PagerDuty service where
+    it should trigger new incidents when an 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.
+
+Scrips
+    This extension will install three new Scrips that do not apply to any
+    queues: On Acknowledge PagerDuty Acknowledge, On Create PagerDuty
+    Trigger, and On Resolve PagerDuty Resolve.
+
+    Once you have added the configuration you can apply these Scrips to the
+    queues you want to integrate with PagerDuty.
+
+CUSTOM FIELDS
+    This extension adds two ticket custom fields: PagerDuty ID and PagerDuty
+    URL.
+
+    When an RT ticket creates an incident on PagerDuty or an incident on
+    PagerDuty creates an 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 an 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 598d2074042255a0afd1ac42db2904fb51b0873f
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..c93eb92
--- /dev/null
+++ b/html/PagerDuty/WebHook.html
@@ -0,0 +1,117 @@
+<%init>
+use JSON;
+use Data::Dumper;
+
+if ( !$ARGS{'POSTDATA'} ) {
+    RT->Logger->error('PagerDuty webhook failed no 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 ($@) {
+    RT->Logger->error("PagerDuty webhook failed to decode json data: $@");
+    $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' ) {
+    RT->Logger->debug('PagerDuty webhook ignoring non incident event');
+    $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);
+}
+
+my $user_obj = $session{CurrentUser};
+unless ($user_obj) {
+    RT->Logger->error('PagerDuty webhook failed no current user');
+    $m->abort(401);
+}
+
+# need to see if a ticket already exists for this incident
+my $tickets = RT::Tickets->new($user_obj);
+$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($user_obj);
+    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($user_obj);
+        $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 1be5c5e17db207de6db9afee8cdcfc6ed4ad884e
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..952ac9a
--- /dev/null
+++ b/lib/RT/Extension/PagerDuty.pm
@@ -0,0 +1,177 @@
+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 C<make initdb>
+
+Only run this the first time you install this module.
+
+If you run this twice, you may end up with duplicate data
+in your database.
+
+If you are upgrading this module, check for upgrading instructions
+in case changes need to be made to your database.
+
+=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 an RT queue name to the PagerDuty service where it should
+trigger new incidents when an 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 an RT ticket creates an incident on PagerDuty or an incident on PagerDuty
+creates an 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. Create an auth token for a user with permissions to create tickets in
+the PagerDuty create queue. To create an auth token go to
+Logged in as -> Settings -> Auth Tokens and create a new token.
+
+=item 2. Go to the PagerDuty Service Integrations Webhooks
+
+=item 3. Add a new webhook, using: C<https://your.rt.example/PagerDuty/WebHook.html>
+as the webhook URL. Add a custom header with the name Authorization and value
+'token #-#-abc123' where '#-#-abc123' is the value for the auth token you
+created in step one. Currently the only event subscriptions supported are
+incident.acknowledged, incident.resolved, and incident.triggered.
+
+=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 e7b0d4bac97bc9ed7f2dd17f2b16f596ce8b4308
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..282fcb3
--- /dev/null
+++ b/etc/initialdata
@@ -0,0 +1,68 @@
+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 = (
+    {   NoAutoGlobal   => 1,
+        Description    => 'On Create PagerDuty Trigger',
+        ScripCondition => 'On Create',
+        ScripAction    => 'PagerDuty Trigger',
+        Template       => 'Blank',
+    },
+    {   NoAutoGlobal   => 1,
+        Description    => 'On Acknowledge PagerDuty Acknowledge',
+        ScripCondition => 'On Acknowledge',
+        ScripAction    => 'PagerDuty Acknowledge',
+        Template       => 'Blank',
+    },
+    {   NoAutoGlobal   => 1,
+        Description    => 'On Resolve PagerDuty Resolve',
+        ScripCondition => 'On Resolve',
+        ScripAction    => 'PagerDuty Resolve',
+        Template       => 'Blank',
+    },
+);
+
+our @CustomFields = (
+    {   Name        => 'PagerDuty ID',
+        Description => 'ID for the related incident on PagerDuty',
+        Type        => 'FreeformSingle',
+        LookupType  => 'RT::Queue-RT::Ticket',
+        ApplyTo     => [],
+    },
+    {   Name        => 'PagerDuty URL',
+        Description => 'URL to view the incident on PagerDuty',
+        Type        => 'FreeformSingle',
+        LookupType  => 'RT::Queue-RT::Ticket',
+        ApplyTo     => [],
+        LinkValueTo => '__CustomField__ ',
+    },
+);
+
+1;
diff --git a/lib/RT/Action/UpdatePagerDuty.pm b/lib/RT/Action/UpdatePagerDuty.pm
new file mode 100644
index 0000000..b069aaa
--- /dev/null
+++ b/lib/RT/Action/UpdatePagerDuty.pm
@@ -0,0 +1,204 @@
+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 ) = @_;
+
+    if ( $self->TicketObj->Subject =~ /^PagerDuty Incident:/ ) {
+
+        # this ticket was created by PagerDuty webhook
+        # so do not need to create incident
+        RT->Logger->debug('ticket created by PagerDuty webhook');
+        return 1;
+    }
+
+    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 $content_obj = $self->TransactionObj->ContentObj;
+    my $subject     = $content_obj ? $content_obj->Subject : '';
+    my $content     = $content_obj ? $content_obj->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() );
+
+    unless ( $resp->is_success ) {
+        RT->Logger->error(
+            'PagerDuty request failed: ' . $resp->status_line );
+        return 0;
+    }
+
+# 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() );
+
+    unless ( $resp->is_success ) {
+        RT->Logger->error(
+            'PagerDuty request failed: ' . $resp->status_line );
+        return 0;
+    }
+
+    return 1;
+}
+
+1;
diff --git a/lib/RT/Condition/OnAcknowledge.pm b/lib/RT/Condition/OnAcknowledge.pm
new file mode 100644
index 0000000..0e1eb88
--- /dev/null
+++ b/lib/RT/Condition/OnAcknowledge.pm
@@ -0,0 +1,19 @@
+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:
 README                            |  56 +++++++++++------
 etc/initialdata                   |  90 +++++++++-------------------
 html/PagerDuty/WebHook.html       |  67 +++++++++++++--------
 lib/RT/Action/UpdatePagerDuty.pm  | 122 +++++++++++++++++++++++++-------------
 lib/RT/Condition/OnAcknowledge.pm |   3 +-
 lib/RT/Extension/PagerDuty.pm     |  49 +++++++--------
 6 files changed, 211 insertions(+), 176 deletions(-)


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


More information about the Bps-public-commit mailing list