This is an automated email from the ASF dual-hosted git repository. csantanapr pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-package-alarms.git
The following commit(s) were added to refs/heads/master by this push: new b8d4d67 optionally limit cron fields to 5 instead of 6 (#126) b8d4d67 is described below commit b8d4d67abb015e728ea1db23c4b39e4b8674b45e Author: Jason Peterson <jason...@us.ibm.com> AuthorDate: Tue Feb 13 12:14:21 2018 -0500 optionally limit cron fields to 5 instead of 6 (#126) --- action/alarmWebAction.js | 35 +++++----- action/lib/common.js | 17 ++++- installCatalog.sh | 37 +++++----- .../system/health/AlarmsHealthFeedTests.scala | 78 +++------------------- .../scala/system/packages/AlarmsFeedTests.scala | 31 ++++++++- 5 files changed, 92 insertions(+), 106 deletions(-) diff --git a/action/alarmWebAction.js b/action/alarmWebAction.js index d56a159..b5b5bde 100644 --- a/action/alarmWebAction.js +++ b/action/alarmWebAction.js @@ -72,6 +72,10 @@ function main(params) { try { cronHandle = new CronJob(params.cron, function() {}); + //validate cron granularity if 5 fields are allowed instead of 6 + if (params.limitCronFields && hasSecondsGranularity(params.cron)) { + return common.sendError(400, 'cron pattern is limited to 5 fields with 1 minute as the finest granularity'); + } newTrigger.cron = params.cron; } catch(ex) { return common.sendError(400, `cron pattern '${params.cron}' is not valid`); @@ -198,7 +202,7 @@ function main(params) { } if (params.trigger_payload) { - updatedParams.payload = constructPayload(params.trigger_payload); + updatedParams.payload = common.constructPayload(params.trigger_payload); } if (trigger.date) { @@ -228,6 +232,10 @@ function main(params) { if (params.cron) { try { new CronJob(params.cron, function() {}); + //validate cron granularity if 5 fields are allowed instead of 6 + if (params.limitCronFields && hasSecondsGranularity(params.cron)) { + return common.sendError(400, 'cron pattern is limited to 5 fields with 1 minute as the finest granularity'); + } } catch (ex) { return reject(common.sendError(400, `cron pattern '${params.cron}' is not valid`)); } @@ -313,20 +321,6 @@ function main(params) { } } -function constructPayload(payload) { - - var updatedPayload; - if (payload) { - if (typeof payload === 'string') { - updatedPayload = {payload: payload}; - } - if (typeof payload === 'object') { - updatedPayload = payload; - } - } - return updatedPayload; -} - function validateDate(date, paramName, startDate) { var dateObject = new Date(date); @@ -346,6 +340,17 @@ function validateDate(date, paramName, startDate) { } +function hasSecondsGranularity(cron) { + + var fields = (cron + '').trim().split(/\s+/); + + if (fields.length > 5 && fields[fields.length - 6] !== '0') { + return true; + } + + return false; +} + exports.main = main; diff --git a/action/lib/common.js b/action/lib/common.js index c62e1f8..663be12 100644 --- a/action/lib/common.js +++ b/action/lib/common.js @@ -104,11 +104,26 @@ function sendError(statusCode, error, message) { }; } +function constructPayload(payload) { + + var updatedPayload; + if (payload) { + if (typeof payload === 'string') { + updatedPayload = {payload: payload}; + } + if (typeof payload === 'object') { + updatedPayload = payload; + } + } + return updatedPayload; +} + module.exports = { 'requestHelper': requestHelper, 'createWebParams': createWebParams, 'verifyTriggerAuth': verifyTriggerAuth, 'parseQName': parseQName, - 'sendError': sendError + 'sendError': sendError, + 'constructPayload': constructPayload }; diff --git a/installCatalog.sh b/installCatalog.sh index ddc9337..85f6467 100755 --- a/installCatalog.sh +++ b/installCatalog.sh @@ -12,9 +12,8 @@ set -x : ${OPENWHISK_HOME:?"OPENWHISK_HOME must be set and non-empty"} WSK_CLI="$OPENWHISK_HOME/bin/wsk" -if [ $# -eq 0 ] -then -echo "Usage: ./installCatalog.sh <authkey> <edgehost> <dburl> <dbprefix> <apihost> <workers>" +if [ $# -eq 0 ]; then + echo "Usage: ./installCatalog.sh <authkey> <edgehost> <dburl> <dbprefix> <apihost> <workers>" fi AUTH="$1" @@ -23,6 +22,7 @@ DB_URL="$3" DB_NAME="${4}alarmservice" APIHOST="$5" WORKERS="$6" +LIMIT_CRON_FIELDS="${LIMIT_CRON_FIELDS}" # If the auth key file exists, read the key in the file. Otherwise, take the # first argument as the key itself. @@ -57,8 +57,7 @@ $WSK_CLI -i --apihost "$EDGEHOST" package update --auth "$AUTH" --shared yes ala # make alarmFeed.zip cd action -if [ -e alarmFeed.zip ] -then +if [ -e alarmFeed.zip ]; then rm -rf alarmFeed.zip fi @@ -82,26 +81,26 @@ $WSK_CLI -i --apihost "$EDGEHOST" action update --kind nodejs:6 --auth "$AUTH" a -a feed true \ -p isInterval true -if [ -n "$WORKERS" ]; -then - $WSK_CLI -i --apihost "$EDGEHOST" package update --auth "$AUTH" --shared no alarmsWeb \ - -p DB_URL "$DB_URL" \ - -p DB_NAME "$DB_NAME" \ - -p apihost "$APIHOST" \ - -p workers "$WORKERS" -else - $WSK_CLI -i --apihost "$EDGEHOST" package update --auth "$AUTH" --shared no alarmsWeb \ - -p DB_URL "$DB_URL" \ - -p DB_NAME "$DB_NAME" \ - -p apihost "$APIHOST" +COMMAND=" -i --apihost $EDGEHOST package update --auth $AUTH --shared no alarmsWeb \ + -p DB_URL $DB_URL \ + -p DB_NAME $DB_NAME \ + -p apihost $APIHOST" + +if [ -n "$WORKERS" ]; then + COMMAND+=" -p workers $WORKERS" +fi + +if [ -n "$LIMIT_CRON_FIELDS" ]; then + COMMAND+=" -p limitCronFields $LIMIT_CRON_FIELDS" fi +$WSK_CLI $COMMAND + # make alarmWebAction.zip cp -f alarmWeb_package.json package.json npm install -if [ -e alarmWebAction.zip ]; -then +if [ -e alarmWebAction.zip ]; then rm -rf alarmWebAction.zip fi diff --git a/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala b/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala index cbee2c5..e1b84f4 100644 --- a/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala +++ b/tests/src/test/scala/system/health/AlarmsHealthFeedTests.scala @@ -43,67 +43,6 @@ class AlarmsHealthFeedTests behavior of "Alarms Health tests" - it should "bind alarm package and fire periodic trigger using cron feed" in withAssetCleaner(wskprops) { - (wp, assetHelper) => - implicit val wskprops = wp // shadow global props and make implicit - val triggerName = s"dummyAlarmsTrigger-${System.currentTimeMillis}" - val ruleName = s"dummyAlarmsRule-${System.currentTimeMillis}" - val actionName = s"dummyAlarmsAction-${System.currentTimeMillis}" - val packageName = "dummyAlarmsPackage" - - // the package alarms should be there - val packageGetResult = wsk.pkg.get("/whisk.system/alarms") - println("fetched package alarms") - packageGetResult.stdout should include("ok") - - // create package binding - assetHelper.withCleaner(wsk.pkg, packageName) { - (pkg, name) => pkg.bind("/whisk.system/alarms", name) - } - - // create action - assetHelper.withCleaner(wsk.action, actionName) { (action, name) => - action.create(name, defaultAction) - } - - // create trigger feed - println(s"Creating trigger: $triggerName") - assetHelper.withCleaner(wsk.trigger, triggerName) { - (trigger, name) => - trigger.create(name, feed = Some(s"$packageName/alarm"), parameters = Map( - "trigger_payload" -> "alarmTest".toJson, - "cron" -> "* * * * * *".toJson)) - } - - // create rule - assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) => - rule.create(name, trigger = triggerName, action = actionName) - } - - println("waiting for triggers") - val activations = wsk.activation.pollFor(N = 5, Some(triggerName), retries = 30).length - println(s"Found activation size (should be at least 5): $activations") - activations should be >= 5 - - // delete the whisk trigger, which must also delete the feed - wsk.trigger.delete(triggerName) - - // get activation list after delete of the trigger - val activationsAfterDelete = wsk.activation.pollFor(N = 100, Some(triggerName), retries = 30).length - val now = Instant.now(Clock.systemUTC()) - println(s"Found activation size after delete ($now): $activationsAfterDelete") - - // recreate the trigger now without the feed - wsk.trigger.create(triggerName) - - // get activation list again, should be same as before waiting - println("confirming no new triggers") - val activationsAfterWait = wsk.activation.pollFor(N = activationsAfterDelete + 1, Some(triggerName)).length - println(s"Found activation size after wait: $activationsAfterWait") - println("Activation list after wait should equal with activation list after delete") - activationsAfterWait should be(activationsAfterDelete) - } - it should "fire an alarm once trigger when specifying a future date" in withAssetCleaner(wskprops) { (wp, assetHelper) => implicit val wskprops = wp // shadow global props and make implicit @@ -173,14 +112,14 @@ class AlarmsHealthFeedTests } val startDate = System.currentTimeMillis + (1000 * 20) - val stopDate = startDate + (1000 * 10) + val stopDate = startDate + (1000 * 60) // create trigger feed println(s"Creating trigger: $triggerName") assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) => trigger.create(name, feed = Some(s"$packageName/alarm"), parameters = Map( - "cron" -> "* * * * * *".toJson, + "cron" -> "* * * * *".toJson, "startDate" -> startDate.toJson, "stopDate" -> stopDate.toJson)) } @@ -191,9 +130,9 @@ class AlarmsHealthFeedTests } println("waiting for triggers") - val activations = wsk.activation.pollFor(N = 20, Some(triggerName), retries = 60).length - println(s"Found activation size (should be at least 5): $activations") - activations should be >= 5 + val activations = wsk.activation.pollFor(N = 1, Some(triggerName), retries = 90).length + println(s"Found activation size (should be 1): $activations") + activations should be(1) // get activation list again, should be same as before waiting @@ -277,8 +216,7 @@ class AlarmsHealthFeedTests val triggerPayload = JsObject( "test" -> JsString("alarmsTest") ) - val cronString = "* * * * * *" - val maxTriggers = -1 + val cronString = "* * * * *" // create trigger feed val feedCreationResult = assetHelper.withCleaner(wsk.trigger, triggerName) { @@ -335,7 +273,7 @@ class AlarmsHealthFeedTests (pkg, name) => pkg.bind("/whisk.system/alarms", name) } - val cron = "* * * * * *" + val cron = "* * * * *" val startDate = System.currentTimeMillis + (1000 * 20) val stopDate = startDate + (1000 * 10) @@ -371,7 +309,7 @@ class AlarmsHealthFeedTests } } - val updatedCron = "*/2 * * * * *" + val updatedCron = "*/2 * * * *" val updatedStartDate = System.currentTimeMillis + (1000 * 20) val updatedStopDate = updatedStartDate + (1000 * 10) diff --git a/tests/src/test/scala/system/packages/AlarmsFeedTests.scala b/tests/src/test/scala/system/packages/AlarmsFeedTests.scala index 62dd74f..59c4b0a 100644 --- a/tests/src/test/scala/system/packages/AlarmsFeedTests.scala +++ b/tests/src/test/scala/system/packages/AlarmsFeedTests.scala @@ -20,7 +20,7 @@ import common._ import org.junit.runner.RunWith import org.scalatest.FlatSpec import org.scalatest.junit.JUnitRunner -import spray.json.DefaultJsonProtocol.{IntJsonFormat, LongJsonFormat, StringJsonFormat} +import spray.json.DefaultJsonProtocol.{BooleanJsonFormat, IntJsonFormat, LongJsonFormat, StringJsonFormat} import spray.json.{JsString, pimpAny} /** @@ -452,4 +452,33 @@ class AlarmsFeedTests error.fields("error") shouldBe JsString(s"startDate parameter '${updatedStartDate}' must be less than the stopDate parameter '${stopDate}'") } } + + it should "return error message when limitCronFields is true and 6 cron fields are used" in withAssetCleaner(wskprops) { + (wp, assetHelper) => + implicit val wskprops = wp // shadow global props and make implicit + val triggerName = s"dummyCloudantTrigger-${System.currentTimeMillis}" + val packageName = "dummyCloudantPackage" + val feed = "alarm" + + // the package alarms should be there + val packageGetResult = wsk.pkg.get("/whisk.system/alarms") + println("fetched package alarms") + packageGetResult.stdout should include("ok") + + // create package binding + assetHelper.withCleaner(wsk.pkg, packageName) { + (pkg, name) => pkg.bind("/whisk.system/alarms", name) + } + + // create trigger with feed + val feedCreationResult = assetHelper.withCleaner(wsk.trigger, triggerName, confirmDelete = false) { + (trigger, name) => + trigger.create(name, feed = Some(s"$packageName/$feed"), parameters = Map( + "cron" -> "* * * * * *".toJson, + "limitCronFields" -> true.toJson), + expectedExitCode = 246) + } + feedCreationResult.stderr should include("cron pattern is limited to 5 fields with 1 minute as the finest granularity") + + } } -- To stop receiving notification emails like this one, please contact csantan...@apache.org.