Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Commit

Permalink
Merge pull request #26 from dr-strangecode/combined_output
Browse files Browse the repository at this point in the history
Combined output
  • Loading branch information
dr-strangecode committed May 27, 2014
2 parents 9aec682 + d5cff80 commit 0ad1428
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 55 deletions.
29 changes: 21 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Expose a directory of bash scripts as an API.

Pyjojo now supports the use of alternative HTTP methods (defined in your script). To support this, we changed the previous GET calls to OPTIONS calls.

Output is now split on newlines and is an Array.

Output can now be combined via the 'output' jojo block argument. Default is 'split'.

## Tutorial

Create a directory to store the bash scripts, by default pyJoJo will be pointed at /srv/pyjojo.
Expand Down Expand Up @@ -34,9 +38,11 @@ Now, start up pyJoJo and hit it with curl:
You should see this as a response:

{
"retcode": 0,
"stderr": "",
"stdout": "echo'd text: hello world!\n"
"retcode": 0,
"stderr": [],
"stdout": [
"echo'd text: hello world!"
]
}

## Usage
Expand Down Expand Up @@ -81,8 +87,8 @@ Example block:
# param: secret2 - more sensitive stuff
# filtered_params: secret1, secret2
# tags: test, staging
# http_method: get|post|put|delete
# lock: false
# http_method: get
# lock: False
# -- jojo --

Fields:
Expand All @@ -95,10 +101,17 @@ Fields:
- format: filtered_params: item1 [,item2]
- **tags**: specifies a list of tags that you want displayed when querying pyjojo about scripts.
- format: tags: item1 [,item2]
- **http_method**: specifies the http method the script should respond to. Default is POST
- format: http_method: get|post|put|delete
- **http_method**: specifies the http method the script should respond to.
- format: http_method: get
- allowed_values: get|put|post|delete
- default: post
- **output**: specifies if the output should be 'split' into stderr and stdout or 'combined' into stdout.
- format: output: combined
- allowed_values: split|combined
- default: split
- **lock**: if true, only one instance of the script will be allowed to run
- format: lock: True|False
- format: lock: True
- default: False

### Script List

Expand Down
84 changes: 56 additions & 28 deletions pyjojo/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,55 +128,83 @@ def get(self, script_name):
""" run the script """

script = self.get_script(script_name, 'get')
retcode, stdout, stderr = yield gen.Task(script.execute, self.params)

self.finish({
"stdout": stdout,
"stderr": stderr,
"retcode": retcode
})

if script.output == 'combined':
retcode, stdout = yield gen.Task(script.execute, self.params)
self.finish({
"stdout": stdout,
"retcode": retcode
})
else:
retcode, stdout, stderr = yield gen.Task(script.execute, self.params)
self.finish({
"stdout": stdout,
"stderr": stderr,
"retcode": retcode
})

@asynchronous
@gen.engine
def delete(self, script_name):
""" run the script """

script = self.get_script(script_name, 'delete')
retcode, stdout, stderr = yield gen.Task(script.execute, self.params)

self.finish({
"stdout": stdout,
"stderr": stderr,
"retcode": retcode
})

if script.output == 'combined':
retcode, stdout = yield gen.Task(script.execute, self.params)
self.finish({
"stdout": stdout,
"retcode": retcode
})
else:
retcode, stdout, stderr = yield gen.Task(script.execute, self.params)
self.finish({
"stdout": stdout,
"stderr": stderr,
"retcode": retcode
})

@asynchronous
@gen.engine
def put(self, script_name):
""" run the script """

script = self.get_script(script_name, 'put')
retcode, stdout, stderr = yield gen.Task(script.execute, self.params)

self.finish({
"stdout": stdout,
"stderr": stderr,
"retcode": retcode
})

if script.output == 'combined':
retcode, stdout = yield gen.Task(script.execute, self.params)
self.finish({
"stdout": stdout,
"retcode": retcode
})
else:
retcode, stdout, stderr = yield gen.Task(script.execute, self.params)
self.finish({
"stdout": stdout,
"stderr": stderr,
"retcode": retcode
})

@asynchronous
@gen.engine
def post(self, script_name):
""" run the script """

script = self.get_script(script_name, 'post')
retcode, stdout, stderr = yield gen.Task(script.execute, self.params)

