mirror of https://github.com/ansible/ansible.git
Adding a data parsing class for v2
parent
c86851be2c
commit
7cb489eca3
@ -0,0 +1,118 @@
|
||||
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__all__ = [
|
||||
'YAML_SYNTAX_ERROR',
|
||||
'YAML_POSITION_DETAILS',
|
||||
'YAML_COMMON_DICT_ERROR',
|
||||
'YAML_COMMON_UNQUOTED_VARIABLE_ERROR',
|
||||
'YAML_COMMON_UNQUOTED_COLON_ERROR',
|
||||
'YAML_COMMON_PARTIALLY_QUOTED_LINE_ERROR',
|
||||
'YAML_COMMON_UNBALANCED_QUOTES_ERROR',
|
||||
]
|
||||
|
||||
YAML_SYNTAX_ERROR = """\
|
||||
Syntax Error while loading YAML.
|
||||
"""
|
||||
|
||||
YAML_POSITION_DETAILS = """\
|
||||
The error appears to have been in '%s': line %s, column %s,
|
||||
but may actually be before there depending on the exact syntax problem.
|
||||
"""
|
||||
|
||||
YAML_COMMON_DICT_ERROR = """\
|
||||
This one looks easy to fix. YAML thought it was looking for the start of a
|
||||
hash/dictionary and was confused to see a second "{". Most likely this was
|
||||
meant to be an ansible template evaluation instead, so we have to give the
|
||||
parser a small hint that we wanted a string instead. The solution here is to
|
||||
just quote the entire value.
|
||||
|
||||
For instance, if the original line was:
|
||||
|
||||
app_path: {{ base_path }}/foo
|
||||
|
||||
It should be written as:
|
||||
|
||||
app_path: "{{ base_path }}/foo"
|
||||
"""
|
||||
|
||||
YAML_COMMON_UNQUOTED_VARIABLE_ERROR = """\
|
||||
We could be wrong, but this one looks like it might be an issue with
|
||||
missing quotes. Always quote template expression brackets when they
|
||||
start a value. For instance:
|
||||
|
||||
with_items:
|
||||
- {{ foo }}
|
||||
|
||||
Should be written as:
|
||||
|
||||
with_items:
|
||||
- "{{ foo }}"
|
||||
"""
|
||||
|
||||
YAML_COMMON_UNQUOTED_COLON_ERROR = """\
|
||||
This one looks easy to fix. There seems to be an extra unquoted colon in the line
|
||||
and this is confusing the parser. It was only expecting to find one free
|
||||
colon. The solution is just add some quotes around the colon, or quote the
|
||||
entire line after the first colon.
|
||||
|
||||
For instance, if the original line was:
|
||||
|
||||
copy: src=file.txt dest=/path/filename:with_colon.txt
|
||||
|
||||
It can be written as:
|
||||
|
||||
copy: src=file.txt dest='/path/filename:with_colon.txt'
|
||||
|
||||
Or:
|
||||
|
||||
copy: 'src=file.txt dest=/path/filename:with_colon.txt'
|
||||
"""
|
||||
|
||||
YAML_COMMON_PARTIALLY_QUOTED_LINE_ERROR = """\
|
||||
This one looks easy to fix. It seems that there is a value started
|
||||
with a quote, and the YAML parser is expecting to see the line ended
|
||||
with the same kind of quote. For instance:
|
||||
|
||||
when: "ok" in result.stdout
|
||||
|
||||
Could be written as:
|
||||
|
||||
when: '"ok" in result.stdout'
|
||||
|
||||
Or equivalently:
|
||||
|
||||
when: "'ok' in result.stdout"
|
||||
"""
|
||||
|
||||
YAML_COMMON_UNBALANCED_QUOTES_ERROR = """\
|
||||
We could be wrong, but this one looks like it might be an issue with
|
||||
unbalanced quotes. If starting a value with a quote, make sure the
|
||||
line ends with the same set of quotes. For instance this arbitrary
|
||||
example:
|
||||
|
||||
foo: "bad" "wolf"
|
||||
|
||||
Could be written as:
|
||||
|
||||
foo: '"bad" "wolf"'
|
||||
"""
|
||||
|
@ -1,104 +0,0 @@
|
||||
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.compat.tests import unittest
|
||||
from ansible.errors import AnsibleInternalError, AnsibleParserError
|
||||
from ansible.parsing import load
|
||||
|
||||
import json
|
||||
import yaml
|
||||
|
||||
from io import FileIO
|
||||
|
||||
class MockFile(FileIO):
|
||||
|
||||
def __init__(self, ds, method='json'):
|
||||
self.ds = ds
|
||||
self.method = method
|
||||
|
||||
def read(self):
|
||||
if self.method == 'json':
|
||||
return json.dumps(self.ds)
|
||||
elif self.method == 'yaml':
|
||||
return yaml.dump(self.ds)
|
||||
elif self.method == 'fail':
|
||||
return """
|
||||
AAARGGGGH:
|
||||
*****
|
||||
THIS WON'T PARSE !!!
|
||||
NOOOOOOOOOOOOOOOOOO
|
||||
"""
|
||||
else:
|
||||
raise Exception("untestable serializer")
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
class TestGeneralParsing(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_parse_json_from_string(self):
|
||||
data = """
|
||||
{
|
||||
"asdf" : "1234",
|
||||
"jkl" : 5678
|
||||
}
|
||||
"""
|
||||
output = load(data)
|
||||
self.assertEqual(output['asdf'], '1234')
|
||||
self.assertEqual(output['jkl'], 5678)
|
||||
|
||||
def test_parse_json_from_file(self):
|
||||
output = load(MockFile(dict(a=1,b=2,c=3), 'json'))
|
||||
self.assertEqual(output, dict(a=1,b=2,c=3))
|
||||
|
||||
def test_parse_yaml_from_dict(self):
|
||||
data = """
|
||||
asdf: '1234'
|
||||
jkl: 5678
|
||||
"""
|
||||
output = load(data)
|
||||
self.assertEqual(output['asdf'], '1234')
|
||||
self.assertEqual(output['jkl'], 5678)
|
||||
|
||||
def test_parse_yaml_from_file(self):
|
||||
output = load(MockFile(dict(a=1,b=2,c=3),'yaml'))
|
||||
self.assertEqual(output, dict(a=1,b=2,c=3))
|
||||
|
||||
def test_parse_fail(self):
|
||||
data = """
|
||||
TEXT:
|
||||
***
|
||||
NOT VALID
|
||||
"""
|
||||
self.assertRaises(AnsibleParserError, load, data)
|
||||
|
||||
def test_parse_fail_from_file(self):
|
||||
self.assertRaises(AnsibleParserError, load, MockFile(None,'fail'))
|
||||
|
||||
def test_parse_fail_invalid_type(self):
|
||||
self.assertRaises(AnsibleInternalError, load, 3000)
|
||||
self.assertRaises(AnsibleInternalError, load, dict(a=1,b=2,c=3))
|
||||
|
@ -0,0 +1,64 @@
|
||||
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from yaml.scanner import ScannerError
|
||||
|
||||
from ansible.compat.tests import unittest
|
||||
from ansible.compat.tests.mock import patch
|
||||
from ansible.errors import AnsibleParserError
|
||||
|
||||
from ansible.parsing.yaml import DataLoader
|
||||
from ansible.parsing.yaml.objects import AnsibleMapping
|
||||
|
||||
class TestDataLoader(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# FIXME: need to add tests that utilize vault_password
|
||||
self._loader = DataLoader()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
@patch.object(DataLoader, '_get_file_contents')
|
||||
def test_parse_json_from_file(self, mock_def):
|
||||
mock_def.return_value = ("""{"a": 1, "b": 2, "c": 3}""", True)
|
||||
output = self._loader.load_from_file('dummy_json.txt')
|
||||
self.assertEqual(output, dict(a=1,b=2,c=3))
|
||||
|
||||
@patch.object(DataLoader, '_get_file_contents')
|
||||
def test_parse_yaml_from_file(self, mock_def):
|
||||
mock_def.return_value = ("""
|
||||
a: 1
|
||||
b: 2
|
||||
c: 3
|
||||
""", True)
|
||||
output = self._loader.load_from_file('dummy_yaml.txt')
|
||||
self.assertEqual(output, dict(a=1,b=2,c=3))
|
||||
|
||||
@patch.object(DataLoader, '_get_file_contents')
|
||||
def test_parse_fail_from_file(self, mock_def):
|
||||
mock_def.return_value = ("""
|
||||
TEXT:
|
||||
***
|
||||
NOT VALID
|
||||
""", True)
|
||||
self.assertRaises(AnsibleParserError, self._loader.load_from_file, 'dummy_yaml_bad.txt')
|
||||
|
@ -1,100 +0,0 @@
|
||||
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.compat.tests import unittest
|
||||
|
||||
from yaml.scanner import ScannerError
|
||||
|
||||
from ansible.parsing.yaml import safe_load
|
||||
from ansible.parsing.yaml.objects import AnsibleMapping
|
||||
|
||||
# a single dictionary instance
|
||||
data1 = '''---
|
||||
key: value
|
||||
'''
|
||||
|
||||
# multiple dictionary instances
|
||||
data2 = '''---
|
||||
- key1: value1
|
||||
- key2: value2
|
||||
|
||||
- key3: value3
|
||||
|
||||
|
||||
- key4: value4
|
||||
'''
|
||||
|
||||
# multiple dictionary instances with other nested
|
||||
# dictionaries contained within those
|
||||
data3 = '''---
|
||||
- key1:
|
||||
subkey1: subvalue1
|
||||
subkey2: subvalue2
|
||||
subkey3:
|
||||
subsubkey1: subsubvalue1
|
||||
- key2:
|
||||
subkey4: subvalue4
|
||||
- list1:
|
||||
- list1key1: list1value1
|
||||
list1key2: list1value2
|
||||
list1key3: list1value3
|
||||
'''
|
||||
|
||||
bad_data1 = '''---
|
||||
foo: bar
|
||||
bam: baz
|
||||
'''
|
||||
|
||||
class TestSafeLoad(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_safe_load_bad(self):
|
||||
# test the loading of bad yaml data
|
||||
self.assertRaises(ScannerError, safe_load, bad_data1)
|
||||
|
||||
def test_safe_load(self):
|
||||
# test basic dictionary
|
||||
res = safe_load(data1)
|
||||
self.assertEqual(type(res), AnsibleMapping)
|
||||
self.assertEqual(res._line_number, 2)
|
||||
|
||||
# test data with multiple dictionaries
|
||||
res = safe_load(data2)
|
||||
self.assertEqual(len(res), 4)
|
||||
self.assertEqual(res[0]._line_number, 2)
|
||||
self.assertEqual(res[1]._line_number, 3)
|
||||
self.assertEqual(res[2]._line_number, 5)
|
||||
self.assertEqual(res[3]._line_number, 8)
|
||||
|
||||
# test data with multiple sub-dictionaries
|
||||
res = safe_load(data3)
|
||||
self.assertEqual(len(res), 3)
|
||||
self.assertEqual(res[0]._line_number, 2)
|
||||
self.assertEqual(res[1]._line_number, 7)
|
||||
self.assertEqual(res[2]._line_number, 9)
|
||||
self.assertEqual(res[0]['key1']._line_number, 3)
|
||||
self.assertEqual(res[1]['key2']._line_number, 8)
|
||||
self.assertEqual(res[2]['list1'][0]._line_number, 10)
|
Loading…
Reference in New Issue