Skip to content

Commit

Permalink
Initial webhooks impl
Browse files Browse the repository at this point in the history
  • Loading branch information
gschueler committed Mar 10, 2011
1 parent d1958df commit 90f2a4e
Show file tree
Hide file tree
Showing 6 changed files with 393 additions and 122 deletions.
61 changes: 33 additions & 28 deletions rundeckapp/grails-app/controllers/ExecutionController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -552,41 +552,46 @@ class ExecutionController {
return null==e.dateCompleted?EXECUTION_RUNNING:"true"==e.status?EXECUTION_SUCCEEDED:e.cancelled?EXECUTION_ABORTED:EXECUTION_FAILED
}
/**
* Utility, render xml response for a list of executions
* Render execution list xml given a List of executions, and a builder delegate
*/
public def renderApiExecutionListResultXML={execlist ->
return new ApiController().success{ delegate->
delegate.'executions'(count:execlist.size()){
execlist.each {Execution e ->
execution(
/** attributes **/
id: e.id,
href: g.createLink(controller: 'execution', action: 'follow', id: e.id, absolute: true),
status: getExecutionState(e)
) {
/** elements */
user(e.user)
delegate.'date-started'(unixtime: e.dateStarted.time, g.w3cDateValue(date: e.dateStarted))
if (null != e.dateCompleted) {
delegate.'date-ended'(unixtime: e.dateCompleted.time, g.w3cDateValue(date: e.dateCompleted))
}
if(e.cancelled){
abortedby(e.abortedby?e.abortedby:e.user)
}
if (e.scheduledExecution) {
job(id: e.scheduledExecution.id) {
name(e.scheduledExecution.jobName)
group(e.scheduledExecution.groupPath ?: '')
project(e.scheduledExecution.project)
description(e.scheduledExecution.description)
}
public def renderApiExecutions= { execlist, delegate ->
delegate.'executions'(count: execlist.size()) {
execlist.each {Execution e ->
execution(
/** attributes **/
id: e.id,
href: g.createLink(controller: 'execution', action: 'follow', id: e.id, absolute: true),
status: getExecutionState(e)
) {
/** elements */
user(e.user)
delegate.'date-started'(unixtime: e.dateStarted.time, g.w3cDateValue(date: e.dateStarted))
if (null != e.dateCompleted) {
delegate.'date-ended'(unixtime: e.dateCompleted.time, g.w3cDateValue(date: e.dateCompleted))
}
if (e.cancelled) {
abortedby(e.abortedby ? e.abortedby : e.user)
}
if (e.scheduledExecution) {
job(id: e.scheduledExecution.id) {
name(e.scheduledExecution.jobName)
group(e.scheduledExecution.groupPath ?: '')
project(e.scheduledExecution.project)
description(e.scheduledExecution.description)
}
description(ExecutionService.summarizeJob(e.scheduledExecution, e))
}
description(ExecutionService.summarizeJob(e.scheduledExecution, e))
}
}
}
}

/**
* Utility, render xml response for a list of executions
*/
public def renderApiExecutionListResultXML={execlist ->
return new ApiController().success(renderApiExecutions.curry(execlist))
}
/**
* API: /api/execution/{id} , version 1
*/
Expand Down
127 changes: 108 additions & 19 deletions rundeckapp/grails-app/controllers/ScheduledExecutionController.groovy
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import org.quartz.*
import java.text.ParseException

import com.dtolabs.rundeck.core.common.Framework
import java.text.SimpleDateFormat
import groovy.xml.QName

import org.springframework.web.multipart.MultipartHttpServletRequest
import java.util.regex.Pattern
import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.HttpMethod
import org.apache.commons.httpclient.methods.GetMethod
import org.apache.commons.httpclient.params.HttpClientParams
import grails.converters.JSON

import org.codehaus.groovy.grails.web.json.JSONElement
import com.dtolabs.rundeck.core.utils.NodeSet
import groovy.xml.MarkupBuilder
import com.dtolabs.client.utils.Constants
import com.dtolabs.utils.Streams

class ScheduledExecutionController {
def Scheduler quartzScheduler
Expand Down Expand Up @@ -757,13 +756,19 @@ class ScheduledExecutionController {

}

if(!params.notifications && (params.notifyOnsuccess || params.notifyOnfailure)){
def nots=[exists:true]
if(!params.notifications && (params.notifyOnsuccess || params.notifyOnfailure || params.notifyOnsuccessUrl || params.notifyOnfailureUrl) ){
def nots=[]
if('true'==params.notifyOnsuccess){
nots['onsuccess']=[email:params.notifySuccessRecipients]
nots << [eventTrigger:'onsuccess',type:'email',content:params.notifySuccessRecipients]
}
if('true'==params.notifyOnsuccessUrl){
nots << [eventTrigger: 'onsuccess', type: 'url', content: params.notifySuccessUrl]
}
if('true'==params.notifyOnfailure){
nots['onfailure']=[email:params.notifyFailureRecipients]
nots << [eventTrigger: 'onfailure', type: 'email', content: params.notifyFailureRecipients]
}
if('true'==params.notifyOnfailureUrl){
nots << [eventTrigger: 'onfailure', type: 'url', content: params.notifyFailureUrl]
}
params.notifications=nots
}
Expand Down Expand Up @@ -1669,19 +1674,24 @@ class ScheduledExecutionController {
}
}
}
if(!params.notifications && (params.notifyOnsuccess || params.notifyOnfailure)){
def nots=[:]
if(!params.notifications && (params.notifyOnsuccess || params.notifyOnfailure || params.notifyOnsuccessUrl || params.notifyOnfailureUrl)){
def nots=[]
if('true'==params.notifyOnsuccess){
nots['onsuccess']=[email:params.notifySuccessRecipients]
nots<<[eventTrigger:'onsuccess',type:'email',content:params.notifySuccessRecipients]
}
if('true'==params.notifyOnsuccessUrl){
nots << [eventTrigger: 'onsuccess', type: 'url', content:params.notifySuccessUrl]
}
if('true'==params.notifyOnfailure){
nots['onfailure']=[email:params.notifyFailureRecipients]
nots << [eventTrigger: 'onfailure', type: 'email', content:params.notifyFailureRecipients]
}
if('true'==params.notifyOnfailureUrl){
nots << [eventTrigger: 'onfailure', type: 'url', content:params.notifyFailureUrl]
}
params.notifications=nots
}
if(params.notifications){
//create notifications
def i=0;
failed=_updateNotifications(params, scheduledExecution)
}
if(scheduledExecution.doNodedispatch){
Expand Down Expand Up @@ -1718,10 +1728,11 @@ class ScheduledExecutionController {
private boolean _updateNotifications(Map params,ScheduledExecution scheduledExecution) {
boolean failed=false
def fieldNames=[onsuccess:'notifySuccessRecipients',onfailure:'notifyFailureRecipients']
['onsuccess', 'onfailure'].each {trigger ->
def notif = params.notifications[trigger]
if (notif && notif.email) {
def arr=notif.email.split(",")
def fieldNamesUrl=[onsuccess:'notifySuccessUrl',onfailure:'notifyFailureUrl']
params.notifications.each {notif ->
def trigger=notif.eventTrigger
if (notif && notif.type=='email' && notif.content) {
def arr=notif.content.split(",")
arr.each{email->
if(email && !org.apache.commons.validator.EmailValidator.getInstance().isValid(email)){
failed=true
Expand Down Expand Up @@ -1751,6 +1762,45 @@ class ScheduledExecutionController {
)
}
n.scheduledExecution = scheduledExecution
}else if (notif && notif.type=='url' && notif.content) {
def arr=notif.content.split(",")

arr.each{String url ->
boolean valid=false
try{
new URL(url)
valid=true
}catch(MalformedURLException e){
valid=false
}
if(url && !valid){
failed=true
scheduledExecution.errors.rejectValue(
fieldNamesUrl[trigger],
'scheduledExecution.notifications.invalidurl.message',
[url] as Object[],
'Invalid URL: {0}'
)
}
}
if(failed){
return
}
def addrs = arr.findAll{it.trim()}.join(",")
Notification n = new Notification(eventTrigger: trigger, type: 'url', content: addrs)
scheduledExecution.addToNotifications(n)
if (!n.validate()) {
failed = true
n.discard()
def errmsg = trigger + " notification: " + n.errors.allErrors.collect {g.message(error: it)}.join(";")
scheduledExecution.errors.rejectValue(
fieldNamesUrl[trigger],
'scheduledExecution.notifications.invalid.message',
[errmsg] as Object[],
'Invalid notification definition: {0}'
)
}
n.scheduledExecution = scheduledExecution
}
}
return failed
Expand All @@ -1764,8 +1814,9 @@ class ScheduledExecutionController {
private boolean _updateNotifications(ScheduledExecution params,ScheduledExecution scheduledExecution) {
boolean failed=false
def fieldNames=[onsuccess:'notifySuccessRecipients',onfailure:'notifyFailureRecipients']
['onsuccess', 'onfailure'].each {trigger ->
def notif = params.notifications.find{it.eventTrigger==trigger}
def fieldNamesUrl=[onsuccess:'notifySuccessUrl',onfailure:'notifyFailureUrl']
params.notifications.each {notif ->
def trigger = notif.eventTrigger
if (notif && notif.type=='email' && notif.content) {
def arr=notif.content.split(",")
arr.each{email->
Expand Down Expand Up @@ -1797,6 +1848,44 @@ class ScheduledExecutionController {
)
}
n.scheduledExecution = scheduledExecution
}else if (notif && notif.type=='url' && notif.content) {
def arr=notif.content.split(",")
arr.each{String url ->
boolean valid = false
try {
new URL(url)
valid = true
} catch (MalformedURLException e) {
valid = false
}
if (url && !valid) {
failed=true
scheduledExecution.errors.rejectValue(
fieldNamesUrl[trigger],
'scheduledExecution.notifications.invalidurl.message',
[url] as Object[],
'Invalid URL: {0}'
)
}
}
if(failed){
return
}
def addrs = arr.findAll{it.trim()}.join(",")
Notification n = new Notification(eventTrigger: trigger, type: 'email', content: addrs)
scheduledExecution.addToNotifications(n)
if (!n.validate()) {
failed = true
n.discard()
def errmsg = trigger + " notification: " + n.errors.allErrors.collect {g.message(error: it)}.join(";")
scheduledExecution.errors.rejectValue(
fieldNamesUrl[trigger],
'scheduledExecution.notifications.invalid.message',
[errmsg] as Object[],
'Invalid notification definition: {0}'
)
}
n.scheduledExecution = scheduledExecution
}
}
return failed
Expand Down
15 changes: 11 additions & 4 deletions rundeckapp/grails-app/domain/Notification.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ public class Notification {
*/
String eventTrigger
/**
* type is the type of notification to initiate, e.g. "email" to send an email
* type is the type of notification to initiate, e.g. "email" to send an email, "url" to POST to a url
*/
String type
/**
* content contains data to use for the notification, e.g. a list of email addresses
* content contains data to use for the notification, e.g. a list of email addresses, or a list of URLs
*/
String content

Expand All @@ -49,13 +49,20 @@ public class Notification {

public static Notification fromMap(String key,Map data){
Notification n = new Notification(eventTrigger:key)
n.type='email'
n.content=data.recipients
if(data.recipients){
n.type='email'
n.content=data.recipients
}else if(data.url){
n.type='url'
n.content=data.url
}
return n;
}
public Map toMap(){
if(type=='email'){
return [recipients:content]
}else if(type=='url'){
return [url:content]
}else{
return null
}
Expand Down
8 changes: 5 additions & 3 deletions rundeckapp/grails-app/domain/ScheduledExecution.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class ScheduledExecution extends ExecutionContext {
Date lastUpdated
String notifySuccessRecipients
String notifyFailureRecipients
static transients = ['adhocExecutionType','notifySuccessRecipients','notifyFailureRecipients','crontabString']
String notifySuccessUrl
String notifyFailureUrl
static transients = ['adhocExecutionType','notifySuccessRecipients','notifyFailureRecipients', 'notifySuccessUrl', 'notifyFailureUrl','crontabString']

static constraints = {
workflow(nullable:true)
Expand Down Expand Up @@ -512,9 +514,9 @@ class ScheduledExecution extends ExecutionContext {
return result;
}

def Notification findNotification(String trigger){
def Notification findNotification(String trigger, String type){
if(this.id){
return Notification.findByScheduledExecutionAndEventTrigger(this,trigger)
return this.notifications.find{it.eventTrigger==trigger && it.type==type}
}else{
return null
}
Expand Down

0 comments on commit 90f2a4e

Please sign in to comment.