def convert_script_to_dict(script_file, tokens): """Converts a JSON file to a config dict. Reads in a JSON file, swaps out any environment variables that have been used inside the JSON, and then returns a dictionary. Args: script_file: Path to the JSON/YAML file to import, or file instance. tokens: dictionary to pass to populate_with_tokens. Returns: <Dictonary of Config Data> Raises: kingpin.exceptions.InvalidScript """ filename = '' try: if type(script_file) in (str, unicode): filename = script_file instance = open(script_file) elif type(script_file) is file: filename = script_file.name instance = script_file else: filename = str(script_file) instance = script_file except IOError as e: raise exceptions.InvalidScript('Error reading script %s: %s' % (script_file, e)) log.debug('Reading %s' % filename) raw = instance.read() parsed = populate_with_tokens(raw, tokens) # If the file ends with .json, use demjson to read it. If it ends with # .yml/.yaml, use PyYAML. If neither, error. suffix = filename.split('.')[-1].strip().lower() try: if suffix == 'json': decoded = demjson.decode(parsed) elif suffix in ('yml', 'yaml'): decoded = yaml.safe_load(parsed) if decoded is None: raise exceptions.InvalidScript('Invalid YAML in `%s`' % filename) else: raise exceptions.InvalidScriptName('Invalid file extension: %s' % suffix) except demjson.JSONError as e: # demjson exceptions have `pretty_description()` method with # much more useful info. raise exceptions.InvalidScript('JSON in `%s` has an error: %s' % (filename, e.pretty_description())) return decoded
def validate(config): """Validates the JSON against our schemas. TODO: Support multiple schema versions Args: config: Dictionary of parsed JSON Returns: None: if all is well Raises: Execption if something went wrong. """ try: return jsonschema.validate(config, SCHEMA_1_0) except jsonschema.exceptions.ValidationError as e: raise exceptions.InvalidScript(e)
def test_init_with_errors(self): # Remote files are prohibited for now with self.assertRaises(exceptions.UnrecoverableActorFailure): misc.Macro('Unit Test', { 'macro': 'ftp://fail.test.json', 'tokens': {} }) # Remote file with bad URL with self.assertRaises(exceptions.UnrecoverableActorFailure): misc.Macro('Unit Test', { 'macro': 'http://fail.test.json', 'tokens': {} }) # Non-existent file with self.assertRaises(exceptions.UnrecoverableActorFailure): misc.Macro('Unit Test', { 'macro': 'dontcreatethis.json', 'tokens': {} }) # We don't want the rest of the tests failing on downloading this file. misc.Macro._get_macro = mock.Mock(return_value='unit-test-file') # Schema failure with mock.patch('kingpin.utils.convert_script_to_dict') as j2d: j2d.return_value = { 'desc': 'unit test', 'options': {} # `actor` keyword is missing } with self.assertRaises(exceptions.UnrecoverableActorFailure): misc.Macro('Unit Test', {'macro': 'test.json', 'tokens': {}}) # JSON syntax error with mock.patch('kingpin.utils.convert_script_to_dict') as j2d: j2d.side_effect = kingpin_exceptions.InvalidScript('Fail!') with self.assertRaises(exceptions.UnrecoverableActorFailure): misc.Macro('Unit Test', {'macro': 'test.json', 'tokens': {}})