You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@openwhisk.apache.org by GitBox <gi...@apache.org> on 2018/02/19 15:48:15 UTC

[GitHub] csantanapr closed pull request #128: Allow delete of trigger and rules after firing alarms once action

csantanapr closed pull request #128: Allow delete of trigger and rules after firing alarms once action
URL: https://github.com/apache/incubator-openwhisk-package-alarms/pull/128
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/README.md b/README.md
index 53c88aa..466feaf 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ The package includes the following feeds.
 | --- | --- | --- | --- |
 | `/whisk.system/alarms` | package | - | Alarms and periodic utility. |
 | `/whisk.system/alarms/interval` | feed | minutes, trigger_payload, startDate, stopDate | Fire Trigger event on an interval based schedule. |
-| `/whisk.system/alarms/once` | feed | date, trigger_payload | Fire Trigger event once on a specific date. |
+| `/whisk.system/alarms/once` | feed | date, trigger_payload, deleteAfterFire | Fire Trigger event once on a specific date. |
 | `/whisk.system/alarms/alarm` | feed | cron, trigger_payload, startDate, stopDate | Fire Trigger event on a time-based schedule using cron. |
 
 
@@ -19,17 +19,17 @@ The package includes the following feeds.
 
 The `/whisk.system/alarms/interval` feed configures the Alarm service to fire a Trigger event on an interval based schedule. The parameters are as follows:
 
-- `minutes`: An integer representing the length of the interval (in minutes) between trigger fires.
+- `minutes` (*required*): An integer representing the length of the interval (in minutes) between trigger fires.
 
-- `trigger_payload`: The value of this parameter becomes the content of the Trigger every time the Trigger is fired.
+- `trigger_payload` (*optional*): The value of this parameter becomes the content of the Trigger every time the Trigger is fired.
 
-- `startDate`: The date when the first trigger will be fired.  Subsequent fires will occur based on the interval length specified by the `minutes` parameter.   
+- `startDate` (*optional*): The date when the first trigger will be fired.  Subsequent fires will occur based on the interval length specified by the `minutes` parameter.   
 
-- `stopDate`: The date when the trigger will stop running.  Triggers will no longer be fired once this date has been reached.
+- `stopDate` (*optional*): The date when the Trigger will stop running.  Triggers will no longer be fired once this date has been reached.
 
   **Note**: The `startDate` and `stopDate` parameters support an integer or string value.  The integer value represents the number of milliseconds since 1 January 1970 00:00:00 UTC and the string value should be in the ISO 8601 format (http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15).
 
-The following example creates a trigger that is fired once every 2 minutes. The trigger fires as soon as possible, and will stop firing January 31, 2019, 23:59:00 UTC.
+The following example creates a trigger that is fired once every 2 minutes. The Trigger fires as soon as possible, and will stop firing January 31, 2019, 23:59:00 UTC.
 
   ```
   wsk trigger create interval \
@@ -45,27 +45,33 @@ Each generated event includes parameters, which are the properties that are spec
 
 The `/whisk.system/alarms/once` feed configures the Alarm service to fire a trigger event on a specified date. The parameters are as follows:
 
-- `date`: The date when the trigger will be fired.  The trigger will be fired just once at the given time. 
+- `date` (*required*): The date when the Trigger will be fired.  The Trigger will be fired just once at the given time. 
 
   **Note**: The `date` parameter supports an integer or string value.  The integer value represents the number of milliseconds 
   since 1 January 1970 00:00:00 UTC and the string value should be in the ISO 8601 format (http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15).
 
-- `trigger_payload`: The value of this parameter becomes the content of the Trigger when the Trigger is fired. 
+- `trigger_payload` (*optional*): The value of this parameter becomes the content of the Trigger when the Trigger is fired. 
 
-The following is an example of creating a trigger that will be fired once on December 25, 2017, 12:30:00 UTC.
+- `deleteAfterFire` (*optional*, default: false): The value of this parameter determines whether the Trigger and potentially all of its associated rules will be deleted after the Trigger is fired.  
+  - `false`: No action will be taken after the Trigger fires. 
+  - `true`: The Trigger will be deleted after it fires. 
+  - `rules`: The Trigger and all of its associated rules will be deleted after it fires.
+
+The following is an example of creating a trigger that will be fired once on December 25, 2019, 12:30:00 UTC.  After the Trigger fires it will be deleted as well as all of its associated rules.  
 
   ```
   wsk trigger create fireOnce \
     --feed /whisk.system/alarms/once \
     --param trigger_payload "{\"name\":\"Odin\",\"place\":\"Asgard\"}" \
