Skip to content

Commit

Permalink
Add /api/resources and /api/resource/name api endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
gschueler committed Feb 14, 2011
1 parent 1db4cfc commit b6bca57
Show file tree
Hide file tree
Showing 6 changed files with 796 additions and 0 deletions.
2 changes: 2 additions & 0 deletions rundeckapp/grails-app/conf/UrlMappings.groovy
Expand Up @@ -24,6 +24,8 @@ class UrlMappings {
"/api/project/$project?"(controller: 'framework', action: 'apiProject')
"/api/projects"(controller: 'framework', action: 'apiProjects')
"/api/renderError"(controller: 'api', action: 'renderError')
"/api/resources"(controller: 'framework', action: 'apiResources')
"/api/resource/$name"(controller: 'framework', action: 'apiResource')
"/api/run/command"(controller: 'scheduledExecution', action: 'apiRunCommand')
"/api/run/script"(controller: 'scheduledExecution', action: 'apiRunScript')

Expand Down
93 changes: 93 additions & 0 deletions rundeckapp/grails-app/controllers/FrameworkController.groovy
Expand Up @@ -5,6 +5,10 @@ import com.dtolabs.rundeck.core.common.Nodes
import com.dtolabs.rundeck.core.common.INodeEntry
import com.dtolabs.rundeck.core.utils.NodeSet
import java.util.regex.PatternSyntaxException
import com.dtolabs.shared.resources.ResourceXMLGenerator
import com.dtolabs.rundeck.core.common.NodesYamlGenerator
import com.dtolabs.rundeck.core.common.NodesFileGenerator
import com.dtolabs.rundeck.core.common.NodesGeneratorException

class FrameworkController {
FrameworkService frameworkService
Expand Down Expand Up @@ -467,5 +471,94 @@ class FrameworkController {
}
}
}

/**
* API: /api/resource/$name, version 1.2
*/
def apiResource={
Framework framework = frameworkService.getFrameworkFromUserSession(session,request)
if(!params.project){
flash.error=g.message(code:'api.error.parameter.required',args:['project'])
return chain(controller:'api',action:'error')
}
if(!params.name){
flash.error=g.message(code:'api.error.parameter.required',args:['name'])
return chain(controller:'api',action:'error')
}
def exists=frameworkService.existsFrameworkProject(params.project,framework)
if(!exists){
flash.error=g.message(code:'api.error.item.doesnotexist',args:['project',params.project])
return chain(controller:'api',action:'error')
}

NodeSet nset = new NodeSet()
nset.setSingleNodeName(params.name)
def pject=frameworkService.getFrameworkProject(params.project,framework)
final Collection nodes = pject.getNodes().filterNodes(nset)
if(!nodes || nodes.size()<1 ){
flash.error=g.message(code:'api.error.item.doesnotexist',args:['Node Name',params.name])
return chain(controller:'api',action:'error')
}
return apiRenderNodeResult(nodes)
}
/**
* API: /api/resources, version 1.2
*/
def apiResources={ExtNodeFilters query->
Framework framework = frameworkService.getFrameworkFromUserSession(session,request)
if(!params.project){
flash.error=g.message(code:'api.error.parameter.required',args:['project'])
return chain(controller:'api',action:'error')
}
def exists=frameworkService.existsFrameworkProject(params.project,framework)
if(!exists){
flash.error=g.message(code:'api.error.item.doesnotexist',args:['project',params.project])
return chain(controller:'api',action:'error')
}

//convert api parameters to node filter parameters
BaseNodeFilters.filterKeys.each{k,v->
if(params[k]){
query["nodeInclude${v}"]=params.remove(k)
}
if(params["exclude-"+k]){
query["nodeExclude${v}"]=params.remove("exclude-"+k)
}
}

if(query.nodeFilterIsEmpty()){
query.nodeIncludeName = framework.getFrameworkNodeName()
}
def pject=frameworkService.getFrameworkProject(params.project,framework)
final Collection nodes = pject.getNodes().filterNodes(ExecutionService.filtersAsNodeSet(query))
return apiRenderNodeResult(nodes)
}
def apiRenderNodeResult={nodes->

withFormat{
xml{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final NodesFileGenerator generator = new ResourceXMLGenerator(baos)
nodes.each {INodeEntry node->
generator.addNode(node)
}
generator.generate()
return render(contentType:"text/xml",encoding:"UTF-8",text:baos.toString())
}
yaml{
if(nodes.size()>0){
StringWriter sw = new StringWriter()
final NodesFileGenerator generator = new NodesYamlGenerator(sw)
nodes.each {INodeEntry node->
generator.addNode(node)
}
generator.generate()
return render(contentType:"text/yaml",encoding:"UTF-8",text:sw.toString())
}else{
return render(contentType:"text/yaml",encoding:"UTF-8",text:"# 0 results for query\n")
}
}
}
}
}

