Skip to content

Commit 42790b0

Browse files
authored
Merge pull request #1 from MediaMath/json_schema
Use JSON-Schema to validate task.
2 parents 0791267 + 8b713d2 commit 42790b0

File tree

14 files changed

+259
-20
lines changed

14 files changed

+259
-20
lines changed

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,3 @@ sample/
1919
# IntelliJ IDEs
2020
.idea/
2121

22-
# LambdaCron config
23-
config/

README.md

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,30 +227,44 @@ Parameters:
227227
* **--directory (-d)**: Path to directory that contains tasks definitions (string)
228228
* **--aws-profile (-a)**: AWS profile to use from aws-cli (string) (optional)
229229
230+
#### validate
231+
232+
Validate a tasks checking if they match with the schema. It can validate a task
233+
from a file or a set of tasks in a directory.
234+
235+
Parameters:
236+
237+
* **--task-file (-t)**: File that contains a task definition.
238+
* **--task-directory (-d)**: Directory with a set of files with taqsks definitions.
239+
230240
231241
## Tasks
232242
233-
Tasks are defined in YAML files, each task in an independent file. The keys that
234-
must contains every task are:
243+
Tasks are defined in YAML files, each task in an independent file. Task must follow
244+
the json schema provided in this repo: [schema](./schema.json).
245+
246+
All tasks must contains following keys and values:
235247
236248
* **name**: task name
237249
* **expression**: crontab expression
238-
* **type**: task type (**queue** | **lambda** | **http**)
239250
* **task**: task definition (customized for each type of tasks)
240251
252+
For each kind of task there a set of specific keys a values to set. Described bellow.
253+
241254
### Queue task
242255
243256
It sends a message to a AWS SQS queue.
244257
The task definition must contains following keys:
245258
259+
* **type**: *queue*
246260
* **QueueName**: Name of the queue (string)
247261
* **MessageBody**: Message to be sent (YAML/JSON)
248262
249263
``` yaml
250264
name: 'Send performance report every morning'
251265
expression: '0 9 * * *'
252-
type: 'queue'
253266
task:
267+
type: 'queue'
254268
QueueName: 'my-scheduler-queue'
255269
MessageBody:
256270
name: 'Performance report'
@@ -268,14 +282,15 @@ All parameters of the function will be supported soon.
268282
It invokes an AWS lambda functions.
269283
The task definition must contains following keys
270284
285+
* **type**: *lambda*
271286
* **FunctionName**: Name of the lambda function to invoke (string)
272287
* **InvokeArgs**: arguments to send (YAML/JSON)
273288
274289
``` yaml
275290
name: 'Run ETL process every hour'
276291
expression: '0 * * * *'
277-
type: 'lambda'
278292
task:
293+
type: 'lambda'
279294
FunctionName: 'run-etl-process-prod'
280295
InvokeArgs:
281296
source: 's3://my-data-source/performance'
@@ -289,14 +304,15 @@ Function is invoked using [boto3 Lambda.Client.invoke_async](http://boto3.readth
289304
It send and HTTP request (GET or POST).
290305
The task definition must contains following keys:
291306
307+
* **type**: *http*
292308
* **method**: http method (get | post)
293309
* **request**: YAML with parameters to send for the selected method using [Requests](http://docs.python-requests.org/en/master/)
294310
295311
``` yaml
296312
name: 'helth check every hour'
297313
expression: '0 * * * *'
298-
type: 'http'
299314
task:
315+
type: 'http'
300316
method: 'get'
301317
request:
302318
url: 'http://helthcheck.my-domain.com'
@@ -361,9 +377,10 @@ Create your first environment (called 'test') with default settings:
361377
$ bin/lambda-cron create --environment=test --create-bucket
362378
```
363379

364-
If you want to set some custom settings create the setting file:
380+
If you want to set some custom settings create the setting file in the home
381+
directory of the user is running the tool.
365382

366-
* config/cli.yml
383+
* ~/lambdacron.yml
367384

368385
For help:
369386

cli/config_cli.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ def get_project_root_directory():
1010

1111

1212
def get_cli_config_file_path():
13-
return os.path.join(get_project_root_directory(), 'config/cli.yml')
13+
return os.path.abspath('~/.lambdacron.yml')
14+
15+
16+
def get_jsonschema_file_path():
17+
return os.path.join(get_project_root_directory(), 'schema.json')
1418

1519

1620
def load_config():

cli/lambda_cron_cli.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from config_cli import ConfigCli
1212
import config_cli
1313
import yaml
14+
import json
15+
import jsonschema
1416

1517

1618
def check_arg(args=None):
@@ -48,6 +50,10 @@ def check_arg(args=None):
4850
deploy_command.add_argument('-d', '--directory', required=True)
4951
deploy_command.add_argument('-a', '--aws-profile', default=None, dest='aws_profile')
5052

53+
deploy_command = commands_parser.add_parser('validate')
54+
deploy_command.add_argument('-t', '--task-file', required=False, dest='task_file')
55+
deploy_command.add_argument('-d', '--task-directory', required=False, dest='task_directory')
56+
5157
return parser.parse_args(args)
5258

5359

@@ -74,7 +80,8 @@ class LambdaCronCLI:
7480
def __init__(self, cli_instructions):
7581
self.cli = cli_instructions
7682
self.timestamp = int(round(time.time() * 1000))
77-
self.config = ConfigCli(self.cli.environment)
83+
if 'environment' in self.cli:
84+
self.config = ConfigCli(self.cli.environment)
7885

7986
def get_tmp_directory(self):
8087
return '/tmp/LambdaCron-{environment}'.format(environment=self.cli.environment)
@@ -306,6 +313,37 @@ def upload_tasks(self):
306313
]
307314
self.exec_aws_command(delete_stack_command)
308315

316+
def validate_task(self, schema, task_file_name):
317+
try:
318+
with open(task_file_name, 'r') as task_file:
319+
task = yaml.load(task_file)
320+
jsonschema.validate(task, schema)
321+
except Exception, ex:
322+
raise ex
323+
324+
def validate(self):
325+
try:
326+
with open(config_cli.get_jsonschema_file_path(), 'r') as schema_file:
327+
schema = json.load(schema_file)
328+
329+
if self.cli.task_file:
330+
self.validate_task(schema, self.cli.task_file)
331+
332+
if self.cli.task_directory:
333+
all_yml_files = [os.path.join(dirpath, f)
334+
for dirpath, dirnames, files in os.walk(self.cli.task_directory)
335+
for f in files if f.endswith('.yml')]
336+
print all_yml_files
337+
for file_name in all_yml_files:
338+
print file_name
339+
self.validate_task(schema, file_name)
340+
except jsonschema.exceptions.ValidationError, ex:
341+
print("Validation failed! Validation error in task: {}".format(ex.message))
342+
sys.exit(1)
343+
except Exception, ex:
344+
raise ex
345+
print 'Validation success!'
346+
309347
def run(self):
310348
command_method = getattr(self, self.cli.command.replace('-', '_'))
311349
command_method()

lambda_cron/lib/task_runner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def __init__(self, cron_checker):
1414

1515
def run(self, task):
1616
if self.cron_checker.should_run(task['expression']):
17-
command_method = getattr(self, "get_{}_task_runner".format(task['type'].lower()))
17+
command_method = getattr(self, "get_{}_task_runner".format(task['task']['type'].lower()))
1818
response = command_method(task['task']).run()
1919
logger.info('Task executed!')
2020
logger.info(response)

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
croniter==0.3.12
22
python-dateutil==2.5.3
33
PyYAML==3.12
4-
requests==2.12.3
4+
requests==2.12.3
5+
jsonschema==2.5.1

schema.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"title": "LambdaCron v0.0.1",
4+
"description": "JSON schema for LambdaCron tasks",
5+
"properties": {
6+
"name": {
7+
"type": "string"
8+
},
9+
"expression": {
10+
"type": "string",
11+
"pattern": "(\\*|[0-5]?[0-9]|\\*\/[0-9]+)\\s+(\\*|1?[0-9]|2[0-3]|\\*\/[0-9]+)\\s+(\\*|[1-2]?[0-9]|3[0-1]|\\*\/[0-9]+)\\s+(\\*|[0-9]|1[0-2]|\\*\/[0-9]+|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\\s+(\\*\/[0-9]+|\\*|[0-7]|sun|mon|tue|wed|thu|fri|sat)\\s*(\\*\/[0-9]+|\\*|[0-9]+)?"
12+
},
13+
"task": {
14+
"oneOf" : [
15+
{
16+
"properties": {
17+
"type": { "type": "string", "enum": ["queue"] },
18+
"QueueName": { "type": "string" },
19+
"MessageBody": { "type": "object" }
20+
},
21+
"required": ["type", "QueueName", "MessageBody"]
22+
},
23+
{
24+
"properties": {
25+
"type": { "type": "string", "enum": ["lambda"] },
26+
"FunctionName": { "type": "string" },
27+
"InvokeArgs": { "type": "object" }
28+
},
29+
"required": ["type", "FunctionName", "InvokeArgs"]
30+
},
31+
{
32+
"properties": {
33+
"type": { "type": "string", "enum": ["http"] },
34+
"method": { "type": "string", "enum": ["GET", "get", "Get", "POST", "post", "Post"]},
35+
"request": { "type": "object" }
36+
},
37+
"required": ["type", "method", "request"]
38+
}
39+
]
40+
}
41+
},
42+
"required": ["name", "expression", "task"]
43+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: 'test queue task'
2+
expression: '0 a * * *'
3+
task:
4+
type: 'queue'
5+
QueueName: 'test-queue'
6+
MessageBody:
7+
message_key_1: 'message_value_1'
8+
message_key_2: ['message_value_2']
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: 'test http get task'
2+
expression: '0 11 * * *'
3+
task:
4+
type: 'http'
5+
method: 'GET'
6+
request:
7+
url: 'http://lambda-cron.com/tests'
8+
params:
9+
param_1: 'param_value_1'
10+
param_2: 2
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: 'test http post task'
2+
expression: '0 11 * * *'
3+
task:
4+
type: 'http'
5+
method: 'POST'
6+
request:
7+
url: 'http://lambda-cron.com/tests'
8+
data:
9+
post_param_1: 'param_value_1'
10+
post_param_2: 2
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: 'test lambda task'
2+
expression: '0 11 * * *'
3+
task:
4+
type: 'lambda'
5+
FunctionName: 'testing-function'
6+
InvokeArgs:
7+
argument_1: 'value_argument_1'
8+
argument_2: 2
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: 'test queue task'
2+
expression: '0 11 * * *'
3+
task:
4+
type: 'queue'
5+
QueueName: 'test-queue'
6+
MessageBody:
7+
message_key_1: 'message_value_1'
8+
message_key_2: ['message_value_2']

0 commit comments

Comments
 (0)