From 580aa54b98ad30a260e7b20bf7dde5c9a2468d8e Mon Sep 17 00:00:00 2001 From: Major Hayden Date: Mon, 7 Aug 2017 11:04:26 -0500 Subject: [PATCH] Add linter for passing pkgs as list This patch adds a new linter that ensures packages are being passed as a list rather than using with_items. It also adds a test for the new linter and adds the test to the existing linter CI job script. Change-Id: Iffd7284fb1c7cc41df2a4e271821e51bb274c3a3 --- ansible-lint/PassListToPackageModules.py | 81 +++++++++++++++++++ .../test/package_module_pass_list.yml | 27 +++++++ .../test/package_module_with_items.yml | 28 +++++++ test-ansible-lint.sh | 3 + 4 files changed, 139 insertions(+) create mode 100755 ansible-lint/PassListToPackageModules.py create mode 100644 ansible-lint/test/package_module_pass_list.yml create mode 100644 ansible-lint/test/package_module_with_items.yml diff --git a/ansible-lint/PassListToPackageModules.py b/ansible-lint/PassListToPackageModules.py new file mode 100755 index 00000000..b4176f4f --- /dev/null +++ b/ansible-lint/PassListToPackageModules.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# Copyright 2017, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Ansible linter for passing packages as list to package modules.""" +import os +import unittest + +from ansiblelint import AnsibleLintRule, RulesCollection, Runner + +TEST_PLAYBOOK_DIR = "{}/test/".format( + os.path.dirname(os.path.realpath(__file__)) +) +PACKAGE_MODULES = [ + 'apt', + 'dnf', + 'package', + 'pip', + 'yum', + 'zypper' +] + + +def is_package_action(task): + """Test if task is using a package module.""" + return task['action']['__ansible_module__'] in PACKAGE_MODULES + + +class PassListToPackageModules(AnsibleLintRule): + """Ansible linter for passing packages as list to package modules.""" + id = 'OSA0002' + shortdesc = 'Pass packages as a list to package modules' + description = ( + "When passing multiple packages to a package module, pass the " + "packages as a list rather than using 'with_items'. This provides a " + "performance boost for deployments." + ) + tags = ['performance'] + + def matchtask(self, file, task): + """Search the task for our concerns.""" + return is_package_action(task) and 'with_items' in task + + +class TestPassListToPackageModules(unittest.TestCase): + """Test the PassListToPackageModules lint rule.""" + collection = RulesCollection() + + def setUp(self): + """Set up the linter testing.""" + self.collection.register(PassListToPackageModules()) + + def test_file_positive(self): + """A valid playbook should pass the linter.""" + playbook = '{}/package_module_pass_list.yml'.format( + TEST_PLAYBOOK_DIR + ) + runner = Runner(self.collection, playbook, [], [], []) + self.assertEqual([], runner.run()) + + def test_file_negative(self): + """An invalid playbook should fail the linter.""" + playbook = '{}/package_module_with_items.yml'.format( + TEST_PLAYBOOK_DIR + ) + runner = Runner(self.collection, playbook, [], [], []) + self.assertNotEqual([], runner.run()) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible-lint/test/package_module_pass_list.yml b/ansible-lint/test/package_module_pass_list.yml new file mode 100644 index 00000000..4f69e311 --- /dev/null +++ b/ansible-lint/test/package_module_pass_list.yml @@ -0,0 +1,27 @@ +# Copyright 2017, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- + +- hosts: all + vars: + packages_to_install: + - bash + - tmux + - vim + tasks: + + - name: Install packages using a list + package: + name: "{{ packages_to_install }}" + state: present diff --git a/ansible-lint/test/package_module_with_items.yml b/ansible-lint/test/package_module_with_items.yml new file mode 100644 index 00000000..53b3d6dd --- /dev/null +++ b/ansible-lint/test/package_module_with_items.yml @@ -0,0 +1,28 @@ +# Copyright 2017, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- + +- hosts: all + vars: + packages_to_install: + - bash + - tmux + - vim + tasks: + + - name: Install packages using with_items + package: + name: "{{ item }}" + state: present + with_items: "{{ packages_to_install }}" diff --git a/test-ansible-lint.sh b/test-ansible-lint.sh index a2420457..d6e26071 100755 --- a/test-ansible-lint.sh +++ b/test-ansible-lint.sh @@ -36,5 +36,8 @@ export COMMON_TESTS_PATH="${WORKING_DIR}/tests/common" # Ensure that the Ansible environment is properly prepared source "${COMMON_TESTS_PATH}/test-ansible-env-prep.sh" +# Run linter tests +python -m unittest discover -s ${WORKING_DIR}/ansible-lint/ -p "*.py" + # Execute ansible-lint ansible-lint ${WORKING_DIR} -R -r ${WORKING_DIR}/ansible-lint/