260 changes: 260 additions & 0 deletions test/api/test-resource.sh
@@ -0,0 +1,260 @@
#!/bin/bash

#test /api/resource/name output.

errorMsg() {
echo "$*" 1>&2
}
assert(){
# assert expected, actual
if [ "$1" != "$2" ] ; then
errorMsg "FAIL: Expected value \"$1\" but saw: \"$2\" ${3}"
exit 2
fi
}

DIR=$(cd `dirname $0` && pwd)

# accept url argument on commandline, if '-' use default
url="$1"
if [ "-" == "$1" ] ; then
url='http://localhost:4440/api'
fi
apiurl="${url}/api"
VERSHEADER="X-RUNDECK-API-VERSION: 1.2"

# curl opts to use a cookie jar, and follow redirects, showing only errors
CURLOPTS="-s -S -L -c $DIR/cookies -b $DIR/cookies"
CURL="curl $CURLOPTS"

file=$DIR/curl.out

XMLSTARLET=xml

###
# Setup: acquire local node name
####

runurl="${apiurl}/resources"

project="test"
params="project=${project}"

# get listing
$CURL --header "$VERSHEADER" ${runurl}?${params} > ${file}
if [ 0 != $? ] ; then
errorMsg "ERROR: failed query request"
exit 2
fi
#test curl.out for valid xml
$XMLSTARLET val -w ${file} > /dev/null 2>&1
validxml=$?
if [ 0 == $validxml ] ; then
#test for for possible result error message
$XMLSTARLET el ${file} | grep -e '^result' -q
if [ 0 == $? ] ; then
#test for error message
#If <result error="true"> then an error occured.
waserror=$($XMLSTARLET sel -T -t -v "/result/@error" ${file})
errmsg=$($XMLSTARLET sel -T -t -v "/result/error/message" ${file})
if [ "" != "$waserror" -a "true" == $waserror ] ; then
errorMsg "ERROR: expected resource.xml content but received error result: $errmsg"
exit 2
fi
fi
fi

#test curl.out for valid xml
if [ 0 != $validxml ] ; then
errorMsg "ERROR: Response was not valid xml"
exit 2
fi

#test for expected /joblist element
$XMLSTARLET el ${file} | grep -e '^project' -q
if [ 0 != $? ] ; then
errorMsg "ERROR: Response did not contain expected result"
exit 2
fi

#Check projects list
itemcount=$($XMLSTARLET sel -T -t -v "count(/project/node)" ${file})
if [ "1" != "$itemcount" ] ; then
errorMsg "FAIL: expected single /project/node element"
exit 2
fi

localnode=$($XMLSTARLET sel -T -t -v "/project/node/@name" ${file})

runurl="${apiurl}/resource/$localnode"
params="project=${project}"

echo "TEST: /api/resource/$localnode"

# get listing
$CURL --header "$VERSHEADER" ${runurl}?${params} > ${file}
if [ 0 != $? ] ; then
errorMsg "ERROR: failed query request"
exit 2
fi
#test curl.out for valid xml
$XMLSTARLET val -w ${file} > /dev/null 2>&1
validxml=$?
if [ 0 == $validxml ] ; then
#test for for possible result error message
$XMLSTARLET el ${file} | grep -e '^result' -q
if [ 0 == $? ] ; then
#test for error message
#If <result error="true"> then an error occured.
waserror=$($XMLSTARLET sel -T -t -v "/result/@error" ${file})
errmsg=$($XMLSTARLET sel -T -t -v "/result/error/message" ${file})
if [ "" != "$waserror" -a "true" == $waserror ] ; then
errorMsg "FAIL: expected resource.xml content but received error result: $errmsg"
exit 2
fi
fi
fi

#test curl.out for valid xml
if [ 0 != $validxml ] ; then
errorMsg "FAIL: Response was not valid xml"
exit 2
fi

#test for expected /joblist element
$XMLSTARLET el ${file} | grep -e '^project' -q
if [ 0 != $? ] ; then
errorMsg "FAIL: Response did not contain expected result"
exit 2
fi

