From 23d0d225f4cb68407f74cf1221893d152577dafd Mon Sep 17 00:00:00 2001 From: Hannes Ljungberg Date: Thu, 11 Apr 2019 12:30:00 +0200 Subject: [PATCH] docker_swarm_service_info: Read information about swarm services (#55008) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add docker_swarm_service_info module * Remove unused import * Limit to retrieving info about one service * Add exists return value * Add yaml 3-dash Co-Authored-By: hannseman * Document return value as jinja `noneĀ“ Co-Authored-By: hannseman * Name is required --- lib/ansible/module_utils/docker/swarm.py | 27 ++++- .../cloud/docker/docker_swarm_service_info.py | 104 ++++++++++++++++++ .../targets/docker_swarm_service_info/aliases | 7 ++ .../docker_swarm_service_info/meta/main.yml | 3 + .../docker_swarm_service_info/tasks/main.yml | 6 + .../tasks/test_docker_swarm_service_info.yml | 76 +++++++++++++ 6 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 lib/ansible/modules/cloud/docker/docker_swarm_service_info.py create mode 100644 test/integration/targets/docker_swarm_service_info/aliases create mode 100644 test/integration/targets/docker_swarm_service_info/meta/main.yml create mode 100644 test/integration/targets/docker_swarm_service_info/tasks/main.yml create mode 100644 test/integration/targets/docker_swarm_service_info/tasks/test_docker_swarm_service_info.yml diff --git a/lib/ansible/module_utils/docker/swarm.py b/lib/ansible/module_utils/docker/swarm.py index 894fe61f490..6b16ce4a0e4 100644 --- a/lib/ansible/module_utils/docker/swarm.py +++ b/lib/ansible/module_utils/docker/swarm.py @@ -4,7 +4,6 @@ import json from time import sleep -from re import split try: from docker.errors import APIError @@ -249,3 +248,29 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient): if self.docker_py_version < LooseVersion('2.7.0'): return None return super(AnsibleDockerSwarmClient, self).get_unlock_key() + + def get_service_inspect(self, service_id, skip_missing=False): + """ + Returns Swarm service info as in 'docker service inspect' command about single service + + :param service_id: service ID or name + :param skip_missing: if True then function will return None instead of failing the task + :return: + Single service information structure + """ + try: + service_info = self.inspect_service(service=service_id) + except APIError as exc: + if exc.status_code == 503: + self.fail("Cannot inspect service: To inspect service execute module on Swarm Manager") + if exc.status_code == 404: + if skip_missing is False: + self.fail("Error while reading from Swarm manager: %s" % to_native(exc)) + else: + return None + except Exception as exc: + self.fail("Error inspecting swarm service: %s" % exc) + + json_str = json.dumps(service_info, ensure_ascii=False) + service_info = json.loads(json_str) + return service_info diff --git a/lib/ansible/modules/cloud/docker/docker_swarm_service_info.py b/lib/ansible/modules/cloud/docker/docker_swarm_service_info.py new file mode 100644 index 00000000000..072f97e72ad --- /dev/null +++ b/lib/ansible/modules/cloud/docker/docker_swarm_service_info.py @@ -0,0 +1,104 @@ +#!/usr/bin/python +# +# (c) 2019 Hannes Ljungberg +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: docker_swarm_service_info + +short_description: Retrieves information about docker services from a Swarm Manager + +description: + - Retrieves information about a docker service. + - Essentially returns the output of C(docker service inspect ). + - Must be executed on a host running as Swarm Manager, otherwise the module will fail. + +version_added: "2.8" + +options: + name: + description: + - The name of the service to inspect. + type: str + required: yes +extends_documentation_fragment: + - docker + - docker.docker_py_1_documentation + +author: + - Hannes Ljungberg (@hannseman) + +requirements: + - "L(Docker SDK for Python,https://docker-py.readthedocs.io/en/stable/) >= 2.0.0" + - "Docker API >= 1.24" +''' + +EXAMPLES = ''' +- name: Get info from a service + docker_swarm_service_info: + name: myservice + register: result +''' + +RETURN = ''' +exists: + description: + - Returns whether the service exists. + type: bool + returned: always + sample: true +service: + description: + - A dictionary representing the current state of the service. Matches the C(docker service inspect) output. + - Will be C(none) if service does not exist. + returned: always + type: dict +''' + +from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient + + +def get_service_info(client): + service = client.module.params['name'] + return client.get_service_inspect( + service_id=service, + skip_missing=True + ) + + +def main(): + argument_spec = dict( + name=dict(type='str', required=True), + ) + + client = AnsibleDockerSwarmClient( + argument_spec=argument_spec, + supports_check_mode=True, + min_docker_version='2.0.0', + min_docker_api_version='1.24', + ) + + client.fail_task_if_not_swarm_manager() + + service = get_service_info(client) + + client.module.exit_json( + changed=False, + service=service, + exists=bool(service) + ) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/docker_swarm_service_info/aliases b/test/integration/targets/docker_swarm_service_info/aliases new file mode 100644 index 00000000000..984b0c06db1 --- /dev/null +++ b/test/integration/targets/docker_swarm_service_info/aliases @@ -0,0 +1,7 @@ +shippable/posix/group3 +skip/osx +skip/freebsd +destructive +skip/docker # The tests sometimes make docker daemon unstable; hence, + # we skip all docker-based CI runs to avoid disrupting + # the whole CI system. diff --git a/test/integration/targets/docker_swarm_service_info/meta/main.yml b/test/integration/targets/docker_swarm_service_info/meta/main.yml new file mode 100644 index 00000000000..07da8c6ddae --- /dev/null +++ b/test/integration/targets/docker_swarm_service_info/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_docker diff --git a/test/integration/targets/docker_swarm_service_info/tasks/main.yml b/test/integration/targets/docker_swarm_service_info/tasks/main.yml new file mode 100644 index 00000000000..295634d63c6 --- /dev/null +++ b/test/integration/targets/docker_swarm_service_info/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- include_tasks: test_docker_swarm_service_info.yml + when: docker_py_version is version('2.0.0', '>=') and docker_api_version is version('1.24', '>=') + +- fail: msg="Too old docker / docker-py version to run docker_swarm_service_info tests!" + when: not(docker_py_version is version('2.0.0', '>=') and docker_api_version is version('1.24', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6) diff --git a/test/integration/targets/docker_swarm_service_info/tasks/test_docker_swarm_service_info.yml b/test/integration/targets/docker_swarm_service_info/tasks/test_docker_swarm_service_info.yml new file mode 100644 index 00000000000..0658e409868 --- /dev/null +++ b/test/integration/targets/docker_swarm_service_info/tasks/test_docker_swarm_service_info.yml @@ -0,0 +1,76 @@ +--- + +- name: Generate service base name + set_fact: + service_base_name: "{{ 'ansible-test-%0x' % ((2**32) | random) }}" + +- name: Registering service names + set_fact: + service_name: "{{ service_base_name ~ '-1' }}" + +- block: + - name: Make sure we're not already using Docker swarm + docker_swarm: + state: absent + force: true + + - name: Try to get docker_swarm_service_info when docker is not running in swarm mode + docker_swarm_service_info: + name: "{{ service_name }}" + ignore_errors: yes + register: output + + - name: assert failure when called when swarm is not in use or not run on manager node + assert: + that: + - 'output is failed' + - 'output.msg == "Error running docker swarm module: must run on swarm manager node"' + + - name: Create a Swarm cluster + docker_swarm: + state: present + register: output + + - name: Create services + docker_swarm_service: + name: "{{ service_name }}" + image: alpine:3.8 + + - name: Try to get docker_swarm_service_info for a single service + docker_swarm_service_info: + name: "{{ service_name }}" + register: output + + - name: assert reading reading service info + assert: + that: + - 'output.exists == true' + - 'output.service.ID is string' + - 'output.service.Spec.Name == service_name' + + - name: Create random name + set_fact: + random_service_name: "{{ 'random-service-%0x' % ((2**32) | random) }}" + + - name: Try to get docker_swarm_service_info using random service name as parameter + docker_swarm_service_info: + name: "{{ random_service_name }}" + register: output + + - name: assert reading reading service info + assert: + that: + - 'output.service is none' + - 'output.exists == false' + + always: + - name: Remove services + docker_swarm_service: + name: "{{ service_name }}" + state: absent + ignore_errors: yes + + - name: Remove swarm + docker_swarm: + state: absent + force: true