Skip to content

Commit

Permalink
Merge pull request getredash#139 from erans/master
Browse files Browse the repository at this point in the history
Added support for running scripts as queries
  • Loading branch information
arikfr committed Mar 18, 2014
2 parents eac6b31 + 642a2b6 commit 09257fa
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 1 deletion.
6 changes: 6 additions & 0 deletions data/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ def start_workers(self, workers_count, connection_type, connection_string):
from redash.data import query_runner_bigquery
connection_params = json.loads(connection_string)
runner = query_runner_bigquery.bigquery(connection_params)
elif connection_type == 'script':
from redash.data import query_runner_script
runner = query_runner_script.script(connection_string)
elif connection_type == 'url':
from redash.data import query_runner_url
runner = query_runner_url.url(connection_string)
else:
from redash.data import query_runner
runner = query_runner.redshift(connection_string)
Expand Down
48 changes: 48 additions & 0 deletions data/query_runner_script.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import json
import logging
import sys
import os
import subprocess

# We use subprocess.check_output because we are lazy.
# If someone will really want to run this on Python < 2.7 they can easily update the code to run
# Popen, check the retcodes and other things and read the standard output to a variable.
if not "check_output" in subprocess.__dict__:
print "ERROR: This runner uses subprocess.check_output function which exists in Python 2.7"

def script(connection_string):

def query_runner(query):
try:
json_data = None
error = None

# Poor man's protection against running scripts from output the scripts directory
if connection_string.find("../") > -1:
return None, "Scripts can only be run from the configured scripts directory"

query = query.strip()

script = os.path.join(connection_string, query)
if not os.path.exists(script):
return None, "Script '%s' not found in script directory" % query

output = subprocess.check_output(script, shell=False)
if output != None:
output = output.strip()
if output != "":
return output, None

error = "Error reading output"
except subprocess.CalledProcessError as e:
return None, str(e)
except KeyboardInterrupt:
error = "Query cancelled by user."
json_data = None
except Exception as e:
raise sys.exc_info()[1], None, sys.exc_info()[2]

return json_data, error

query_runner.annotate_query = False
return query_runner
45 changes: 45 additions & 0 deletions data/query_runner_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import json
import logging
import sys
import os
import urllib2

def url(connection_string):

def query_runner(query):
base_url = connection_string

try:
json_data = None
error = None

query = query.strip()

if base_url is not None and base_url != "":
if query.find("://") > -1:
return None, "Accepting only relative URLs to '%s'" % base_url

if base_url is None:
base_url = ""

url = base_url + query

json_data = urllib2.urlopen(url).read().strip()

if not json_data:
error = "Error reading data from '%s'" % url

return json_data, error

except urllib2.URLError as e:
return None, str(e)
except KeyboardInterrupt:
error = "Query cancelled by user."
json_data = None
except Exception as e:
raise sys.exc_info()[1], None, sys.exc_info()[2]

return json_data, error

query_runner.annotate_query = False
return query_runner
7 changes: 6 additions & 1 deletion settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,19 @@ def parse_boolean(str):

NAME = os.environ.get('REDASH_NAME', 're:dash')

# "pg", "graphite" or "mysql"
# "pg", "graphite", "mysql", "bigquery" or "script"
CONNECTION_ADAPTER = os.environ.get("REDASH_CONNECTION_ADAPTER", "pg")
# Connection string for the database that is used to run queries against. Examples:
# -- mysql: CONNECTION_STRING = "Server=;User=;Pwd=;Database="
# -- pg: CONNECTION_STRING = "user= password= host= port=5439 dbname="
# -- graphite: CONNECTION_STRING = {"url": "https://graphite.yourcompany.com", "auth": ["user", "password"], "verify": true}
# -- bigquery: CONNECTION_STRING = {"serviceAccount" : "43242343247-fjdfakljr3r2@developer.gserviceaccount.com", "privateKey" : "/somewhere/23fjkfjdsfj21312-privatekey.p12", "projectId" : "myproject-123" }
# to obtain bigquery credentials follow the guidelines at https://developers.google.com/bigquery/authorization#service-accounts
# -- script: CONNECTION_STRING = "PATH TO ALL SCRIPTS" (.i.e /home/user/redash_scripts/)
# all scripts must be have the executable flag set and reside in the path configured in CONNECTION_STRING.
# The output of the scripts must be in the output format defined here: <TODO: Add link to documentation of output format>
# -- url: CONNECTION_STRING = "base URL" (i.e. http://myserver/somewhere)
# If CONNECTION_STRING is set, the query should be a relative URL. If it is not set a full URL can be used
CONNECTION_STRING = os.environ.get("REDASH_CONNECTION_STRING", "user= password= host= port=5439 dbname=")

# Connection settings for re:dash's own database (where we store the queries, results, etc)
Expand Down

0 comments on commit 09257fa

Please sign in to comment.