def test__load_task_wrong_input_task_args(self, mock_isfile, mock_safe_load): mock_safe_load.side_effect = yaml.ParserError("foo") mock_isfile.side_effect = (True, False) # use real file to avoid mocking open task_file = __file__ e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, self.real_api, task_file, raw_args="{'test': {}") self.assertEqual("Invalid --task-args passed:\n\n\t Value has to be " "YAML or JSON. Details:\n\nfoo", e.format_message()) mock_safe_load.assert_called_once_with("{'test': {}") # the case #2 mock_safe_load.reset_mock() mock_isfile.side_effect = (True, False) e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, self.real_api, task_file, raw_args="[]") self.assertEqual("Invalid --task-args passed:\n\n\t Value has to be " "YAML or JSON. Details:\n\nfoo", e.format_message()) mock_safe_load.assert_called_once_with("[]") # the case #3 mock_safe_load.reset_mock() mock_isfile.side_effect = (True, False) e = self.assertRaises(task.FailedToLoadTask, self.task._load_and_validate_task, self.real_api, task_file, raw_args="foo") self.assertEqual("Invalid --task-args passed:\n\n\t Value has to be " "YAML or JSON. Details:\n\nfoo", e.format_message()) mock_safe_load.assert_called_once_with("foo")
def _load_and_validate_task(self, api, task_file, args_file=None, raw_args=None): """Load, render and validate tasks template from file with passed args. :param task_file: Path to file with input task :param raw_args: JSON or YAML representation of dict with args that will be used to render input task with jinja2 :param args_file: Path to file with JSON or YAML representation of dict, that will be used to render input with jinja2. If both specified task_args and task_args_file they will be merged. raw_args has bigger priority so it will update values from args_file. :returns: Str with loaded and rendered task """ print(cliutils.make_header("Preparing input task")) try: input_task = open(task_file).read() except IOError as err: raise FailedToLoadTask(source="--task", msg="Error reading %s: %s" % (task_file, err)) task_dir = os.path.expanduser(os.path.dirname(task_file)) or "./" task_args = {} if args_file: try: task_args.update(yaml.safe_load(open(args_file).read())) except yaml.ParserError as e: raise FailedToLoadTask( source="--task-args-file", msg="File '%s' has to be YAML or JSON. Details:\n\n%s" % (args_file, e)) except IOError as err: raise FailedToLoadTask(source="--task-args-file", msg="Error reading %s: %s" % (args_file, err)) if raw_args: try: data = yaml.safe_load(raw_args) if isinstance(data, (six.text_type, six.string_types)): raise yaml.ParserError("String '%s' doesn't look like a " "dictionary." % raw_args) task_args.update(data) except yaml.ParserError as e: args = [ keypair.split("=", 1) for keypair in raw_args.split(",") ] if len([a for a in args if len(a) != 1]) != len(args): raise FailedToLoadTask( source="--task-args", msg="Value has to be YAML or JSON. Details:\n\n%s" % e) else: task_args.update(dict(args)) try: rendered_task = api.task.render_template(task_template=input_task, template_dir=task_dir, **task_args) except Exception as e: raise FailedToLoadTask( source="--task", msg="Failed to render task template.\n\n%s" % e) print("Task is:\n%s\n" % rendered_task.strip()) try: parsed_task = yaml.safe_load(rendered_task) except Exception as e: raise FailedToLoadTask( source="--task", msg="Wrong format of rendered input task. It should be YAML or" " JSON. Details:\n\n%s" % e) print("Task syntax is correct :)") return parsed_task