-    --param date "2017-12-25T12:30:00.000Z"
+    --param date "2019-12-25T12:30:00.000Z" \
+    --param deleteAfterFire "rules"
   ``` 
 
 ## Firing a Trigger on a time-based schedule using cron
 
 The `/whisk.system/alarms/alarm` feed configures the Alarm service to fire a Trigger event at a specified frequency. The parameters are as follows:
 
-- `cron`: A string, based on the UNIX crontab syntax that indicates when to fire the Trigger in Coordinated Universal Time (UTC). The string is a sequence of five fields that are separated by spaces: `X X X X X`.
+- `cron` (*required*): A string, based on the UNIX crontab syntax that indicates when to fire the Trigger in Coordinated Universal Time (UTC). The string is a sequence of five fields that are separated by spaces: `X X X X X`.
 For more information, see: http://crontab.org. The following strings are examples that use varying duration's of frequency.
 
   - `* * * * *`: The Trigger fires at the top of every minute.
@@ -78,15 +84,15 @@ For more information, see: http://crontab.org. The following strings are example
   Here is an example using six fields notation:
     - `*/30 * * * * *`: every thirty seconds.
     
-- `trigger_payload`: The value of this parameter becomes the content of the Trigger every time the Trigger is fired.
+- `trigger_payload` (*optional*): The value of this parameter becomes the content of the Trigger every time the Trigger is fired.
 
-- `startDate`: The date when the Trigger will start running. The Trigger fires based on the schedule specified by the cron parameter.  
+- `startDate` (*optional*): The date when the Trigger will start running. The Trigger fires based on the schedule specified by the cron parameter.  
 
-- `stopDate`: The date when the Trigger will stop running. Triggers are no longer fired once this date is reached.
+- `stopDate` (*optional*): The date when the Trigger will stop running. Triggers are no longer fired once this date is reached.
 
   **Note**: The `startDate` and `stopDate` parameters support an integer or string value.  The integer value represents the number of milliseconds since 1 January 1970 00:00:00 UTC, and the string value should be in the ISO 8601 format (http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15).
 
-The following is an example of creating a trigger that fires once every 2 minutes with `name` and `place` values in the trigger event.  The trigger will not start firing until
+The following is an example of creating a trigger that fires once every 2 minutes with `name` and `place` values in the trigger event.  The Trigger will not start firing until
 January 1, 2019, 00:00:00 UTC and will stop firing January 31, 2019, 23:59:00 UTC.
 
   ```
