From dfb8fea539b53a0f9615282615f1dca90e7ecb91 Mon Sep 17 00:00:00 2001 From: Ajinkya Udgirkar Date: Tue, 30 Apr 2024 16:26:54 +0530 Subject: [PATCH] Make `ignore_unreachable` accept jinja templates (#4120) --- src/ansiblelint/schemas/ansible.json | 8 +- src/ansiblelint/schemas/playbook.json | 16 +- src/ansiblelint/schemas/tasks.json | 8 +- .../playbooks/ignore-unreachable.yml | 13 + .../playbooks/ignore-unreachable.yml.md | 329 ++++++++++++++++++ .../test/playbooks/ignore-unreachable.yml | 13 + 6 files changed, 371 insertions(+), 16 deletions(-) create mode 100644 test/schemas/negative_test/playbooks/ignore-unreachable.yml create mode 100644 test/schemas/negative_test/playbooks/ignore-unreachable.yml.md create mode 100644 test/schemas/test/playbooks/ignore-unreachable.yml diff --git a/src/ansiblelint/schemas/ansible.json b/src/ansiblelint/schemas/ansible.json index 50ded20094..161a31a5fd 100644 --- a/src/ansiblelint/schemas/ansible.json +++ b/src/ansiblelint/schemas/ansible.json @@ -168,7 +168,7 @@ }, "ignore_unreachable": { "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean" }, "module_defaults": { "title": "Module Defaults" @@ -528,7 +528,7 @@ }, "ignore_unreachable": { "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean" }, "max_fail_percentage": { "title": "Max Fail Percentage", @@ -732,7 +732,7 @@ }, "ignore_unreachable": { "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean" }, "module_defaults": { "title": "Module Defaults" @@ -935,7 +935,7 @@ }, "ignore_unreachable": { "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean" }, "listen": { "anyOf": [ diff --git a/src/ansiblelint/schemas/playbook.json b/src/ansiblelint/schemas/playbook.json index a1a31e811a..4bf44b6ba6 100644 --- a/src/ansiblelint/schemas/playbook.json +++ b/src/ansiblelint/schemas/playbook.json @@ -171,8 +171,8 @@ "$ref": "#/$defs/ignore_errors" }, "ignore_unreachable": { - "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean", + "title": "Ignore Unreachable" }, "module_defaults": { "title": "Module Defaults" @@ -537,8 +537,8 @@ "$ref": "#/$defs/ignore_errors" }, "ignore_unreachable": { - "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean", + "title": "Ignore Unreachable" }, "max_fail_percentage": { "$ref": "#/$defs/templated-integer", @@ -747,8 +747,8 @@ "$ref": "#/$defs/ignore_errors" }, "ignore_unreachable": { - "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean", + "title": "Ignore Unreachable" }, "module_defaults": { "title": "Module Defaults" @@ -947,8 +947,8 @@ "$ref": "#/$defs/ignore_errors" }, "ignore_unreachable": { - "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean", + "title": "Ignore Unreachable" }, "include": { "$ref": "#/$defs/removed-include-module" diff --git a/src/ansiblelint/schemas/tasks.json b/src/ansiblelint/schemas/tasks.json index e0273fd50b..d6efec839e 100644 --- a/src/ansiblelint/schemas/tasks.json +++ b/src/ansiblelint/schemas/tasks.json @@ -123,8 +123,8 @@ "$ref": "#/$defs/ignore_errors" }, "ignore_unreachable": { - "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean", + "title": "Ignore Unreachable" }, "module_defaults": { "title": "Module Defaults" @@ -382,8 +382,8 @@ "$ref": "#/$defs/ignore_errors" }, "ignore_unreachable": { - "title": "Ignore Unreachable", - "type": "boolean" + "$ref": "#/$defs/templated-boolean", + "title": "Ignore Unreachable" }, "include": { "$ref": "#/$defs/removed-include-module" diff --git a/test/schemas/negative_test/playbooks/ignore-unreachable.yml b/test/schemas/negative_test/playbooks/ignore-unreachable.yml new file mode 100644 index 0000000000..09349362d1 --- /dev/null +++ b/test/schemas/negative_test/playbooks/ignore-unreachable.yml @@ -0,0 +1,13 @@ +--- +- name: Test + hosts: localhost + tasks: + - name: Debug + ansible.builtin.debug: + msg: ignore_unreachable should not be a string + ignore_unreachable: "yes" + + - name: Debug + ansible.builtin.debug: + msg: jinja evaluation should not be a string + ignore_unreachable: 123 diff --git a/test/schemas/negative_test/playbooks/ignore-unreachable.yml.md b/test/schemas/negative_test/playbooks/ignore-unreachable.yml.md new file mode 100644 index 0000000000..b12403ddb5 --- /dev/null +++ b/test/schemas/negative_test/playbooks/ignore-unreachable.yml.md @@ -0,0 +1,329 @@ +# ajv errors + +```json +[ + { + "instancePath": "/0", + "keyword": "required", + "message": "must have required property 'ansible.builtin.import_playbook'", + "params": { + "missingProperty": "ansible.builtin.import_playbook" + }, + "schemaPath": "#/oneOf/0/required" + }, + { + "instancePath": "/0", + "keyword": "required", + "message": "must have required property 'import_playbook'", + "params": { + "missingProperty": "import_playbook" + }, + "schemaPath": "#/oneOf/1/required" + }, + { + "instancePath": "/0", + "keyword": "oneOf", + "message": "must match exactly one schema in oneOf", + "params": { + "passingSchemas": null + }, + "schemaPath": "#/oneOf" + }, + { + "instancePath": "/0", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "hosts" + }, + "schemaPath": "#/additionalProperties" + }, + { + "instancePath": "/0", + "keyword": "additionalProperties", + "message": "must NOT have additional properties", + "params": { + "additionalProperty": "tasks" + }, + "schemaPath": "#/additionalProperties" + }, + { + "instancePath": "/0/tasks/0", + "keyword": "required", + "message": "must have required property 'block'", + "params": { + "missingProperty": "block" + }, + "schemaPath": "#/required" + }, + { + "instancePath": "/0/tasks/0/ignore_unreachable", + "keyword": "type", + "message": "must be boolean", + "params": { + "type": "boolean" + }, + "schemaPath": "#/oneOf/0/type" + }, + { + "instancePath": "/0/tasks/0/ignore_unreachable", + "keyword": "pattern", + "message": "must match pattern \"^\\{[\\{%](.|[\r\n])*[\\}%]\\}$\"", + "params": { + "pattern": "^\\{[\\{%](.|[\r\n])*[\\}%]\\}$" + }, + "schemaPath": "#/$defs/full-jinja/pattern" + }, + { + "instancePath": "/0/tasks/0/ignore_unreachable", + "keyword": "oneOf", + "message": "must match exactly one schema in oneOf", + "params": { + "passingSchemas": null + }, + "schemaPath": "#/oneOf" + }, + { + "instancePath": "/0/tasks/0/ignore_unreachable", + "keyword": "type", + "message": "must be boolean", + "params": { + "type": "boolean" + }, + "schemaPath": "#/oneOf/0/type" + }, + { + "instancePath": "/0/tasks/0/ignore_unreachable", + "keyword": "pattern", + "message": "must match pattern \"^\\{[\\{%](.|[\r\n])*[\\}%]\\}$\"", + "params": { + "pattern": "^\\{[\\{%](.|[\r\n])*[\\}%]\\}$" + }, + "schemaPath": "#/$defs/full-jinja/pattern" + }, + { + "instancePath": "/0/tasks/0/ignore_unreachable", + "keyword": "oneOf", + "message": "must match exactly one schema in oneOf", + "params": { + "passingSchemas": null + }, + "schemaPath": "#/oneOf" + }, + { + "instancePath": "/0/tasks/0", + "keyword": "anyOf", + "message": "must match a schema in anyOf", + "params": {}, + "schemaPath": "#/items/anyOf" + }, + { + "instancePath": "/0/tasks/1", + "keyword": "required", + "message": "must have required property 'block'", + "params": { + "missingProperty": "block" + }, + "schemaPath": "#/required" + }, + { + "instancePath": "/0/tasks/1/ignore_unreachable", + "keyword": "type", + "message": "must be boolean", + "params": { + "type": "boolean" + }, + "schemaPath": "#/oneOf/0/type" + }, + { + "instancePath": "/0/tasks/1/ignore_unreachable", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string" + }, + "schemaPath": "#/oneOf/1/type" + }, + { + "instancePath": "/0/tasks/1/ignore_unreachable", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string" + }, + "schemaPath": "#/$defs/full-jinja/type" + }, + { + "instancePath": "/0/tasks/1/ignore_unreachable", + "keyword": "oneOf", + "message": "must match exactly one schema in oneOf", + "params": { + "passingSchemas": null + }, + "schemaPath": "#/oneOf" + }, + { + "instancePath": "/0/tasks/1/ignore_unreachable", + "keyword": "type", + "message": "must be boolean", + "params": { + "type": "boolean" + }, + "schemaPath": "#/oneOf/0/type" + }, + { + "instancePath": "/0/tasks/1/ignore_unreachable", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string" + }, + "schemaPath": "#/oneOf/1/type" + }, + { + "instancePath": "/0/tasks/1/ignore_unreachable", + "keyword": "type", + "message": "must be string", + "params": { + "type": "string" + }, + "schemaPath": "#/$defs/full-jinja/type" + }, + { + "instancePath": "/0/tasks/1/ignore_unreachable", + "keyword": "oneOf", + "message": "must match exactly one schema in oneOf", + "params": { + "passingSchemas": null + }, + "schemaPath": "#/oneOf" + }, + { + "instancePath": "/0/tasks/1", + "keyword": "anyOf", + "message": "must match a schema in anyOf", + "params": {}, + "schemaPath": "#/items/anyOf" + }, + { + "instancePath": "/0", + "keyword": "oneOf", + "message": "must match exactly one schema in oneOf", + "params": { + "passingSchemas": null + }, + "schemaPath": "#/items/oneOf" + } +] +``` + +# check-jsonschema + +stdout: + +```json +{ + "status": "fail", + "successes": [], + "errors": [ + { + "filename": "negative_test/playbooks/ignore-unreachable.yml", + "path": "$[0]", + "message": "{'name': 'Test', 'hosts': 'localhost', 'tasks': [{'name': 'Debug', 'ansible.builtin.debug': {'msg': 'ignore_unreachable should not be a string'}, 'ignore_unreachable': 'yes'}, {'name': 'Debug', 'ansible.builtin.debug': {'msg': 'jinja evaluation should not be a string'}, 'ignore_unreachable': 123}]} is not valid under any of the given schemas", + "has_sub_errors": true, + "best_match": { + "path": "$[0]", + "message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'" + }, + "best_deep_match": { + "path": "$[0].tasks[0].ignore_unreachable", + "message": "'yes' is not of type 'boolean'" + }, + "num_sub_errors": 19, + "sub_errors": [ + { + "path": "$[0]", + "message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'" + }, + { + "path": "$[0]", + "message": "{'name': 'Test', 'hosts': 'localhost', 'tasks': [{'name': 'Debug', 'ansible.builtin.debug': {'msg': 'ignore_unreachable should not be a string'}, 'ignore_unreachable': 'yes'}, {'name': 'Debug', 'ansible.builtin.debug': {'msg': 'jinja evaluation should not be a string'}, 'ignore_unreachable': 123}]} is not valid under any of the given schemas" + }, + { + "path": "$[0]", + "message": "'ansible.builtin.import_playbook' is a required property" + }, + { + "path": "$[0]", + "message": "'import_playbook' is a required property" + }, + { + "path": "$[0].tasks[0]", + "message": "{'name': 'Debug', 'ansible.builtin.debug': {'msg': 'ignore_unreachable should not be a string'}, 'ignore_unreachable': 'yes'} is not valid under any of the given schemas" + }, + { + "path": "$[0].tasks[0].ignore_unreachable", + "message": "'yes' is not valid under any of the given schemas" + }, + { + "path": "$[0].tasks[0].ignore_unreachable", + "message": "'yes' is not of type 'boolean'" + }, + { + "path": "$[0].tasks[0].ignore_unreachable", + "message": "'yes' does not match '^\\\\{[\\\\{%](.|[\\r\\n])*[\\\\}%]\\\\}$'" + }, + { + "path": "$[0].tasks[0]", + "message": "'block' is a required property" + }, + { + "path": "$[0].tasks[0].ignore_unreachable", + "message": "'yes' is not valid under any of the given schemas" + }, + { + "path": "$[0].tasks[0].ignore_unreachable", + "message": "'yes' is not of type 'boolean'" + }, + { + "path": "$[0].tasks[0].ignore_unreachable", + "message": "'yes' does not match '^\\\\{[\\\\{%](.|[\\r\\n])*[\\\\}%]\\\\}$'" + }, + { + "path": "$[0].tasks[1]", + "message": "{'name': 'Debug', 'ansible.builtin.debug': {'msg': 'jinja evaluation should not be a string'}, 'ignore_unreachable': 123} is not valid under any of the given schemas" + }, + { + "path": "$[0].tasks[1].ignore_unreachable", + "message": "123 is not valid under any of the given schemas" + }, + { + "path": "$[0].tasks[1].ignore_unreachable", + "message": "123 is not of type 'boolean'" + }, + { + "path": "$[0].tasks[1].ignore_unreachable", + "message": "123 is not of type 'string'" + }, + { + "path": "$[0].tasks[1]", + "message": "'block' is a required property" + }, + { + "path": "$[0].tasks[1].ignore_unreachable", + "message": "123 is not valid under any of the given schemas" + }, + { + "path": "$[0].tasks[1].ignore_unreachable", + "message": "123 is not of type 'boolean'" + }, + { + "path": "$[0].tasks[1].ignore_unreachable", + "message": "123 is not of type 'string'" + } + ] + } + ], + "parse_errors": [] +} +``` diff --git a/test/schemas/test/playbooks/ignore-unreachable.yml b/test/schemas/test/playbooks/ignore-unreachable.yml new file mode 100644 index 0000000000..8dfdc21a59 --- /dev/null +++ b/test/schemas/test/playbooks/ignore-unreachable.yml @@ -0,0 +1,13 @@ +--- +- name: Test + hosts: localhost + tasks: + - name: Debug + ansible.builtin.debug: + msg: ignore_unreachable should be a boolean + ignore_unreachable: true + + - name: Debug + ansible.builtin.debug: + msg: "foo" + ignore_unreachable: '{{ "yes" | bool }}'