self.finish({
"stdout": stdout,
"stderr": stderr,
"retcode": retcode
})

if script.output == 'combined':
retcode, stdout = yield gen.Task(script.execute, self.params)
self.finish({
"stdout": stdout,
"retcode": retcode
})
else:
retcode, stdout, stderr = yield gen.Task(script.execute, self.params)
self.finish({
"stdout": stdout,
"stderr": stderr,
"retcode": retcode
})

def get_script(self, script_name, http_method):
script = self.settings['scripts'].get(script_name, None)
Expand Down
71 changes: 52 additions & 19 deletions pyjojo/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import os.path
import re
import subprocess

from tornado import gen
from tornado.process import Subprocess
Expand Down Expand Up @@ -33,7 +34,7 @@ def metadata(self):
class Script(object):
""" a single script in the directory """

def __init__(self, filename, name, description, params, filtered_params, tags, http_method, needs_lock):
def __init__(self, filename, name, description, params, filtered_params, tags, http_method, output, needs_lock):
self.lock = toro.Lock()
self.filename = filename
self.name = name
Expand All @@ -43,6 +44,7 @@ def __init__(self, filename, name, description, params, filtered_params, tags, h
self.tags = tags
self.http_method = http_method
self.needs_lock = needs_lock
self.output = output

def filter_params(self, params):
filtered_params = dict(params)
Expand All @@ -66,22 +68,38 @@ def execute(self, params, callback):
@gen.engine
def do_execute(self, params, callback):
env = self.create_env(params)

child = Subprocess(
self.filename,
env=env,
stdout=Subprocess.STREAM,
stderr=Subprocess.STREAM,
io_loop=IOLoop.instance()
)

retcode, stdout, stderr = yield [
gen.Task(child.set_exit_callback),
gen.Task(child.stdout.read_until_close),
gen.Task(child.stderr.read_until_close)
]

callback((child.returncode, stdout, stderr))
if self.output == 'combined':
child = Subprocess(
self.filename,
env=env,
stdout=Subprocess.STREAM,
stderr=subprocess.STDOUT,
io_loop=IOLoop.instance()
)

retcode, stdout = yield [
gen.Task(child.set_exit_callback),
gen.Task(child.stdout.read_until_close)
]

callback((child.returncode, stdout.split()))
else:
child = Subprocess(
self.filename,
env=env,
stdout=Subprocess.STREAM,
stderr=Subprocess.STREAM,
io_loop=IOLoop.instance()
)

retcode, stdout, stderr = yield [
gen.Task(child.set_exit_callback),
gen.Task(child.stdout.read_until_close),
gen.Task(child.stderr.read_until_close)
]

callback((child.returncode, stdout.split(), stderr.split()))

def create_env(self, input):
output = {}
Expand All @@ -102,6 +120,7 @@ def metadata(self):
"params": self.params,
"filtered_params": self.filtered_params,
"tags": self.tags,
"output": self.output,
"lock": self.needs_lock
}

Expand Down Expand Up @@ -144,6 +163,7 @@ def create_script(script_name, filename):
filtered_params = []
tags = []
http_method = 'post'
output = 'split'
lock = False

# warn the user if we can't execute this file
Expand Down Expand Up @@ -191,8 +211,21 @@ def create_script(script_name, filename):

# http_method
if in_block and key == "http_method":
http_method = value.lower()
continue
if value.lower() in ['get','post','put','delete']:
http_method = value.lower()
continue
else:
log.warn("unrecognized http_method type in jojo block: {0}".format(value.lower()))
continue

# output
if in_block and key == "output":
if value.lower() in ['split','combined']:
output = value.lower()
continue
else:
log.warn("unrecognized output type in jojo block: {0}".format(value.lower()))
continue

# param
if in_block and key == "param":
Expand Down Expand Up @@ -239,4 +272,4 @@ def create_script(script_name, filename):
log.error("file with filename {0} is missing an end block, Ignoring".format(filename))
return None

return Script(filename, script_name, description, params, filtered_params, tags, http_method, lock)
return Script(filename, script_name, description, params, filtered_params, tags, http_method, output, lock)

0 comments on commit 0ad1428

Please sign in to comment.