1--- 2- name: "Running include_role test1" 3 hosts: localhost 4 gather_facts: false 5 vars: 6 ansible_unicode_type_match: "<type 'ansible.parsing.yaml.objects.AnsibleUnicode'>" 7 unicode_type_match: "<type 'unicode'>" 8 string_type_match: "<type 'str'>" 9 float_type_match: "<type 'float'>" 10 list_type_match: "<type 'list'>" 11 ansible_list_type_match: "<type 'ansible.parsing.yaml.objects.AnsibleSequence'>" 12 dict_type_match: "<type 'dict'>" 13 ansible_dict_type_match: "<type 'ansible.parsing.yaml.objects.AnsibleMapping'>" 14 ansible_unicode_class_match: "<class 'ansible.parsing.yaml.objects.AnsibleUnicode'>" 15 unicode_class_match: "<class 'unicode'>" 16 string_class_match: "<class 'str'>" 17 bytes_class_match: "<class 'bytes'>" 18 float_class_match: "<class 'float'>" 19 list_class_match: "<class 'list'>" 20 ansible_list_class_match: "<class 'ansible.parsing.yaml.objects.AnsibleSequence'>" 21 dict_class_match: "<class 'dict'>" 22 ansible_dict_class_match: "<class 'ansible.parsing.yaml.objects.AnsibleMapping'>" 23 expected: 24 test1_1: 25 argument_errors: [ 26 "argument 'tidy_expected' is of type <class 'ansible.parsing.yaml.objects.AnsibleMapping'> and we were unable to convert to list: <class 'ansible.parsing.yaml.objects.AnsibleMapping'> cannot be converted to a list", 27 "argument 'bust_some_stuff' is of type <class 'str'> and we were unable to convert to int: <class 'str'> cannot be converted to an int", 28 "argument 'some_list' is of type <class 'ansible.parsing.yaml.objects.AnsibleMapping'> and we were unable to convert to list: <class 'ansible.parsing.yaml.objects.AnsibleMapping'> cannot be converted to a list", 29 "argument 'some_dict' is of type <class 'ansible.parsing.yaml.objects.AnsibleSequence'> and we were unable to convert to dict: <class 'ansible.parsing.yaml.objects.AnsibleSequence'> cannot be converted to a dict", 30 "argument 'some_int' is of type <class 'float'> and we were unable to convert to int: <class 'float'> cannot be converted to an int", 31 "argument 'some_float' is of type <class 'str'> and we were unable to convert to float: <class 'str'> cannot be converted to a float", 32 "argument 'some_bytes' is of type <class 'bytes'> and we were unable to convert to bytes: <class 'bytes'> cannot be converted to a Byte value", 33 "argument 'some_bits' is of type <class 'str'> and we were unable to convert to bits: <class 'str'> cannot be converted to a Bit value", 34 "value of test1_choices must be one of: this paddle game, the astray, this remote control, the chair, got: My dog", 35 "value of some_choices must be one of: choice1, choice2, got: choice4", 36 "argument 'some_second_level' is of type <class 'ansible.parsing.yaml.objects.AnsibleUnicode'> found in 'some_dict_options'. and we were unable to convert to bool: The value 'not-a-bool' is not a valid boolean. ", 37 "argument 'third_level' is of type <class 'ansible.parsing.yaml.objects.AnsibleUnicode'> found in 'multi_level_option -> second_level'. and we were unable to convert to int: <class 'ansible.parsing.yaml.objects.AnsibleUnicode'> cannot be converted to an int" 38 ] 39 40 tasks: 41 # This test play requires jinja >= 2.7 42 - name: get the jinja2 version 43 shell: python -c 'import jinja2; print(jinja2.__version__)' 44 register: jinja2_version 45 delegate_to: localhost 46 changed_when: false 47 48 - debug: 49 msg: "Jinja version: {{ jinja2_version.stdout }}" 50 51 - name: include_role test1 since it has a arg_spec.yml 52 block: 53 - include_role: 54 name: test1 55 vars: 56 tidy_expected: 57 some_key: some_value 58 test1_var1: 37.4 59 test1_choices: "My dog" 60 bust_some_stuff: "some_string_that_is_not_an_int" 61 some_choices: "choice4" 62 some_str: 37.5 63 some_list: {'a': false} 64 some_dict: 65 - "foo" 66 - "bar" 67 some_int: 37. 68 some_float: "notafloatisit" 69 some_path: "anything_is_a_valid_path" 70 some_raw: {"anything_can_be": "a_raw_type"} 71 # not sure what would be an invalid jsonarg 72 # some_jsonarg: "not sure what this does yet" 73 some_json: | 74 '{[1, 3, 3] 345345|45v<#!}' 75 some_jsonarg: | 76 {"foo": [1, 3, 3]} 77 # not sure we can load binary in safe_load 78 some_bytes: !!binary | 79 R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5 80 OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+ 81 +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC 82 AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= 83 some_bits: "foo" 84 # some_str_nicknames: [] 85 # some_str_akas: {} 86 some_str_removed_in: "foo" 87 some_dict_options: 88 some_second_level: "not-a-bool" 89 multi_level_option: 90 second_level: 91 third_level: "should_be_int" 92 93 - fail: 94 msg: "Should not get here" 95 96 rescue: 97 - debug: 98 var: ansible_failed_result 99 100 - name: replace py version specific types with generic names so tests work on py2 and py3 101 set_fact: 102 # We want to compare if the actual failure messages and the expected failure messages 103 # are the same. But to compare and do set differences, we have to handle some 104 # differences between py2/py3. 105 # The validation failure messages include python type and class reprs, which are 106 # different between py2 and py3. For ex, "<type 'str'>" vs "<class 'str'>". Plus 107 # the usual py2/py3 unicode/str/bytes type shenanigans. The 'THE_FLOAT_REPR' is 108 # because py3 quotes the value in the error while py2 does not, so we just ignore 109 # the rest of the line. 110 actual_generic: "{{ ansible_failed_result.argument_errors| 111 map('replace', ansible_unicode_type_match, 'STR')| 112 map('replace', unicode_type_match, 'STR')| 113 map('replace', string_type_match, 'STR')| 114 map('replace', float_type_match, 'FLOAT')| 115 map('replace', list_type_match, 'LIST')| 116 map('replace', ansible_list_type_match, 'LIST')| 117 map('replace', dict_type_match, 'DICT')| 118 map('replace', ansible_dict_type_match, 'DICT')| 119 map('replace', ansible_unicode_class_match, 'STR')| 120 map('replace', unicode_class_match, 'STR')| 121 map('replace', string_class_match, 'STR')| 122 map('replace', bytes_class_match, 'STR')| 123 map('replace', float_class_match, 'FLOAT')| 124 map('replace', list_class_match, 'LIST')| 125 map('replace', ansible_list_class_match, 'LIST')| 126 map('replace', dict_class_match, 'DICT')| 127 map('replace', ansible_dict_class_match, 'DICT')| 128 map('regex_replace', '''float:.*$''', 'THE_FLOAT_REPR')| 129 map('regex_replace', 'Valid booleans include.*$', '')| 130 list }}" 131 expected_generic: "{{ expected.test1_1.argument_errors| 132 map('replace', ansible_unicode_type_match, 'STR')| 133 map('replace', unicode_type_match, 'STR')| 134 map('replace', string_type_match, 'STR')| 135 map('replace', float_type_match, 'FLOAT')| 136 map('replace', list_type_match, 'LIST')| 137 map('replace', ansible_list_type_match, 'LIST')| 138 map('replace', dict_type_match, 'DICT')| 139 map('replace', ansible_dict_type_match, 'DICT')| 140 map('replace', ansible_unicode_class_match, 'STR')| 141 map('replace', unicode_class_match, 'STR')| 142 map('replace', string_class_match, 'STR')| 143 map('replace', bytes_class_match, 'STR')| 144 map('replace', float_class_match, 'FLOAT')| 145 map('replace', list_class_match, 'LIST')| 146 map('replace', ansible_list_class_match, 'LIST')| 147 map('replace', dict_class_match, 'DICT')| 148 map('replace', ansible_dict_class_match, 'DICT')| 149 map('regex_replace', '''float:.*$''', 'THE_FLOAT_REPR')| 150 map('regex_replace', 'Valid booleans include.*$', '')| 151 list }}" 152 153 - name: figure out the difference between expected and actual validate_argument_spec failures 154 set_fact: 155 actual_not_in_expected: "{{ actual_generic| difference(expected_generic) | sort() }}" 156 expected_not_in_actual: "{{ expected_generic | difference(actual_generic) | sort() }}" 157 158 - name: assert that all actual validate_argument_spec failures were in expected 159 assert: 160 that: 161 - actual_not_in_expected | length == 0 162 msg: "Actual validate_argument_spec failures that were not expected: {{ actual_not_in_expected }}" 163 164 - name: assert that all expected validate_argument_spec failures were in expected 165 assert: 166 that: 167 - expected_not_in_actual | length == 0 168 msg: "Expected validate_argument_spec failures that were not in actual results: {{ expected_not_in_actual }}" 169 170 - name: assert that `validate_args_context` return value has what we expect 171 assert: 172 that: 173 - ansible_failed_result.validate_args_context.argument_spec_name == "main" 174 - ansible_failed_result.validate_args_context.name == "test1" 175 - ansible_failed_result.validate_args_context.type == "role" 176 - "ansible_failed_result.validate_args_context.path is search('roles_arg_spec/roles/test1')" 177 178 # skip this task if jinja isnt >= 2.7, aka centos6 179 when: 180 - jinja2_version.stdout is version('2.7', '>=') 181