#Check projects list
itemcount=$($XMLSTARLET sel -T -t -v "count(/project/node)" ${file})
if [ "1" != "$itemcount" ] ; then
errorMsg "FAIL: expected single /project/node element"
exit 2
fi

testname=$($XMLSTARLET sel -T -t -v "/project/node/@name" ${file})

assert "$localnode" "$testname" "Wrong node name returned"
echo "OK"

#test yaml output
params="project=${project}&format=yaml"

echo "TEST: /api/resource/$localnode YAML response"

# get listing
$CURL --header "$VERSHEADER" ${runurl}?${params} > ${file}
if [ 0 != $? ] ; then
errorMsg "ERROR: failed query request"
exit 2
fi
#test curl.out for valid xml
$XMLSTARLET val -w ${file} > /dev/null 2>&1
validxml=$?
if [ 0 == $validxml ] ; then
#test for error message
#If <result error="true"> then an error occured.
waserror=$($XMLSTARLET sel -T -t -v "/result/@error" ${file})
errmsg=$($XMLSTARLET sel -T -t -v "/result/error/message" ${file})
errorMsg "FAIL: expected YAML content but received error result: $errmsg"
exit 2
fi

itemcount=$(grep 'hostname: ' ${file} | wc -l | sed 's/^[ ]*//g')

if [ "1" != "$itemcount" ] ; then
errorMsg "FAIL: Expected single yaml result, was \"${itemcount}\""
exit 2
fi

testname=$(grep 'nodename: ' ${file})

assert " nodename: $localnode" "$testname" "Wrong node name returned"

echo "OK"

####
# test with preset resources.
####

# temporarily move actual resources.xml out of the way, and replace with our own

cp $RDECK_BASE/projects/test/etc/resources.xml $RDECK_BASE/projects/test/etc/resources.xml.backup

cat <<END > $RDECK_BASE/projects/test/etc/resources.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project PUBLIC "-//DTO Labs Inc.//DTD Resources Document 1.0//EN" "project.dtd">
<project>
<node name="test1" type="Node" description="Rundeck test node" tags="test1,testboth" hostname="testhost1" osArch="x86_64" osFamily="unix" osName="Mac OS X" osVersion="10.6.6" username="rdeck" editUrl="" remoteUrl=""/>
<node name="test2" type="Node" description="Rundeck test node" tags="test2,testboth" hostname="testhost2" osArch="x86_64" osFamily="unix" osName="Mac OS X" osVersion="10.6.6" username="rdeck1" editUrl="" remoteUrl=""/>
</project>
END

####
# query specific test nodes
####

params="project=${project}&format=xml&"
runurl="${apiurl}/resource/test1"

echo "TEST: query result for /etc/resources/test1"

$CURL --header "$VERSHEADER" ${runurl}?${params} > ${file}
if [ 0 != $? ] ; then
errorMsg "ERROR: failed query request"
exit 2
fi
$XMLSTARLET val -w ${file} > /dev/null 2>&1
if [ 0 != $? ] ; then
errorMsg "FAIL: result was not valid xml"
exit 2
fi

#Check projects list
itemcount=$($XMLSTARLET sel -T -t -v "count(/project/node)" ${file})
if [ "1" != "$itemcount" ] ; then
errorMsg "FAIL: expected single /project/node element"
exit 2
fi
itemname=$($XMLSTARLET sel -T -t -v "/project/node/@name" ${file})
assert "test1" $itemname "Query result name was wrong"

echo "OK"

####
#query test2 node
####

params="project=${project}&format=xml&"
runurl="${apiurl}/resource/test2"

echo "TEST: query result for /etc/resource/test2"

$CURL --header "$VERSHEADER" ${runurl}?${params} > ${file}
if [ 0 != $? ] ; then
errorMsg "ERROR: failed query request"
exit 2
fi
$XMLSTARLET val -w ${file} > /dev/null 2>&1
if [ 0 != $? ] ; then
errorMsg "FAIL: result was not valid xml"
exit 2
fi

#Check projects list
itemcount=$($XMLSTARLET sel -T -t -v "count(/project/node)" ${file})
if [ "1" != "$itemcount" ] ; then
errorMsg "FAIL: expected single /project/node element"
exit 2
fi
itemname=$($XMLSTARLET sel -T -t -v "/project/node/@name" ${file})
assert "test2" $itemname "Query result name was wrong"

echo "OK"

rm ${file}
mv $RDECK_BASE/projects/test/etc/resources.xml.backup $RDECK_BASE/projects/test/etc/resources.xml

0 comments on commit b6bca57

Please sign in to comment.