diff --git a/action/alarmWebAction.js b/action/alarmWebAction.js
index b5b5bde..478af06 100644
--- a/action/alarmWebAction.js
+++ b/action/alarmWebAction.js
@@ -18,6 +18,7 @@ function main(params) {
     var triggerURL = `https://${params.apihost}/api/v1/namespaces/${triggerParts.namespace}/triggers/${triggerParts.name}`;
 
     var workers = params.workers instanceof Array ? params.workers : [];
+    var deleteAfterFireArray = ['false', 'true', 'rules'];
     var db;
 
     if (params.__ow_method === "post") {
@@ -47,6 +48,14 @@ function main(params) {
                 return common.sendError(400, date);
             }
             newTrigger.date = date;
+
+            if (params.deleteAfterFire) {
+                var deleteAfterFire = ('' + params.deleteAfterFire).trim().toLowerCase();
+                if (deleteAfterFireArray.indexOf(deleteAfterFire) === -1) {
+                    return common.sendError(400, 'deleteAfterFire parameter must be one of [false, true, rules].');
+                }
+                newTrigger.deleteAfterFire = deleteAfterFire;
+            }
         }
         else {
             var cronHandle;
@@ -213,6 +222,14 @@ function main(params) {
                         }
                         updatedParams.date = date;
                     }
+
+                    if (params.deleteAfterFire) {
+                        var deleteAfterFire = ('' + params.deleteAfterFire).trim().toLowerCase();
+                        if (deleteAfterFireArray.indexOf(deleteAfterFire) === -1) {
+                            return common.sendError(400, 'deleteAfterFire parameter must be one of [false, true, rules].');
+                        }
+                        newTrigger.deleteAfterFire = deleteAfterFire;
+                    }
                 }
                 else {
                     if (trigger.minutes) {
@@ -344,11 +361,7 @@ function hasSecondsGranularity(cron) {
 
     var fields = (cron + '').trim().split(/\s+/);
 
-    if (fields.length > 5 && fields[fields.length - 6] !== '0') {
-        return true;
-    }
-
-    return false;
+    return fields.length > 5 && fields[fields.length - 6] !== '0';
 }
 
 exports.main = main;
diff --git a/action/lib/Database.js b/action/lib/Database.js
index fcf062b..236f2f5 100644
--- a/action/lib/Database.js
+++ b/action/lib/Database.js
@@ -63,12 +63,12 @@ module.exports = function(dbURL, dbName) {
 
         return new Promise(function(resolve, reject) {
 
-            utilsDB.db.get(triggerID, function (err, existing) {
+            var qName = triggerID.split('/');
+            var id = retry ? triggerID : qName[0] + '/_/' + qName[2];
+            utilsDB.db.get(id, function (err, existing) {
                 if (err) {
                     if (retry) {
-                        var parts = triggerID.split('/');
-                        var id = parts[0] + '/_/' + parts[2];
-                        utilsDB.getTrigger(id, false)
+                        utilsDB.getTrigger(triggerID, false)
                         .then(doc => {
                             resolve(doc);
                         })
@@ -76,7 +76,8 @@ module.exports = function(dbURL, dbName) {
                             reject(err);
                         });
                     } else {
-                        reject(common.sendError(err.statusCode, 'could not find the trigger in the database'));
+                        var name = '/' + qName[1] + '/' + qName[2];
+                        reject(common.sendError(err.statusCode, 'could not find trigger ' + name + ' in the database'));
                     }
                 } else {
                     resolve(existing);
@@ -156,7 +157,9 @@ module.exports = function(dbURL, dbName) {
                     });
                 }
                 else {
-                    reject(common.sendError(err.statusCode, 'could not find the trigger in the database'));
+                    var qName = triggerID.split('/');
+                    var name = '/' + qName[1] + '/' + qName[2];
+                    reject(common.sendError(err.statusCode, 'could not find trigger ' + name + ' in the database'));
                 }
             });
         });
diff --git a/gradle/docker.gradle b/gradle/docker.gradle
index 8192d3a..c74ba30 100644
--- a/gradle/docker.gradle
+++ b/gradle/docker.gradle
@@ -37,16 +37,21 @@ if(project.hasProperty('dockerHost')) {
 }
 
 if(project.hasProperty('dockerBuildArgs')) {
-    dockerBuildArg += ['--build-arg', project.dockerBuildArgs]
+    dockerBuildArgs.each { arg  ->
+        dockerBuildArg += ['--build-arg', arg]
+    }
 }
 
-task distDocker << {
+task distDocker {
+    doLast {
     def start = new Date()
     def cmd = dockerBinary + dockerBuildArg + ['-t', dockerImageName, project.buildscript.sourceFile.getParentFile().getAbsolutePath()]
     retry(cmd, dockerRetries, dockerTimeout)
     println("Building '${dockerImageName}' took ${TimeCategory.minus(new Date(), start)}")
 }
-task tagImage << {
+}
+task tagImage {
+    doLast {
     def versionString = (dockerBinary + ['-v']).execute().text
     def matched = (versionString =~ /(\d+)\.(\d+)\.(\d+)/)
 
@@ -59,11 +64,14 @@ task tagImage << {
     }
     retry(dockerBinary + dockerCmd + [dockerImageName, dockerTaggedImageName], dockerRetries, dockerTimeout)
 }
+}
 
-task pushImage << {
+task pushImage {
+    doLast {
     def cmd = dockerBinary + ['push', dockerTaggedImageName]
     retry(cmd, dockerRetries, dockerTimeout)
 }
+}
 pushImage.dependsOn tagImage
 pushImage.onlyIf { dockerRegistry != '' }
 distDocker.finalizedBy pushImage
diff --git a/installCatalog.sh b/installCatalog.sh
index 85f6467..9261716 100755
--- a/installCatalog.sh
+++ b/installCatalog.sh
@@ -71,7 +71,7 @@ $WSK_CLI -i --apihost "$EDGEHOST" action update --kind nodejs:6 --auth "$AUTH" a
 
 $WSK_CLI -i --apihost "$EDGEHOST" action update --kind nodejs:6 --auth "$AUTH" alarms/once "$PACKAGE_HOME/action/alarmFeed.zip" \
      -a description 'Fire trigger once when alarm occurs' \
-     -a parameters '[ {"name":"date", "required":true} ]' \
+     -a parameters '[ {"name":"date", "required":true}, {"name":"deleteAfterFire", "required":false} ]' \
      -a feed true \
      -p fireOnce true
 
diff --git a/provider/lib/cronAlarm.js b/provider/lib/cronAlarm.js
index f60ec62..ff89d39 100644
--- a/provider/lib/cronAlarm.js
+++ b/provider/lib/cronAlarm.js
@@ -11,7 +11,9 @@ module.exports = function(logger, newTrigger) {
         name: newTrigger.name,
         namespace: newTrigger.namespace,
         payload: newTrigger.payload,
-        cron: newTrigger.cron
+        cron: newTrigger.cron,
+        triggerID: newTrigger.triggerID,
+        uri: newTrigger.uri
     };
 
     this.scheduleAlarm = function(triggerIdentifier, callback) {
diff --git a/provider/lib/dateAlarm.js b/provider/lib/dateAlarm.js
index 011261d..f505ca2 100644
--- a/provider/lib/dateAlarm.js
+++ b/provider/lib/dateAlarm.js
@@ -7,7 +7,10 @@ module.exports = function(logger, newTrigger) {
         name: newTrigger.name,
         namespace: newTrigger.namespace,
         payload: newTrigger.payload,
-        date: newTrigger.date
+        date: newTrigger.date,
+        deleteAfterFire: newTrigger.deleteAfterFire,
+        triggerID: newTrigger.triggerID,
+        uri: newTrigger.uri
     };
 
     this.scheduleAlarm = function(triggerIdentifier, callback) {
diff --git a/provider/lib/intervalAlarm.js b/provider/lib/intervalAlarm.js
index 077a68b..39d1a78 100644
--- a/provider/lib/intervalAlarm.js
+++ b/provider/lib/intervalAlarm.js
@@ -8,7 +8,9 @@ module.exports = function(logger, newTrigger) {
         name: newTrigger.name,
         namespace: newTrigger.namespace,
         payload: newTrigger.payload,
-        minutes: newTrigger.minutes
+        minutes: newTrigger.minutes,
+        triggerID: newTrigger.triggerID,
+        uri: newTrigger.uri
     };
 
     this.scheduleAlarm = function(triggerIdentifier, callback) {
diff --git a/provider/lib/sanitizer.js b/provider/lib/sanitizer.js
new file mode 100644
index 0000000..19bb747
--- /dev/null
+++ b/provider/lib/sanitizer.js
@@ -0,0 +1,143 @@
+var request = require('request');
+
+module.exports = function(logger, triggerDB, uriHost) {
+
+    var sanitizer = this;
+
+    this.deleteTriggerFromDB = function(triggerID, retryCount) {
+        var method = 'deleteTriggerFromDB';
+
+        //delete from database
+        triggerDB.get(triggerID, function (err, existing) {
+            if (!err) {
+                triggerDB.destroy(existing._id, existing._rev, function (err) {
+                    if (err) {
+                        if (err.statusCode === 409 && retryCount < 5) {
+                            setTimeout(function () {
+                                sanitizer.deleteTriggerFromDB(triggerID, (retryCount + 1));
+                            }, 1000);
+                        }
+                        else {
+                            logger.error(method, triggerID, 'there was an error deleting the trigger from the database');
+                        }
+                    }
+                });
+            }
+            else {
+                logger.error(method, triggerID, 'could not find the trigger in the database');
+            }
+        });
+    };
+
+    this.deleteTriggerAndRules = function(dataTrigger) {
+        var method = 'deleteTriggerAndRules';
+
+        var triggerIdentifier = dataTrigger.triggerID;
+        var auth = dataTrigger.apikey.split(':');
+
+        request({
+            method: 'get',
+            uri: dataTrigger.uri,
+            auth: {
+                user: auth[0],
+                pass: auth[1]
+            },
+        }, function(error, response, body) {
+            logger.info(method, triggerIdentifier, 'http get request, STATUS:', response ? response.statusCode : undefined);
+
+            if (error || response.statusCode >= 400) {
+                logger.error(method, triggerIdentifier, 'trigger get request failed');
+            }
+            else {
+                //delete the trigger
+                sanitizer.deleteTrigger(dataTrigger, auth, 0)
+                .then((info) => {
+                    logger.info(method, triggerIdentifier, info);
+                    if (body) {
+                        try {
+                            var jsonBody = JSON.parse(body);
+                            for (var rule in jsonBody.rules) {
+                                var qualifiedName = rule.split('/');
+                                var uri = uriHost + '/api/v1/namespaces/' + qualifiedName[0] + '/rules/' + qualifiedName[1];
+                                sanitizer.deleteRule(rule, uri, auth, 0);
+                            }
+                        }
+                        catch (err) {
+                            logger.error(method, triggerIdentifier, err);
+                        }
+                    }
+                })
+                .catch(err => {
+                    logger.error(method, triggerIdentifier, err);
+                });
+            }
+        });
+    };
+
+    this.deleteTrigger = function(dataTrigger, auth, retryCount) {
+        var method = 'deleteTrigger';
+
+        return new Promise(function(resolve, reject) {
+
+            var triggerIdentifier = dataTrigger.triggerID;
+            request({
+                method: 'delete',
+                uri: dataTrigger.uri,
+                auth: {
+                    user: auth[0],
+                    pass: auth[1]
+                },
+            }, function (error, response) {
+                logger.info(method, triggerIdentifier, 'http delete request, STATUS:', response ? response.statusCode : undefined);
+                if (error || response.statusCode >= 400) {
+                    if (!error && response.statusCode === 409 && retryCount < 5) {
+                        logger.info(method, 'attempting to delete trigger again', triggerIdentifier, 'Retry Count:', (retryCount + 1));
+                        setTimeout(function () {
+                            sanitizer.deleteTrigger(dataTrigger, auth, (retryCount + 1))
+                            .then(info => {
+                                resolve(info);
+                            })
+                            .catch(err => {
+                                reject(err);
+                            });
+                        }, 1000);
+                    } else {
+                        reject('trigger delete request failed');
+                    }
+                }
+                else {
+                    resolve('trigger delete request was successful');
+                }
+            });
+        });
+    };
+
+    this.deleteRule = function(rule, uri, auth, retryCount) {
+        var method = 'deleteRule';
+
+        request({
+            method: 'delete',
+            uri: uri,
+            auth: {
+                user: auth[0],
+                pass: auth[1]
+            },
+        }, function(error, response) {
+            logger.info(method, rule, 'http delete rule request, STATUS:', response ? response.statusCode : undefined);
+            if (error || response.statusCode >= 400) {
+                if (!error && response.statusCode === 409 && retryCount < 5) {
+                    logger.info(method, 'attempting to delete rule again', rule, 'Retry Count:', (retryCount + 1));
+                    setTimeout(function () {
+                        sanitizer.deleteRule(rule, uri, auth, (retryCount + 1));
+                    }, 1000);
+                } else {
+                    logger.error(method, rule, 'rule delete request failed');
+                }
+            }
+            else {
+                logger.info(method, rule, 'rule delete request was successful');
+            }
+        });
+    };
+
+};
diff --git a/provider/lib/utils.js b/provider/lib/utils.js
index a8f3153..6402b85 100644
--- a/provider/lib/utils.js
+++ b/provider/lib/utils.js
@@ -5,12 +5,10 @@ var constants = require('./constants.js');
 var DateAlarm = require('./dateAlarm.js');
 var CronAlarm = require('./cronAlarm.js');
 var IntervalAlarm = require('./intervalAlarm.js');
+var Sanitizer = require('./sanitizer');
+
+module.exports = function(logger, triggerDB, redisClient) {
 
-module.exports = function(
-  logger,
-  triggerDB,
-  redisClient
-) {
     this.module = 'utils';
     this.triggers = {};
     this.endpointAuth = process.env.ENDPOINT_AUTH;
@@ -22,6 +20,8 @@ module.exports = function(
     this.redisClient = redisClient;
     this.redisHash = triggerDB.config.db + '_' + this.worker;
     this.redisKey = constants.REDIS_KEY;
+    this.uriHost ='https://' + this.routerHost + ':443';
+    this.sanitizer = new Sanitizer(logger, triggerDB, this.uriHost);
 
     var retryDelay = constants.RETRY_DELAY;
     var retryAttempts = constants.RETRY_ATTEMPTS;
@@ -46,6 +46,9 @@ module.exports = function(
             }
         };
 
+        newTrigger.uri = utils.uriHost + '/api/v1/namespaces/' + newTrigger.namespace + '/triggers/' + newTrigger.name;
+        newTrigger.triggerID = triggerIdentifier;
+
         var alarm;
         if (newTrigger.date) {
             alarm = new DateAlarm(logger, newTrigger);
@@ -63,24 +66,22 @@ module.exports = function(
     this.fireTrigger = function(dataTrigger) {
         var method = 'fireTrigger';
 
-        var triggerIdentifier = utils.getTriggerIdentifier(dataTrigger.apikey, dataTrigger.namespace, dataTrigger.name);
-        var host = 'https://' + utils.routerHost + ':443';
+        var triggerIdentifier = dataTrigger.triggerID;
         var auth = dataTrigger.apikey.split(':');
-        var uri = host + '/api/v1/namespaces/' + dataTrigger.namespace + '/triggers/' + dataTrigger.name;
 
         logger.info(method, 'Alarm fired for', triggerIdentifier, 'attempting to fire trigger');
-        utils.postTrigger(dataTrigger, uri, auth, 0)
+        utils.postTrigger(dataTrigger, auth, 0)
         .then(triggerId => {
             logger.info(method, 'Trigger', triggerId, 'was successfully fired');
-            utils.disableExtinctTriggers(triggerIdentifier, dataTrigger);
+            utils.handleExpiredTriggers(dataTrigger);
         })
         .catch(err => {
             logger.error(method, err);
-            utils.disableExtinctTriggers(triggerIdentifier, dataTrigger);
+            utils.handleExpiredTriggers(dataTrigger);
         });
     };
 
-    this.postTrigger = function(dataTrigger, uri, auth, retryCount) {
+    this.postTrigger = function(dataTrigger, auth, retryCount) {
         var method = 'postTrigger';
 
         return new Promise(function(resolve, reject) {
@@ -92,7 +93,7 @@ module.exports = function(
 
             request({
                 method: 'post',
-                uri: uri,
+                uri: dataTrigger.uri,
                 auth: {
                     user: auth[0],
                     pass: auth[1]
@@ -100,8 +101,8 @@ module.exports = function(
                 json: dataTrigger.payload
             }, function(error, response) {
                 try {
-                    var triggerIdentifier = utils.getTriggerIdentifier(dataTrigger.apikey, dataTrigger.namespace, dataTrigger.name);
-                    logger.info(method, triggerIdentifier, 'http post request, STATUS:', response ? response.statusCode : response);
+                    var triggerIdentifier = dataTrigger.triggerID;
+                    logger.info(method, triggerIdentifier, 'http post request, STATUS:', response ? response.statusCode : undefined);
 
                     if (error || response.statusCode >= 400) {
                         // only manage trigger fires if they are not infinite
@@ -119,7 +120,7 @@ module.exports = function(
                             if (retryCount < retryAttempts) {
                                 logger.info(method, 'attempting to fire trigger again', triggerIdentifier, 'Retry Count:', (retryCount + 1));
                                 setTimeout(function () {
-                                    utils.postTrigger(dataTrigger, uri, auth, (retryCount + 1))
+                                    utils.postTrigger(dataTrigger, auth, (retryCount + 1))
                                     .then(triggerId => {
                                         resolve(triggerId);
                                     })
@@ -148,12 +149,36 @@ module.exports = function(
             [HttpStatus.REQUEST_TIMEOUT, HttpStatus.TOO_MANY_REQUESTS].indexOf(statusCode) === -1);
     };
 
-    this.disableExtinctTriggers = function(triggerIdentifier, dataTrigger) {
-        var method = 'disableExtinctTriggers';
+    this.handleExpiredTriggers = function(dataTrigger) {
+        var method = 'handleExpiredTriggers';
 
+        var triggerIdentifier = dataTrigger.triggerID;
         if (dataTrigger.date) {
-            utils.disableTrigger(triggerIdentifier, undefined, 'Automatically disabled after firing once');
-            logger.info(method, 'the fire once date has expired, disabled', triggerIdentifier);
+            if (dataTrigger.deleteAfterFire && dataTrigger.deleteAfterFire !== 'false') {
+                utils.stopTrigger(triggerIdentifier);
+
+                //delete trigger feed from database
+                utils.sanitizer.deleteTriggerFromDB(triggerIdentifier, 0);
+
+                //check if trigger and all associated rules should be deleted
+                if (dataTrigger.deleteAfterFire === 'rules') {
+                    utils.sanitizer.deleteTriggerAndRules(dataTrigger);
+                }
+                else {
+                    var auth = dataTrigger.apikey.split(':');
+                    utils.sanitizer.deleteTrigger(dataTrigger, auth, 0)
+                    .then((info) => {
+                        logger.info(method, triggerIdentifier, info);
+                    })
+                    .catch(err => {
+                        logger.error(method, triggerIdentifier, err);
+                    });
+                }
+            }
+            else {
+                utils.disableTrigger(triggerIdentifier, undefined, 'Automatically disabled after firing once');
+                logger.info(method, 'the fire once date has expired, disabled', triggerIdentifier);
+            }
         }
         else if (dataTrigger.stopDate) {
             //check if the next scheduled trigger is after the stop date
@@ -199,14 +224,14 @@ module.exports = function(
             }
             else {
                 logger.info(method, 'could not find', triggerIdentifier, 'in database');
-                //make sure it is removed from memory as well
-                utils.deleteTrigger(triggerIdentifier);
+                //make sure it is already stopped
+                utils.stopTrigger(triggerIdentifier);
             }
         });
     };
 
-    this.deleteTrigger = function(triggerIdentifier) {
-        var method = 'deleteTrigger';
+    this.stopTrigger = function (triggerIdentifier) {
+        var method = 'stopTrigger';
 
         if (utils.triggers[triggerIdentifier]) {
             if (utils.triggers[triggerIdentifier].cronHandle) {
@@ -220,10 +245,6 @@ module.exports = function(
         }
     };
 
-    this.getTriggerIdentifier = function(apikey, namespace, name) {
-        return apikey + '/' + namespace + '/' + name;
-    };
-
     this.initAllTriggers = function() {
         var method = 'initAllTriggers';
 
@@ -242,14 +263,13 @@ module.exports = function(
                         var namespace = doc.namespace;
                         var name = doc.name;
                         var apikey = doc.apikey;
-                        var host = 'https://' + utils.routerHost + ':' + 443;
-                        var triggerURL = host + '/api/v1/namespaces/' + namespace + '/triggers/' + name;
+                        var uri = utils.uriHost + '/api/v1/namespaces/' + namespace + '/triggers/' + name;
                         var auth = apikey.split(':');
 
                         logger.info(method, 'Checking if trigger', triggerIdentifier, 'still exists');
                         request({
                             method: 'get',
-                            url: triggerURL,
+                            url: uri,
                             auth: {
                                 user: auth[0],
                                 pass: auth[1]
@@ -308,7 +328,7 @@ module.exports = function(
 
                 if (utils.triggers[triggerIdentifier]) {
                     if (doc.status && doc.status.active === false) {
-                        utils.deleteTrigger(triggerIdentifier);
+                        utils.stopTrigger(triggerIdentifier);
                     }
                 }
                 else {
diff --git a/settings.gradle b/settings.gradle
index 9c7a819..33ecef8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -11,6 +11,6 @@ include 'tests'
 rootProject.name = 'openwhisk-package-alarms'
 
 gradle.ext.scala = [
-    version: '2.11.8',
+    version: '2.11.11',
     compileFlags: ['-feature', '-unchecked', '-deprecation', '-Xfatal-warnings', '-Ywarn-unused-import']
 ]
diff --git a/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala b/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala
index 456d9ab..e158f7a 100644
--- a/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala
+++ b/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala
@@ -35,9 +35,8 @@ class AlarmsHealthFeedTests
 
     val wskprops = WskProps()
     val wsk = new Wsk
-
-
     val defaultAction = Some(TestUtils.getTestActionFilename("hello.js"))
+    val maxRetries = System.getProperty("max.retries", "100").toInt
 
     behavior of "Alarms Health tests"
 
@@ -60,8 +59,8 @@ class AlarmsHealthFeedTests
             }
 
             //create action
-            assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
-                action.create(name, defaultAction)
+            assetHelper.withCleaner(wsk.action, actionName) {
+                (action, name) => action.create(name, defaultAction)
             }
 
             val futureDate = System.currentTimeMillis + (1000 * 20)
@@ -72,18 +71,30 @@ class AlarmsHealthFeedTests
                 (trigger, name) =>
                     trigger.create(name, feed = Some(s"$packageName/once"), parameters = Map(
                         "trigger_payload" -> "alarmTest".toJson,
-                        "date" -> futureDate.toJson))
+                        "date" -> futureDate.toJson,
+                        "deleteAfterFire" -> "rules".toJson))
             }
 
             // create rule
-            assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
-                rule.create(name, trigger = triggerName, action = actionName)
+            assetHelper.withCleaner(wsk.rule, ruleName) {
+                (rule, name) => rule.create(name, trigger = triggerName, action = actionName)
             }
 
             println("waiting for trigger")
-            val activations = wsk.activation.pollFor(N = 1, Some(triggerName), retries = 90).length
+            val activations = wsk.activation.pollFor(N = 1, Some(triggerName), retries = maxRetries).length
             println(s"Found activation size (should be 1): $activations")
             activations should be(1)
+
+            // get activation list again, should be same as before waiting
+            println("confirming no new triggers")
+            val afterWait = wsk.activation.pollFor(N = activations + 1, Some(triggerName)).length
+            println(s"Found activation size after wait: $afterWait")
+            println("Activation list after wait should equal with activation list after firing once")
+            afterWait should be(activations)
+
+            //check that assets had been deleted by verifying we can recreate them
+            wsk.trigger.create(triggerName)
+            wsk.rule.create(ruleName, triggerName, actionName)
     }
 
     it should "fire cron trigger using startDate and stopDate" in withAssetCleaner(wskprops) {
@@ -105,8 +116,8 @@ class AlarmsHealthFeedTests
             }
 
             // create action
-            assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
-                action.create(name, defaultAction)
+            assetHelper.withCleaner(wsk.action, actionName) {
+                (action, name) => action.create(name, defaultAction)
             }
 
             val startDate = System.currentTimeMillis + (1000 * 20)
@@ -123,12 +134,12 @@ class AlarmsHealthFeedTests
             }
 
             // create rule
-            assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
-                rule.create(name, trigger = triggerName, action = actionName)
+            assetHelper.withCleaner(wsk.rule, ruleName) {
+                (rule, name) => rule.create(name, trigger = triggerName, action = actionName)
             }
 
             println("waiting for triggers")
-            val activations = wsk.activation.pollFor(N = 1, Some(triggerName), retries = 90).length
+            val activations = wsk.activation.pollFor(N = 1, Some(triggerName), retries = maxRetries).length
             println(s"Found activation size (should be 1): $activations")
             activations should be(1)
 
@@ -160,8 +171,8 @@ class AlarmsHealthFeedTests
             }
 
             // create action
-            assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
-                action.create(name, defaultAction)
+            assetHelper.withCleaner(wsk.action, actionName) {
+                (action, name) => action.create(name, defaultAction)
             }
 
             val startDate = System.currentTimeMillis + (1000 * 20)
@@ -178,17 +189,17 @@ class AlarmsHealthFeedTests
             }
 
             // create rule
-            assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
-                rule.create(name, trigger = triggerName, action = actionName)
+            assetHelper.withCleaner(wsk.rule, ruleName) {
+                (rule, name) => rule.create(name, trigger = triggerName, action = actionName)
             }
 
             println("waiting for start date")
-            val activations = wsk.activation.pollFor(N = 1, Some(triggerName), retries = 90).length
+            val activations = wsk.activation.pollFor(N = 1, Some(triggerName), retries = maxRetries).length
             println(s"Found activation size (should be 1): $activations")
             activations should be(1)
 
             println("waiting for interval")
-            val activationsAfterInterval = wsk.activation.pollFor(N = 2, Some(triggerName), retries = 90).length
+            val activationsAfterInterval = wsk.activation.pollFor(N = 2, Some(triggerName), retries = maxRetries).length
             println(s"Found activation size (should be 2): $activationsAfterInterval")
             activationsAfterInterval should be(2)
     }
@@ -233,8 +244,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, run) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
 
                     inside (activation.response.result) {
                         case Some(result) =>
@@ -294,8 +304,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, readRunResult) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
 
                     inside(activation.response.result) {
                         case Some(result) =>
@@ -321,8 +330,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, updateRunAction) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
             }
 
             val runResult = wsk.action.invoke(actionName, parameters = Map(
@@ -332,8 +340,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, runResult) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
 
                     inside(activation.response.result) {
                         case Some(result) =>
@@ -384,8 +391,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, readRunResult) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
 
                     inside(activation.response.result) {
                         case Some(result) =>
@@ -410,8 +416,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, updateRunAction) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
             }
 
             val runResult = wsk.action.invoke(actionName, parameters = Map(
@@ -421,8 +426,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, runResult) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
 
                     inside(activation.response.result) {
                         case Some(result) =>
@@ -473,8 +477,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, readRunResult) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
 
                     inside(activation.response.result) {
                         case Some(result) =>
@@ -500,8 +503,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, updateRunAction) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
             }
 
             val runResult = wsk.action.invoke(actionName, parameters = Map(
@@ -511,8 +513,7 @@ class AlarmsHealthFeedTests
             ))
 
             withActivation(wsk.activation, runResult) {
-                activation =>
-                    activation.response.success shouldBe true
+                activation => activation.response.success shouldBe true
 
                     inside(activation.response.result) {
                         case Some(result) =>


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services