mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-09 23:00:02 -07:00
Making the switch to v2
This commit is contained in:
parent
8cf4452d48
commit
ce3ef7f4c1
486 changed files with 7948 additions and 9070 deletions
21
test/units/parsing/__init__.py
Normal file
21
test/units/parsing/__init__.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# (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
|
||||
|
90
test/units/parsing/test_data_loader.py
Normal file
90
test/units/parsing/test_data_loader.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
# (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 six import PY2
|
||||
from yaml.scanner import ScannerError
|
||||
|
||||
from ansible.compat.tests import unittest
|
||||
from ansible.compat.tests.mock import patch, mock_open
|
||||
from ansible.errors import AnsibleParserError
|
||||
|
||||
from ansible.parsing 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')
|
||||
|
||||
class TestDataLoaderWithVault(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._loader = DataLoader(vault_password='ansible')
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
@patch.multiple(DataLoader, path_exists=lambda s, x: True, is_file=lambda s, x: True)
|
||||
def test_parse_from_vault_1_1_file(self):
|
||||
vaulted_data = """$ANSIBLE_VAULT;1.1;AES256
|
||||
33343734386261666161626433386662623039356366656637303939306563376130623138626165
|
||||
6436333766346533353463636566313332623130383662340a393835656134633665333861393331
|
||||
37666233346464636263636530626332623035633135363732623332313534306438393366323966
|
||||
3135306561356164310a343937653834643433343734653137383339323330626437313562306630
|
||||
3035
|
||||
"""
|
||||
if PY2:
|
||||
builtins_name = '__builtin__'
|
||||
else:
|
||||
builtins_name = 'builtins'
|
||||
|
||||
with patch(builtins_name + '.open', mock_open(read_data=vaulted_data)):
|
||||
output = self._loader.load_from_file('dummy_vault.txt')
|
||||
self.assertEqual(output, dict(foo='bar'))
|
130
test/units/parsing/test_mod_args.py
Normal file
130
test/units/parsing/test_mod_args.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
# (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.parsing.mod_args import ModuleArgsParser
|
||||
from ansible.errors import AnsibleParserError
|
||||
|
||||
from ansible.compat.tests import unittest
|
||||
|
||||
class TestModArgsDwim(unittest.TestCase):
|
||||
|
||||
# TODO: add tests that construct ModuleArgsParser with a task reference
|
||||
# TODO: verify the AnsibleError raised on failure knows the task
|
||||
# and the task knows the line numbers
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def _debug(self, mod, args, to):
|
||||
print("RETURNED module = {0}".format(mod))
|
||||
print(" args = {0}".format(args))
|
||||
print(" to = {0}".format(to))
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_basic_shell(self):
|
||||
m = ModuleArgsParser(dict(shell='echo hi'))
|
||||
mod, args, to = m.parse()
|
||||
self._debug(mod, args, to)
|
||||
self.assertEqual(mod, 'command')
|
||||
self.assertEqual(args, dict(
|
||||
_raw_params = 'echo hi',
|
||||
_uses_shell = True,
|
||||
))
|
||||
self.assertIsNone(to)
|
||||
|
||||
def test_basic_command(self):
|
||||
m = ModuleArgsParser(dict(command='echo hi'))
|
||||
mod, args, to = m.parse()
|
||||
self._debug(mod, args, to)
|
||||
self.assertEqual(mod, 'command')
|
||||
self.assertEqual(args, dict(
|
||||
_raw_params = 'echo hi',
|
||||
))
|
||||
self.assertIsNone(to)
|
||||
|
||||
def test_shell_with_modifiers(self):
|
||||
m = ModuleArgsParser(dict(shell='/bin/foo creates=/tmp/baz removes=/tmp/bleep'))
|
||||
mod, args, to = m.parse()
|
||||
self._debug(mod, args, to)
|
||||
self.assertEqual(mod, 'command')
|
||||
self.assertEqual(args, dict(
|
||||
creates = '/tmp/baz',
|
||||
removes = '/tmp/bleep',
|
||||
_raw_params = '/bin/foo',
|
||||
_uses_shell = True,
|
||||
))
|
||||
self.assertIsNone(to)
|
||||
|
||||
def test_normal_usage(self):
|
||||
m = ModuleArgsParser(dict(copy='src=a dest=b'))
|
||||
mod, args, to = m.parse()
|
||||
self._debug(mod, args, to)
|
||||
self.assertEqual(mod, 'copy')
|
||||
self.assertEqual(args, dict(src='a', dest='b'))
|
||||
self.assertIsNone(to)
|
||||
|
||||
def test_complex_args(self):
|
||||
m = ModuleArgsParser(dict(copy=dict(src='a', dest='b')))
|
||||
mod, args, to = m.parse()
|
||||
self._debug(mod, args, to)
|
||||
self.assertEqual(mod, 'copy')
|
||||
self.assertEqual(args, dict(src='a', dest='b'))
|
||||
self.assertIsNone(to)
|
||||
|
||||
def test_action_with_complex(self):
|
||||
m = ModuleArgsParser(dict(action=dict(module='copy', src='a', dest='b')))
|
||||
mod, args, to = m.parse()
|
||||
self._debug(mod, args, to)
|
||||
self.assertEqual(mod, 'copy')
|
||||
self.assertEqual(args, dict(src='a', dest='b'))
|
||||
self.assertIsNone(to)
|
||||
|
||||
def test_action_with_complex_and_complex_args(self):
|
||||
m = ModuleArgsParser(dict(action=dict(module='copy', args=dict(src='a', dest='b'))))
|
||||
mod, args, to = m.parse()
|
||||
self._debug(mod, args, to)
|
||||
self.assertEqual(mod, 'copy')
|
||||
self.assertEqual(args, dict(src='a', dest='b'))
|
||||
self.assertIsNone(to)
|
||||
|
||||
def test_local_action_string(self):
|
||||
m = ModuleArgsParser(dict(local_action='copy src=a dest=b'))
|
||||
mod, args, to = m.parse()
|
||||
self._debug(mod, args, to)
|
||||
self.assertEqual(mod, 'copy')
|
||||
self.assertEqual(args, dict(src='a', dest='b'))
|
||||
self.assertIs(to, 'localhost')
|
||||
|
||||
def test_multiple_actions(self):
|
||||
m = ModuleArgsParser(dict(action='shell echo hi', local_action='shell echo hi'))
|
||||
self.assertRaises(AnsibleParserError, m.parse)
|
||||
|
||||
m = ModuleArgsParser(dict(action='shell echo hi', shell='echo hi'))
|
||||
self.assertRaises(AnsibleParserError, m.parse)
|
||||
|
||||
m = ModuleArgsParser(dict(local_action='shell echo hi', shell='echo hi'))
|
||||
self.assertRaises(AnsibleParserError, m.parse)
|
||||
|
||||
m = ModuleArgsParser(dict(ping='data=hi', shell='echo hi'))
|
||||
self.assertRaises(AnsibleParserError, m.parse)
|
||||
|
112
test/units/parsing/test_splitter.py
Normal file
112
test/units/parsing/test_splitter.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
# coding: utf-8
|
||||
# (c) 2015, Toshio Kuratomi <tkuratomi@ansible.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 nose import tools
|
||||
from ansible.compat.tests import unittest
|
||||
|
||||
from ansible.parsing.splitter import split_args, parse_kv
|
||||
|
||||
|
||||
# Tests using nose's test generators cannot use unittest base class.
|
||||
# http://nose.readthedocs.org/en/latest/writing_tests.html#test-generators
|
||||
class TestSplitter_Gen:
|
||||
SPLIT_DATA = (
|
||||
(u'a',
|
||||
[u'a'],
|
||||
{u'_raw_params': u'a'}),
|
||||
(u'a=b',
|
||||
[u'a=b'],
|
||||
{u'a': u'b'}),
|
||||
(u'a="foo bar"',
|
||||
[u'a="foo bar"'],
|
||||
{u'a': u'foo bar'}),
|
||||
(u'"foo bar baz"',
|
||||
[u'"foo bar baz"'],
|
||||
{u'_raw_params': '"foo bar baz"'}),
|
||||
(u'foo bar baz',
|
||||
[u'foo', u'bar', u'baz'],
|
||||
{u'_raw_params': u'foo bar baz'}),
|
||||
(u'a=b c="foo bar"',
|
||||
[u'a=b', u'c="foo bar"'],
|
||||
{u'a': u'b', u'c': u'foo bar'}),
|
||||
(u'a="echo \\"hello world\\"" b=bar',
|
||||
[u'a="echo \\"hello world\\""', u'b=bar'],
|
||||
{u'a': u'echo "hello world"', u'b': u'bar'}),
|
||||
(u'a="multi\nline"',
|
||||
[u'a="multi\nline"'],
|
||||
{u'a': u'multi\nline'}),
|
||||
(u'a="blank\n\nline"',
|
||||
[u'a="blank\n\nline"'],
|
||||
{u'a': u'blank\n\nline'}),
|
||||
(u'a="blank\n\n\nlines"',
|
||||
[u'a="blank\n\n\nlines"'],
|
||||
{u'a': u'blank\n\n\nlines'}),
|
||||
(u'a="a long\nmessage\\\nabout a thing\n"',
|
||||
[u'a="a long\nmessage\\\nabout a thing\n"'],
|
||||
{u'a': u'a long\nmessage\\\nabout a thing\n'}),
|
||||
(u'a="multiline\nmessage1\\\n" b="multiline\nmessage2\\\n"',
|
||||
[u'a="multiline\nmessage1\\\n"', u'b="multiline\nmessage2\\\n"'],
|
||||
{u'a': 'multiline\nmessage1\\\n', u'b': u'multiline\nmessage2\\\n'}),
|
||||
(u'a={{jinja}}',
|
||||
[u'a={{jinja}}'],
|
||||
{u'a': u'{{jinja}}'}),
|
||||
(u'a={{ jinja }}',
|
||||
[u'a={{ jinja }}'],
|
||||
{u'a': u'{{ jinja }}'}),
|
||||
(u'a="{{jinja}}"',
|
||||
[u'a="{{jinja}}"'],
|
||||
{u'a': u'{{jinja}}'}),
|
||||
(u'a={{ jinja }}{{jinja2}}',
|
||||
[u'a={{ jinja }}{{jinja2}}'],
|
||||
{u'a': u'{{ jinja }}{{jinja2}}'}),
|
||||
(u'a="{{ jinja }}{{jinja2}}"',
|
||||
[u'a="{{ jinja }}{{jinja2}}"'],
|
||||
{u'a': u'{{ jinja }}{{jinja2}}'}),
|
||||
(u'a={{jinja}} b={{jinja2}}',
|
||||
[u'a={{jinja}}', u'b={{jinja2}}'],
|
||||
{u'a': u'{{jinja}}', u'b': u'{{jinja2}}'}),
|
||||
(u'a="{{jinja}}\n" b="{{jinja2}}\n"',
|
||||
[u'a="{{jinja}}\n"', u'b="{{jinja2}}\n"'],
|
||||
{u'a': u'{{jinja}}\n', u'b': u'{{jinja2}}\n'}),
|
||||
(u'a="café eñyei"',
|
||||
[u'a="café eñyei"'],
|
||||
{u'a': u'café eñyei'}),
|
||||
(u'a=café b=eñyei',
|
||||
[u'a=café', u'b=eñyei'],
|
||||
{u'a': u'café', u'b': u'eñyei'}),
|
||||
)
|
||||
|
||||
def check_split_args(self, args, expected):
|
||||
tools.eq_(split_args(args), expected)
|
||||
|
||||
def test_split_args(self):
|
||||
for datapoint in self.SPLIT_DATA:
|
||||
yield self.check_split_args, datapoint[0], datapoint[1]
|
||||
|
||||
def check_parse_kv(self, args, expected):
|
||||
tools.eq_(parse_kv(args), expected)
|
||||
|
||||
def test_parse_kv(self):
|
||||
for datapoint in self.SPLIT_DATA:
|
||||
try:
|
||||
yield self.check_parse_kv, datapoint[0], datapoint[2]
|
||||
except: pass
|
21
test/units/parsing/vault/__init__.py
Normal file
21
test/units/parsing/vault/__init__.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# (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
|
||||
|
159
test/units/parsing/vault/test_vault.py
Normal file
159
test/units/parsing/vault/test_vault.py
Normal file
|
@ -0,0 +1,159 @@
|
|||
# (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
|
||||
|
||||
import getpass
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import tempfile
|
||||
import six
|
||||
|
||||
from binascii import unhexlify
|
||||
from binascii import hexlify
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from ansible.compat.tests import unittest
|
||||
from ansible.utils.unicode import to_bytes, to_unicode
|
||||
|
||||
from ansible import errors
|
||||
from ansible.parsing.vault import VaultLib
|
||||
|
||||
# Counter import fails for 2.0.1, requires >= 2.6.1 from pip
|
||||
try:
|
||||
from Crypto.Util import Counter
|
||||
HAS_COUNTER = True
|
||||
except ImportError:
|
||||
HAS_COUNTER = False
|
||||
|
||||
# KDF import fails for 2.0.1, requires >= 2.6.1 from pip
|
||||
try:
|
||||
from Crypto.Protocol.KDF import PBKDF2
|
||||
HAS_PBKDF2 = True
|
||||
except ImportError:
|
||||
HAS_PBKDF2 = False
|
||||
|
||||
# AES IMPORTS
|
||||
try:
|
||||
from Crypto.Cipher import AES as AES
|
||||
HAS_AES = True
|
||||
except ImportError:
|
||||
HAS_AES = False
|
||||
|
||||
class TestVaultLib(unittest.TestCase):
|
||||
|
||||
def test_methods_exist(self):
|
||||
v = VaultLib('ansible')
|
||||
slots = ['is_encrypted',
|
||||
'encrypt',
|
||||
'decrypt',
|
||||
'_add_header',
|
||||
'_split_header',]
|
||||
for slot in slots:
|
||||
assert hasattr(v, slot), "VaultLib is missing the %s method" % slot
|
||||
|
||||
def test_is_encrypted(self):
|
||||
v = VaultLib(None)
|
||||
assert not v.is_encrypted(u"foobar"), "encryption check on plaintext failed"
|
||||
data = u"$ANSIBLE_VAULT;9.9;TEST\n%s" % hexlify(b"ansible")
|
||||
assert v.is_encrypted(data), "encryption check on headered text failed"
|
||||
|
||||
def test_add_header(self):
|
||||
v = VaultLib('ansible')
|
||||
v.cipher_name = "TEST"
|
||||
sensitive_data = "ansible"
|
||||
data = v._add_header(sensitive_data)
|
||||
lines = data.split(b'\n')
|
||||
assert len(lines) > 1, "failed to properly add header"
|
||||
header = to_unicode(lines[0])
|
||||
assert header.endswith(';TEST'), "header does end with cipher name"
|
||||
header_parts = header.split(';')
|
||||
assert len(header_parts) == 3, "header has the wrong number of parts"
|
||||
assert header_parts[0] == '$ANSIBLE_VAULT', "header does not start with $ANSIBLE_VAULT"
|
||||
assert header_parts[1] == v.version, "header version is incorrect"
|
||||
assert header_parts[2] == 'TEST', "header does end with cipher name"
|
||||
|
||||
def test_split_header(self):
|
||||
v = VaultLib('ansible')
|
||||
data = b"$ANSIBLE_VAULT;9.9;TEST\nansible"
|
||||
rdata = v._split_header(data)
|
||||
lines = rdata.split(b'\n')
|
||||
assert lines[0] == b"ansible"
|
||||
assert v.cipher_name == 'TEST', "cipher name was not set"
|
||||
assert v.version == "9.9"
|
||||
|
||||
def test_encrypt_decrypt_aes(self):
|
||||
if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
|
||||
raise SkipTest
|
||||
v = VaultLib('ansible')
|
||||
v.cipher_name = u'AES'
|
||||
enc_data = v.encrypt("foobar")
|
||||
dec_data = v.decrypt(enc_data)
|
||||
assert enc_data != "foobar", "encryption failed"
|
||||
assert dec_data == "foobar", "decryption failed"
|
||||
|
||||
def test_encrypt_decrypt_aes256(self):
|
||||
if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
|
||||
raise SkipTest
|
||||
v = VaultLib('ansible')
|
||||
v.cipher_name = 'AES256'
|
||||
enc_data = v.encrypt("foobar")
|
||||
dec_data = v.decrypt(enc_data)
|
||||
assert enc_data != "foobar", "encryption failed"
|
||||
assert dec_data == "foobar", "decryption failed"
|
||||
|
||||
def test_encrypt_encrypted(self):
|
||||
if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
|
||||
raise SkipTest
|
||||
v = VaultLib('ansible')
|
||||
v.cipher_name = 'AES'
|
||||
data = "$ANSIBLE_VAULT;9.9;TEST\n%s" % hexlify(six.b("ansible"))
|
||||
error_hit = False
|
||||
try:
|
||||
enc_data = v.encrypt(data)
|
||||
except errors.AnsibleError as e:
|
||||
error_hit = True
|
||||
assert error_hit, "No error was thrown when trying to encrypt data with a header"
|
||||
|
||||
def test_decrypt_decrypted(self):
|
||||
if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
|
||||
raise SkipTest
|
||||
v = VaultLib('ansible')
|
||||
data = "ansible"
|
||||
error_hit = False
|
||||
try:
|
||||
dec_data = v.decrypt(data)
|
||||
except errors.AnsibleError as e:
|
||||
error_hit = True
|
||||
assert error_hit, "No error was thrown when trying to decrypt data without a header"
|
||||
|
||||
def test_cipher_not_set(self):
|
||||
# not setting the cipher should default to AES256
|
||||
if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
|
||||
raise SkipTest
|
||||
v = VaultLib('ansible')
|
||||
data = "ansible"
|
||||
error_hit = False
|
||||
try:
|
||||
enc_data = v.encrypt(data)
|
||||
except errors.AnsibleError as e:
|
||||
error_hit = True
|
||||
assert not error_hit, "An error was thrown when trying to encrypt data without the cipher set"
|
||||
assert v.cipher_name == "AES256", "cipher name is not set to AES256: %s" % v.cipher_name
|
214
test/units/parsing/vault/test_vault_editor.py
Normal file
214
test/units/parsing/vault/test_vault_editor.py
Normal file
|
@ -0,0 +1,214 @@
|
|||
# (c) 2014, James Tanner <tanner.jc@gmail.com>
|
||||
# (c) 2014, James Cammarata, <jcammarata@ansible.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
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import getpass
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import tempfile
|
||||
from binascii import unhexlify
|
||||
from binascii import hexlify
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from ansible.compat.tests import unittest
|
||||
from ansible.compat.tests.mock import patch
|
||||
from ansible.utils.unicode import to_bytes, to_unicode
|
||||
|
||||
from ansible import errors
|
||||
from ansible.parsing.vault import VaultLib
|
||||
from ansible.parsing.vault import VaultEditor
|
||||
|
||||
# Counter import fails for 2.0.1, requires >= 2.6.1 from pip
|
||||
try:
|
||||
from Crypto.Util import Counter
|
||||
HAS_COUNTER = True
|
||||
except ImportError:
|
||||
HAS_COUNTER = False
|
||||
|
||||
# KDF import fails for 2.0.1, requires >= 2.6.1 from pip
|
||||
try:
|
||||
from Crypto.Protocol.KDF import PBKDF2
|
||||
HAS_PBKDF2 = True
|
||||
except ImportError:
|
||||
HAS_PBKDF2 = False
|
||||
|
||||
# AES IMPORTS
|
||||
try:
|
||||
from Crypto.Cipher import AES as AES
|
||||
HAS_AES = True
|
||||
except ImportError:
|
||||
HAS_AES = False
|
||||
|
||||
v10_data = """$ANSIBLE_VAULT;1.0;AES
|
||||
53616c7465645f5fd0026926a2d415a28a2622116273fbc90e377225c12a347e1daf4456d36a77f9
|
||||
9ad98d59f61d06a4b66718d855f16fb7bdfe54d1ec8aeaa4d06c2dc1fa630ae1846a029877f0eeb1
|
||||
83c62ffb04c2512995e815de4b4d29ed"""
|
||||
|
||||
v11_data = """$ANSIBLE_VAULT;1.1;AES256
|
||||
62303130653266653331306264616235333735323636616539316433666463323964623162386137
|
||||
3961616263373033353631316333623566303532663065310a393036623466376263393961326530
|
||||
64336561613965383835646464623865663966323464653236343638373165343863623638316664
|
||||
3631633031323837340a396530313963373030343933616133393566366137363761373930663833
|
||||
3739"""
|
||||
|
||||
class TestVaultEditor(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_methods_exist(self):
|
||||
v = VaultEditor(None, None, None)
|
||||
slots = ['create_file',
|
||||
'decrypt_file',
|
||||
'edit_file',
|
||||
'encrypt_file',
|
||||
'rekey_file',
|
||||
'read_data',
|
||||
'write_data',
|
||||
'shuffle_files']
|
||||
for slot in slots:
|
||||
assert hasattr(v, slot), "VaultLib is missing the %s method" % slot
|
||||
|
||||
@patch.object(VaultEditor, '_editor_shell_command')
|
||||
def test_create_file(self, mock_editor_shell_command):
|
||||
|
||||
def sc_side_effect(filename):
|
||||
return ['touch', filename]
|
||||
mock_editor_shell_command.side_effect = sc_side_effect
|
||||
|
||||
tmp_file = tempfile.NamedTemporaryFile()
|
||||
os.unlink(tmp_file.name)
|
||||
|
||||
ve = VaultEditor(None, "ansible", tmp_file.name)
|
||||
ve.create_file()
|
||||
|
||||
self.assertTrue(os.path.exists(tmp_file.name))
|
||||
|
||||
def test_decrypt_1_0(self):
|
||||
"""
|
||||
Skip testing decrypting 1.0 files if we don't have access to AES, KDF or
|
||||
Counter, or we are running on python3 since VaultAES hasn't been backported.
|
||||
"""
|
||||
if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2 or sys.version > '3':
|
||||
raise SkipTest
|
||||
|
||||
v10_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
with v10_file as f:
|
||||
f.write(to_bytes(v10_data))
|
||||
|
||||
ve = VaultEditor(None, "ansible", v10_file.name)
|
||||
|
||||
# make sure the password functions for the cipher
|
||||
error_hit = False
|
||||
try:
|
||||
ve.decrypt_file()
|
||||
except errors.AnsibleError as e:
|
||||
error_hit = True
|
||||
|
||||
# verify decrypted content
|
||||
f = open(v10_file.name, "rb")
|
||||
fdata = to_unicode(f.read())
|
||||
f.close()
|
||||
|
||||
os.unlink(v10_file.name)
|
||||
|
||||
assert error_hit == False, "error decrypting 1.0 file"
|
||||
assert fdata.strip() == "foo", "incorrect decryption of 1.0 file: %s" % fdata.strip()
|
||||
|
||||
|
||||
def test_decrypt_1_1(self):
|
||||
if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2:
|
||||
raise SkipTest
|
||||
|
||||
v11_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
with v11_file as f:
|
||||
f.write(to_bytes(v11_data))
|
||||
|
||||
ve = VaultEditor(None, "ansible", v11_file.name)
|
||||
|
||||
# make sure the password functions for the cipher
|
||||
error_hit = False
|
||||
try:
|
||||
ve.decrypt_file()
|
||||
except errors.AnsibleError as e:
|
||||
error_hit = True
|
||||
|
||||
# verify decrypted content
|
||||
f = open(v11_file.name, "rb")
|
||||
fdata = to_unicode(f.read())
|
||||
f.close()
|
||||
|
||||
os.unlink(v11_file.name)
|
||||
|
||||
assert error_hit == False, "error decrypting 1.0 file"
|
||||
assert fdata.strip() == "foo", "incorrect decryption of 1.0 file: %s" % fdata.strip()
|
||||
|
||||
|
||||
def test_rekey_migration(self):
|
||||
"""
|
||||
Skip testing rekeying files if we don't have access to AES, KDF or
|
||||
Counter, or we are running on python3 since VaultAES hasn't been backported.
|
||||
"""
|
||||
if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2 or sys.version > '3':
|
||||
raise SkipTest
|
||||
|
||||
v10_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
with v10_file as f:
|
||||
f.write(to_bytes(v10_data))
|
||||
|
||||
ve = VaultEditor(None, "ansible", v10_file.name)
|
||||
|
||||
# make sure the password functions for the cipher
|
||||
error_hit = False
|
||||
try:
|
||||
ve.rekey_file('ansible2')
|
||||
except errors.AnsibleError as e:
|
||||
error_hit = True
|
||||
|
||||
# verify decrypted content
|
||||
f = open(v10_file.name, "rb")
|
||||
fdata = f.read()
|
||||
f.close()
|
||||
|
||||
assert error_hit == False, "error rekeying 1.0 file to 1.1"
|
||||
|
||||
# ensure filedata can be decrypted, is 1.1 and is AES256
|
||||
vl = VaultLib("ansible2")
|
||||
dec_data = None
|
||||
error_hit = False
|
||||
try:
|
||||
dec_data = vl.decrypt(fdata)
|
||||
except errors.AnsibleError as e:
|
||||
error_hit = True
|
||||
|
||||
os.unlink(v10_file.name)
|
||||
|
||||
assert vl.cipher_name == "AES256", "wrong cipher name set after rekey: %s" % vl.cipher_name
|
||||
assert error_hit == False, "error decrypting migrated 1.0 file"
|
||||
assert dec_data.strip() == "foo", "incorrect decryption of rekeyed/migrated file: %s" % dec_data
|
||||
|
||||
|
0
test/units/parsing/yaml/__init__.py
Normal file
0
test/units/parsing/yaml/__init__.py
Normal file
283
test/units/parsing/yaml/test_loader.py
Normal file
283
test/units/parsing/yaml/test_loader.py
Normal file
|
@ -0,0 +1,283 @@
|
|||
# coding: utf-8
|
||||
# (c) 2015, Toshio Kuratomi <tkuratomi@ansible.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 six import text_type, binary_type
|
||||
from six.moves import StringIO
|
||||
from collections import Sequence, Set, Mapping
|
||||
|
||||
from ansible.compat.tests import unittest
|
||||
from ansible.compat.tests.mock import patch
|
||||
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
|
||||
|
||||
class TestAnsibleLoaderBasic(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_parse_number(self):
|
||||
stream = StringIO("""
|
||||
1
|
||||
""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
data = loader.get_single_data()
|
||||
self.assertEqual(data, 1)
|
||||
# No line/column info saved yet
|
||||
|
||||
def test_parse_string(self):
|
||||
stream = StringIO("""
|
||||
Ansible
|
||||
""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
data = loader.get_single_data()
|
||||
self.assertEqual(data, u'Ansible')
|
||||
self.assertIsInstance(data, text_type)
|
||||
|
||||
self.assertEqual(data.ansible_pos, ('myfile.yml', 2, 17))
|
||||
|
||||
def test_parse_utf8_string(self):
|
||||
stream = StringIO("""
|
||||
Cafè Eñyei
|
||||
""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
data = loader.get_single_data()
|
||||
self.assertEqual(data, u'Cafè Eñyei')
|
||||
self.assertIsInstance(data, text_type)
|
||||
|
||||
self.assertEqual(data.ansible_pos, ('myfile.yml', 2, 17))
|
||||
|
||||
def test_parse_dict(self):
|
||||
stream = StringIO("""
|
||||
webster: daniel
|
||||
oed: oxford
|
||||
""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
data = loader.get_single_data()
|
||||
self.assertEqual(data, {'webster': 'daniel', 'oed': 'oxford'})
|
||||
self.assertEqual(len(data), 2)
|
||||
self.assertIsInstance(list(data.keys())[0], text_type)
|
||||
self.assertIsInstance(list(data.values())[0], text_type)
|
||||
|
||||
# Beginning of the first key
|
||||
self.assertEqual(data.ansible_pos, ('myfile.yml', 2, 17))
|
||||
|
||||
self.assertEqual(data[u'webster'].ansible_pos, ('myfile.yml', 2, 26))
|
||||
self.assertEqual(data[u'oed'].ansible_pos, ('myfile.yml', 3, 22))
|
||||
|
||||
def test_parse_list(self):
|
||||
stream = StringIO("""
|
||||
- a
|
||||
- b
|
||||
""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
data = loader.get_single_data()
|
||||
self.assertEqual(data, [u'a', u'b'])
|
||||
self.assertEqual(len(data), 2)
|
||||
self.assertIsInstance(data[0], text_type)
|
||||
|
||||
self.assertEqual(data.ansible_pos, ('myfile.yml', 2, 17))
|
||||
|
||||
self.assertEqual(data[0].ansible_pos, ('myfile.yml', 2, 19))
|
||||
self.assertEqual(data[1].ansible_pos, ('myfile.yml', 3, 19))
|
||||
|
||||
def test_parse_short_dict(self):
|
||||
stream = StringIO("""{"foo": "bar"}""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
data = loader.get_single_data()
|
||||
self.assertEqual(data, dict(foo=u'bar'))
|
||||
|
||||
self.assertEqual(data.ansible_pos, ('myfile.yml', 1, 1))
|
||||
self.assertEqual(data[u'foo'].ansible_pos, ('myfile.yml', 1, 9))
|
||||
|
||||
stream = StringIO("""foo: bar""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
data = loader.get_single_data()
|
||||
self.assertEqual(data, dict(foo=u'bar'))
|
||||
|
||||
self.assertEqual(data.ansible_pos, ('myfile.yml', 1, 1))
|
||||
self.assertEqual(data[u'foo'].ansible_pos, ('myfile.yml', 1, 6))
|
||||
|
||||
def test_error_conditions(self):
|
||||
stream = StringIO("""{""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
self.assertRaises(loader.get_single_data)
|
||||
|
||||
def test_front_matter(self):
|
||||
stream = StringIO("""---\nfoo: bar""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
data = loader.get_single_data()
|
||||
self.assertEqual(data, dict(foo=u'bar'))
|
||||
|
||||
self.assertEqual(data.ansible_pos, ('myfile.yml', 2, 1))
|
||||
self.assertEqual(data[u'foo'].ansible_pos, ('myfile.yml', 2, 6))
|
||||
|
||||
# Initial indent (See: #6348)
|
||||
stream = StringIO(""" - foo: bar\n baz: qux""")
|
||||
loader = AnsibleLoader(stream, 'myfile.yml')
|
||||
data = loader.get_single_data()
|
||||
self.assertEqual(data, [{u'foo': u'bar', u'baz': u'qux'}])
|
||||
|
||||
self.assertEqual(data.ansible_pos, ('myfile.yml', 1, 2))
|
||||
self.assertEqual(data[0].ansible_pos, ('myfile.yml', 1, 4))
|
||||
self.assertEqual(data[0][u'foo'].ansible_pos, ('myfile.yml', 1, 9))
|
||||
self.assertEqual(data[0][u'baz'].ansible_pos, ('myfile.yml', 2, 9))
|
||||
|
||||
|
||||
class TestAnsibleLoaderPlay(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
stream = StringIO("""
|
||||
- hosts: localhost
|
||||
vars:
|
||||
number: 1
|
||||
string: Ansible
|
||||
utf8_string: Cafè Eñyei
|
||||
dictionary:
|
||||
webster: daniel
|
||||
oed: oxford
|
||||
list:
|
||||
- a
|
||||
- b
|
||||
- 1
|
||||
- 2
|
||||
tasks:
|
||||
- name: Test case
|
||||
ping:
|
||||
data: "{{ utf8_string }}"
|
||||
|
||||
- name: Test 2
|
||||
ping:
|
||||
data: "Cafè Eñyei"
|
||||
|
||||
- name: Test 3
|
||||
command: "printf 'Cafè Eñyei\\n'"
|
||||
""")
|
||||
self.play_filename = '/path/to/myplay.yml'
|
||||
stream.name = self.play_filename
|
||||
self.loader = AnsibleLoader(stream)
|
||||
self.data = self.loader.get_single_data()
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_data_complete(self):
|
||||
self.assertEqual(len(self.data), 1)
|
||||
self.assertIsInstance(self.data, list)
|
||||
self.assertEqual(frozenset(self.data[0].keys()), frozenset((u'hosts', u'vars', u'tasks')))
|
||||
|
||||
self.assertEqual(self.data[0][u'hosts'], u'localhost')
|
||||
|
||||
self.assertEqual(self.data[0][u'vars'][u'number'], 1)
|
||||
self.assertEqual(self.data[0][u'vars'][u'string'], u'Ansible')
|
||||
self.assertEqual(self.data[0][u'vars'][u'utf8_string'], u'Cafè Eñyei')
|
||||
self.assertEqual(self.data[0][u'vars'][u'dictionary'],
|
||||
{u'webster': u'daniel',
|
||||
u'oed': u'oxford'})
|
||||
self.assertEqual(self.data[0][u'vars'][u'list'], [u'a', u'b', 1, 2])
|
||||
|
||||
self.assertEqual(self.data[0][u'tasks'],
|
||||
[{u'name': u'Test case', u'ping': {u'data': u'{{ utf8_string }}'}},
|
||||
{u'name': u'Test 2', u'ping': {u'data': u'Cafè Eñyei'}},
|
||||
{u'name': u'Test 3', u'command': u'printf \'Cafè Eñyei\n\''},
|
||||
])
|
||||
|
||||
def walk(self, data):
|
||||
# Make sure there's no str in the data
|
||||
self.assertNotIsInstance(data, binary_type)
|
||||
|
||||
# Descend into various container types
|
||||
if isinstance(data, text_type):
|
||||
# strings are a sequence so we have to be explicit here
|
||||
return
|
||||
elif isinstance(data, (Sequence, Set)):
|
||||
for element in data:
|
||||
self.walk(element)
|
||||
elif isinstance(data, Mapping):
|
||||
for k, v in data.items():
|
||||
self.walk(k)
|
||||
self.walk(v)
|
||||
|
||||
# Scalars were all checked so we're good to go
|
||||
return
|
||||
|
||||
def test_no_str_in_data(self):
|
||||
# Checks that no strings are str type
|
||||
self.walk(self.data)
|
||||
|
||||
def check_vars(self):
|
||||
# Numbers don't have line/col information yet
|
||||
#self.assertEqual(self.data[0][u'vars'][u'number'].ansible_pos, (self.play_filename, 4, 21))
|
||||
|
||||
self.assertEqual(self.data[0][u'vars'][u'string'].ansible_pos, (self.play_filename, 5, 29))
|
||||
self.assertEqual(self.data[0][u'vars'][u'utf8_string'].ansible_pos, (self.play_filename, 6, 34))
|
||||
|
||||
self.assertEqual(self.data[0][u'vars'][u'dictionary'].ansible_pos, (self.play_filename, 8, 23))
|
||||
self.assertEqual(self.data[0][u'vars'][u'dictionary'][u'webster'].ansible_pos, (self.play_filename, 8, 32))
|
||||
self.assertEqual(self.data[0][u'vars'][u'dictionary'][u'oed'].ansible_pos, (self.play_filename, 9, 28))
|
||||
|
||||
self.assertEqual(self.data[0][u'vars'][u'list'].ansible_pos, (self.play_filename, 11, 23))
|
||||
self.assertEqual(self.data[0][u'vars'][u'list'][0].ansible_pos, (self.play_filename, 11, 25))
|
||||
self.assertEqual(self.data[0][u'vars'][u'list'][1].ansible_pos, (self.play_filename, 12, 25))
|
||||
# Numbers don't have line/col info yet
|
||||
#self.assertEqual(self.data[0][u'vars'][u'list'][2].ansible_pos, (self.play_filename, 13, 25))
|
||||
#self.assertEqual(self.data[0][u'vars'][u'list'][3].ansible_pos, (self.play_filename, 14, 25))
|
||||
|
||||
def check_tasks(self):
|
||||
#
|
||||
# First Task
|
||||
#
|
||||
self.assertEqual(self.data[0][u'tasks'][0].ansible_pos, (self.play_filename, 16, 23))
|
||||
self.assertEqual(self.data[0][u'tasks'][0][u'name'].ansible_pos, (self.play_filename, 16, 29))
|
||||
self.assertEqual(self.data[0][u'tasks'][0][u'ping'].ansible_pos, (self.play_filename, 18, 25))
|
||||
self.assertEqual(self.data[0][u'tasks'][0][u'ping'][u'data'].ansible_pos, (self.play_filename, 18, 31))
|
||||
|
||||
#
|
||||
# Second Task
|
||||
#
|
||||
self.assertEqual(self.data[0][u'tasks'][1].ansible_pos, (self.play_filename, 20, 23))
|
||||
self.assertEqual(self.data[0][u'tasks'][1][u'name'].ansible_pos, (self.play_filename, 20, 29))
|
||||
self.assertEqual(self.data[0][u'tasks'][1][u'ping'].ansible_pos, (self.play_filename, 22, 25))
|
||||
self.assertEqual(self.data[0][u'tasks'][1][u'ping'][u'data'].ansible_pos, (self.play_filename, 22, 31))
|
||||
|
||||
#
|
||||
# Third Task
|
||||
#
|
||||
self.assertEqual(self.data[0][u'tasks'][2].ansible_pos, (self.play_filename, 24, 23))
|
||||
self.assertEqual(self.data[0][u'tasks'][2][u'name'].ansible_pos, (self.play_filename, 24, 29))
|
||||
self.assertEqual(self.data[0][u'tasks'][2][u'command'].ansible_pos, (self.play_filename, 25, 32))
|
||||
|
||||
def test_line_numbers(self):
|
||||
# Check the line/column numbers are correct
|
||||
# Note: Remember, currently dicts begin at the start of their first entry
|
||||
self.assertEqual(self.data[0].ansible_pos, (self.play_filename, 2, 19))
|
||||
self.assertEqual(self.data[0][u'hosts'].ansible_pos, (self.play_filename, 2, 26))
|
||||
self.assertEqual(self.data[0][u'vars'].ansible_pos, (self.play_filename, 4, 21))
|
||||
|
||||
self.check_vars()
|
||||
|
||||
self.assertEqual(self.data[0][u'tasks'].ansible_pos, (self.play_filename, 16, 21))
|
||||
|
||||
self.check_tasks()
|
Loading…
Add table
Add a link
Reference in a new issue