Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ltamaster committed Oct 16, 2018
0 parents commit da36877
Show file tree
Hide file tree
Showing 10 changed files with 521 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.idea
build
.gradle
gradle
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# jq-JSON mapper log filter plugin

This plugin maps a JSON String output from a step to key/value context variables.
It uses the [jackson-jq](https://github.com/eiiches/jackson-jq) java library.

## Build

```
gradle clean install
```

## Install

```
cp build/lib/jq-json-logfilter-X.X.X.jar $RDECKBASE/libext
```

## How to use

- Add a Log filter to the workflow step what has a json output string

![Add Filter](examples/example1.png)

More information about the available jq Filters [here](https://github.com/eiiches/jackson-jq#implementation-status-and-current-limitations)


- Use the context variables on the next steps

![Add Filter](examples/example2.png)

![Add Filter](examples/example3.png)

91 changes: 91 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
plugins {
id 'groovy'
id 'java'
id 'pl.allegro.tech.build.axion-release' version '1.7.1'
}

group 'com.rundeck.plugin'

ext.rundeckVersion='3.0.6-20180917'
defaultTasks 'clean','build'
apply plugin: 'java'
apply plugin: 'idea'

sourceCompatibility = 1.8
ext.rundeckPluginVersion= '1.2'

scmVersion {
ignoreUncommittedChanges = false
tag {
prefix = ''
versionSeparator = ''
def origDeserialize=deserialize
//apend .0 to satisfy semver if the tag version is only X.Y
deserialize = { config, position, tagName ->
def orig = origDeserialize(config, position, tagName)
if (orig.split('\\.').length < 3) {
orig += ".0"
}
orig
}
}
}
project.version = scmVersion.version

/**
* Set this to a comma-separated list of full classnames of your implemented Rundeck
* plugins.
*/
ext.pluginClassNames='com.rundeck.plugins.logging.JsonLogFilter'


sourceCompatibility = 1.8

repositories {
mavenCentral()
}

configurations{
//declare custom pluginLibs configuration to include only libs for this plugin
pluginLibs

//declare compile to extend from pluginLibs so it inherits the dependencies
compile{
extendsFrom pluginLibs
}
}


dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.11'

compile group: 'org.rundeck', name: 'rundeck-core', version: rundeckVersion
pluginLibs group: 'net.thisptr', name: 'jackson-jq', version: '0.0.9'


testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
}


// task to copy plugin libs to output/lib dir
task copyToLib(type: Copy) {
into "$buildDir/output/lib"
from configurations.pluginLibs
}


jar {
from "$buildDir/output"
manifest {
def libList = configurations.pluginLibs.collect{'lib/'+it.name}.join(' ')
attributes 'Rundeck-Plugin-Classnames': pluginClassNames
attributes 'Rundeck-Plugin-File-Version': version
attributes 'Rundeck-Plugin-Version': rundeckPluginVersion, 'Rundeck-Plugin-Archive': 'true'
attributes 'Rundeck-Plugin-Libs': "${libList}"
attributes 'Main-Class': "io.github.valfadeev.rundeck.plugin.vault.VaultStoragePlugin"
attributes 'Class-Path': "${libList} lib/rundeck-core-${rundeckVersion}.jar"
}
}
//set jar task to depend on copyToLib
jar.dependsOn(copyToLib)
Binary file added examples/example1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/example2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/example3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions examples/test-json-array.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<joblist>
<job>
<defaultTab>summary</defaultTab>
<description></description>
<executionEnabled>true</executionEnabled>
<group>JSON</group>
<loglevel>INFO</loglevel>
<name>test-json-array</name>
<nodeFilterEditable>false</nodeFilterEditable>
<scheduleEnabled>true</scheduleEnabled>
<sequence keepgoing='false' strategy='node-first'>
<command>
<plugins>
<LogFilter type='json-mapper'>
<config>
<filter>.[]</filter>
<logData>true</logData>
<prefix>data</prefix>
</config>
</LogFilter>
</plugins>
<script><![CDATA[cat <<-END
{
"result": {
"parent": {
"link": "https://rundeck-test/api",
"value": "123345"
},
"user": "rundeck",
"created_by": {
"link": "https://rundeck-test/user/1",
"value": "123345"
},
"created_date": "2018-10-03 19:17:54",
"status": "1",
"closed_date": "2018-10-03 19:46:49"
}
}
END]]></script>
<scriptargs />
</command>
<command>
<exec>echo ${data.created_date}</exec>
</command>
</sequence>
</job>
</joblist>
47 changes: 47 additions & 0 deletions examples/test-json-simple.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<joblist>
<job>
<defaultTab>summary</defaultTab>
<description></description>
<executionEnabled>true</executionEnabled>
<group>JSON</group>
<loglevel>INFO</loglevel>
<name>test-json-simple</name>
<nodeFilterEditable>false</nodeFilterEditable>
<scheduleEnabled>true</scheduleEnabled>
<sequence keepgoing='false' strategy='node-first'>
<command>
<plugins>
<LogFilter type='json-mapper'>
<config>
<filter>.</filter>
<logData>true</logData>
<prefix>data</prefix>
</config>
</LogFilter>
</plugins>
<script><![CDATA[cat <<-END
{
"result": {
"parent": {
"link": "https://rundeck-test/api",
"value": "123345"
},
"user": "rundeck",
"created_by": {
"link": "https://rundeck-test/user/1",
"value": "123345"
},
"created_date": "2018-10-03 19:17:54",
"status": "1",
"closed_date": "2018-10-03 19:46:49"
}
}
END]]></script>
<scriptargs />
</command>
<command>
<exec>echo ${data.result.created_date}</exec>
</command>
</sequence>
</job>
</joblist>
165 changes: 165 additions & 0 deletions src/main/groovy/com/rundeck/plugins/logging/JsonLogFilter.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package com.rundeck.plugins.logging

import com.dtolabs.rundeck.core.execution.workflow.OutputContext
import com.dtolabs.rundeck.core.logging.LogEventControl
import com.dtolabs.rundeck.core.logging.LogLevel
import com.dtolabs.rundeck.core.logging.PluginLoggingContext
import com.dtolabs.rundeck.core.plugins.Plugin
import com.dtolabs.rundeck.plugins.descriptions.PluginDescription
import com.dtolabs.rundeck.plugins.descriptions.PluginProperty
import com.dtolabs.rundeck.plugins.logging.LogFilterPlugin
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.JsonNodeType
import net.thisptr.jackson.jq.JsonQuery
import net.thisptr.jackson.jq.Scope

@Plugin(name = JsonLogFilter.PROVIDER_NAME, service = 'LogFilter')
@PluginDescription(title = 'JSON jq key/value mapper',
description = 'Map a json logoutput on key/value to context data using jq filters')
class JsonLogFilter implements LogFilterPlugin{
public static final String PROVIDER_NAME = 'json-mapper'

@PluginProperty(
title = 'jq Filter',
description = '''Add a jq Filter''',
defaultValue=".",
required=true

)
String filter

@PluginProperty(
title = 'Prefix',
description = '''(Optional) Result prefix''',
defaultValue="result"
)
String prefix

@PluginProperty(
title = 'Log Data',
description = '''If true, log the captured data''',
defaultValue = 'false'
)
Boolean logData

private StringBuffer buffer;
OutputContext outputContext
Map<String, String> allData
private ObjectMapper mapper


@Override
void init(final PluginLoggingContext context) {
outputContext = context.getOutputContext()
buffer = new StringBuffer()
mapper = new ObjectMapper()
allData = [:]

}

@Override
void handleEvent(final PluginLoggingContext context, final LogEventControl event) {
if (event.eventType == 'log' && event.loglevel == LogLevel.NORMAL && event.message?.length() > 0) {
buffer.append(event.message)
}

}

@Override
void complete(final PluginLoggingContext context) {

if(buffer.size()>0) {
//apply json transform
this.processJson(context)

if (logData) {
context.log(
2,
mapper.writeValueAsString(allData),
[
'content-data-type' : 'application/json',
'content-meta:table-title': 'Key Value Data: Results'
]
)
}
}
}

void processJson(final PluginLoggingContext context){

try{

Scope rootScope = Scope.newEmptyScope()
rootScope.loadFunctions(Scope.class.getClassLoader())
JsonQuery q = JsonQuery.compile(filter)
JsonNode inData = mapper.readTree(buffer.toString());

List<JsonNode> out = q.apply(rootScope, inData)

out.each {it->
//process object
if(it.getNodeType()==JsonNodeType.OBJECT){
def map = it.fields()
for (Map.Entry<String, JsonNode> entry : map) {
this.iterateJsonObject(entry)
}
}else{
if(it.getNodeType()==JsonNodeType.ARRAY){
this.iterateArray(it.elements())
}else{
allData.put(prefix, it.toString())
}
}
}

outputContext.addOutput("data",allData)

}catch(Exception exception){
context.log(0, "[error] cannot map the json output: " + exception.message)
}
}

def iterateArray(def list){

Integer i=0
list.each{it->
if(it.getNodeType() == JsonNodeType.STRING){
allData.put(prefix +"."+ i.toString(),it.textValue())
}else{
allData.put(prefix +"."+ i.toString(),it.toString())
}

i++
}
}

def iterateJsonObject(Map.Entry<String, JsonNode> entry, String path=""){

def key = entry.getKey()
def value = entry.getValue()
def newPath

if(!path){
newPath=key
}else{
newPath=path + "."+ key
}

if(value.getNodeType()== JsonNodeType.OBJECT){
for (Map.Entry<String, JsonNode> subKey : entry.getValue().fields()) {
iterateJsonObject(subKey, newPath)
}
}else{
def extractValue
if(value.getNodeType() == JsonNodeType.STRING){
extractValue=value.textValue()
}else{
extractValue = value.toString()
}

allData.put(newPath,extractValue)
}
}

}
Loading

0 comments on commit da36877

Please sign in to comment.