From 0d9873b81c7e62ce4a3192a78b88504c5bbf4af0 Mon Sep 17 00:00:00 2001 From: Denis Karpelevich Date: Fri, 3 Mar 2023 22:01:57 +0100 Subject: [PATCH 1/4] Allow multiple services creation Adding an option to create multiple services in one go. Adding tests (present/absent/without_skip_host_check) Copied from PR #1054 Signed-off-by: Denis Karpelevich --- plugins/modules/ipaservice.py | 301 ++++++++++++++---- tests/service/create_services_json.yml | 22 ++ tests/service/hosts.sh | 24 ++ tests/service/services.sh | 25 ++ tests/service/services_absent.sh | 22 ++ tests/service/test_services_absent.yml | 32 ++ tests/service/test_services_present.yml | 39 +++ tests/service/test_services_present_slice.yml | 46 +++ .../test_services_without_skip_host_check.yml | 100 ++++++ 9 files changed, 555 insertions(+), 56 deletions(-) create mode 100644 tests/service/create_services_json.yml create mode 100644 tests/service/hosts.sh create mode 100644 tests/service/services.sh create mode 100644 tests/service/services_absent.sh create mode 100644 tests/service/test_services_absent.yml create mode 100644 tests/service/test_services_present.yml create mode 100644 tests/service/test_services_present_slice.yml create mode 100644 tests/service/test_services_without_skip_host_check.yml diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py index a6fa9cb1..5046144a 100644 --- a/plugins/modules/ipaservice.py +++ b/plugins/modules/ipaservice.py @@ -45,6 +45,127 @@ options: elements: str required: true aliases: ["service"] + services: + description: The list of service dicts. + type: list + elements: dict + suboptions: + name: + description: The service to manage + type: str + required: true + aliases: ["service"] + certificate: + description: Base-64 encoded service certificate. + required: false + type: list + elements: str + aliases: ["usercertificate"] + pac_type: + description: Supported PAC type. + required: false + choices: ["MS-PAC", "PAD", "NONE", ""] + type: list + elements: str + aliases: ["pac_type", "ipakrbauthzdata"] + auth_ind: + description: Defines an allow list for Authentication Indicators. + type: list + elements: str + required: false + choices: ["otp", "radius", "pkinit", "hardened", ""] + aliases: ["krbprincipalauthind"] + skip_host_check: + description: Skip checking if host object exists. + required: False + type: bool + force: + description: Force principal name even if host is not in DNS. + required: False + type: bool + requires_pre_auth: + description: Pre-authentication is required for the service. + required: false + type: bool + aliases: ["ipakrbrequirespreauth"] + ok_as_delegate: + description: Client credentials may be delegated to the service. + required: false + type: bool + aliases: ["ipakrbokasdelegate"] + ok_to_auth_as_delegate: + description: Allow service to authenticate on behalf of a client. + required: false + type: bool + aliases: ["ipakrboktoauthasdelegate"] + principal: + description: List of principal aliases for the service. + required: false + type: list + elements: str + aliases: ["krbprincipalname"] + smb: + description: Add a SMB service. + required: false + type: bool + netbiosname: + description: NETBIOS name for the SMB service. + required: false + type: str + host: + description: Host that can manage the service. + required: false + type: list + elements: str + aliases: ["managedby_host"] + allow_create_keytab_user: + description: Users allowed to create a keytab of this host. + required: false + type: list + elements: str + aliases: ["ipaallowedtoperform_write_keys_user"] + allow_create_keytab_group: + description: Groups allowed to create a keytab of this host. + required: false + type: list + elements: str + aliases: ["ipaallowedtoperform_write_keys_group"] + allow_create_keytab_host: + description: Hosts allowed to create a keytab of this host. + required: false + type: list + elements: str + aliases: ["ipaallowedtoperform_write_keys_host"] + allow_create_keytab_hostgroup: + description: Host group allowed to create a keytab of this host. + required: false + type: list + elements: str + aliases: ["ipaallowedtoperform_write_keys_hostgroup"] + allow_retrieve_keytab_user: + description: User allowed to retrieve a keytab of this host. + required: false + type: list + elements: str + aliases: ["ipaallowedtoperform_read_keys_user"] + allow_retrieve_keytab_group: + description: Groups allowed to retrieve a keytab of this host. + required: false + type: list + elements: str + aliases: ["ipaallowedtoperform_read_keys_group"] + allow_retrieve_keytab_host: + description: Hosts allowed to retrieve a keytab of this host. + required: false + type: list + elements: str + aliases: ["ipaallowedtoperform_read_keys_host"] + allow_retrieve_keytab_hostgroup: + description: Host groups allowed to retrieve a keytab of this host. + required: false + type: list + elements: str + aliases: ["ipaallowedtoperform_read_keys_hostgroup"] certificate: description: Base-64 encoded service certificate. required: false @@ -239,6 +360,15 @@ EXAMPLES = """ - host1.example.com - host2.example.com action: member + + # Ensure multiple services are present. + - ipaservice: + ipaadmin_password: SomeADMINpassword + services: + - name: HTTP/www.example.com + host: + - host1.example.com + - name: HTTP/www.service.com """ RETURN = """ @@ -248,6 +378,9 @@ from ansible.module_utils.ansible_freeipa_module import \ IPAAnsibleModule, compare_args_ipa, encode_certificate, \ gen_add_del_lists, gen_add_list, gen_intersection_list, ipalib_errors, \ api_get_realm, to_text +from ansible.module_utils import six +if six.PY3: + unicode = str def find_service(module, name): @@ -321,8 +454,9 @@ def check_parameters(module, state, action, names): 'allow_retrieve_keytab_hostgroup'] if state == 'present': - if len(names) != 1: - module.fail_json(msg="Only one service can be added at a time.") + if names is not None and len(names) != 1: + module.fail_json(msg="Only one service can be added at a time " + "using 'name'.") if action == 'service': invalid = ['delete_continue'] @@ -338,9 +472,6 @@ def check_parameters(module, state, action, names): invalid.append('delete_continue') elif state == 'absent': - if len(names) < 1: - module.fail_json(msg="No name given.") - if action == "service": invalid.extend(invalid_not_member) else: @@ -360,67 +491,85 @@ def check_parameters(module, state, action, names): def init_ansible_module(): + service_spec = dict( + # service attributesstr + certificate=dict(type="list", elements="str", + aliases=['usercertificate'], + default=None, required=False), + principal=dict(type="list", elements="str", + aliases=["krbprincipalname"], default=None), + smb=dict(type="bool", required=False), + netbiosname=dict(type="str", required=False), + pac_type=dict(type="list", elements="str", + aliases=["ipakrbauthzdata"], + choices=["MS-PAC", "PAD", "NONE", ""]), + auth_ind=dict(type="list", elements="str", + aliases=["krbprincipalauthind"], + choices=["otp", "radius", "pkinit", "hardened", ""]), + skip_host_check=dict(type="bool"), + force=dict(type="bool"), + requires_pre_auth=dict( + type="bool", aliases=["ipakrbrequirespreauth"]), + ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"]), + ok_to_auth_as_delegate=dict(type="bool", + aliases=["ipakrboktoauthasdelegate"]), + host=dict(type="list", elements="str", aliases=["managedby_host"], + required=False), + allow_create_keytab_user=dict( + type="list", elements="str", required=False, no_log=False, + aliases=['ipaallowedtoperform_write_keys_user']), + allow_retrieve_keytab_user=dict( + type="list", elements="str", required=False, no_log=False, + aliases=['ipaallowedtoperform_read_keys_user']), + allow_create_keytab_group=dict( + type="list", elements="str", required=False, no_log=False, + aliases=['ipaallowedtoperform_write_keys_group']), + allow_retrieve_keytab_group=dict( + type="list", elements="str", required=False, no_log=False, + aliases=['ipaallowedtoperform_read_keys_group']), + allow_create_keytab_host=dict( + type="list", elements="str", required=False, no_log=False, + aliases=['ipaallowedtoperform_write_keys_host']), + allow_retrieve_keytab_host=dict( + type="list", elements="str", required=False, no_log=False, + aliases=['ipaallowedtoperform_read_keys_host']), + allow_create_keytab_hostgroup=dict( + type="list", elements="str", required=False, no_log=False, + aliases=['ipaallowedtoperform_write_keys_hostgroup']), + allow_retrieve_keytab_hostgroup=dict( + type="list", elements="str", required=False, no_log=False, + aliases=['ipaallowedtoperform_read_keys_hostgroup']), + delete_continue=dict(type="bool", required=False, + aliases=['continue']), + ) ansible_module = IPAAnsibleModule( argument_spec=dict( # general name=dict(type="list", elements="str", aliases=["service"], - required=True), - # service attributesstr - certificate=dict(type="list", elements="str", - aliases=['usercertificate'], - default=None, required=False), - principal=dict(type="list", elements="str", - aliases=["krbprincipalname"], default=None), - smb=dict(type="bool", required=False), - netbiosname=dict(type="str", required=False), - pac_type=dict(type="list", elements="str", - aliases=["ipakrbauthzdata"], - choices=["MS-PAC", "PAD", "NONE", ""]), - auth_ind=dict(type="list", elements="str", - aliases=["krbprincipalauthind"], - choices=["otp", "radius", "pkinit", "hardened", ""]), - skip_host_check=dict(type="bool"), - force=dict(type="bool"), - requires_pre_auth=dict( - type="bool", aliases=["ipakrbrequirespreauth"]), - ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"]), - ok_to_auth_as_delegate=dict(type="bool", - aliases=["ipakrboktoauthasdelegate"]), - host=dict(type="list", elements="str", aliases=["managedby_host"], - required=False), - allow_create_keytab_user=dict( - type="list", elements="str", required=False, no_log=False, - aliases=['ipaallowedtoperform_write_keys_user']), - allow_retrieve_keytab_user=dict( - type="list", elements="str", required=False, no_log=False, - aliases=['ipaallowedtoperform_read_keys_user']), - allow_create_keytab_group=dict( - type="list", elements="str", required=False, no_log=False, - aliases=['ipaallowedtoperform_write_keys_group']), - allow_retrieve_keytab_group=dict( - type="list", elements="str", required=False, no_log=False, - aliases=['ipaallowedtoperform_read_keys_group']), - allow_create_keytab_host=dict( - type="list", elements="str", required=False, no_log=False, - aliases=['ipaallowedtoperform_write_keys_host']), - allow_retrieve_keytab_host=dict( - type="list", elements="str", required=False, no_log=False, - aliases=['ipaallowedtoperform_read_keys_host']), - allow_create_keytab_hostgroup=dict( - type="list", elements="str", required=False, no_log=False, - aliases=['ipaallowedtoperform_write_keys_hostgroup']), - allow_retrieve_keytab_hostgroup=dict( - type="list", elements="str", required=False, no_log=False, - aliases=['ipaallowedtoperform_read_keys_hostgroup']), - delete_continue=dict(type="bool", required=False, - aliases=['continue']), + default=None, required=False), + services=dict(type="list", + default=None, + options=dict( + # Here name is a simple string + name=dict(type="str", required=True, + aliases=["service"]), + # Add service specific parameters + **service_spec + ), + elements='dict', + required=False), # action action=dict(type="str", default="service", choices=["member", "service"]), # state state=dict(type="str", default="present", choices=["present", "absent", "disabled"]), + + # Add service specific parameters for simple use case + **service_spec ), + mutually_exclusive=[["name", "services"]], + required_one_of=[["name", "services"]], supports_check_mode=True, ) @@ -436,6 +585,7 @@ def main(): # general names = ansible_module.params_get("name") + services = ansible_module.params_get("services") # service attributes principal = ansible_module.params_get("principal") @@ -462,8 +612,16 @@ def main(): state = ansible_module.params_get("state") # check parameters + if (names is None or len(names) < 1) and \ + (services is None or len(services) < 1): + ansible_module.fail_json(msg="At least one name or services is " + "required") check_parameters(ansible_module, state, action, names) + # Use services if names is None + if services is not None: + names = services + # Init changed = False @@ -480,8 +638,39 @@ def main(): commands = [] keytab_members = ["user", "group", "host", "hostgroup"] + service_set = set() - for name in names: + for service in names: + if isinstance(service, dict): + name = service.get("name") + if name in service_set: + ansible_module.fail_json( + msg="service '%s' is used more than once" % name) + service_set.add(name) + principal = service.get("principal") + certificate = service.get("certificate") + pac_type = service.get("pac_type") + auth_ind = service.get("auth_ind") + skip_host_check = service.get("skip_host_check") + if skip_host_check and not has_skip_host_check: + ansible_module.fail_json( + msg="Skipping host check is not supported by your IPA " + "version") + force = service.get("force") + requires_pre_auth = service.get("requires_pre_auth") + ok_as_delegate = service.get("ok_as_delegate") + ok_to_auth_as_delegate = service.get("ok_to_auth_as_delegate") + smb = service.get("smb") + netbiosname = service.get("netbiosname") + host = service.get("host") + + delete_continue = service.get("delete_continue") + + elif isinstance(service, (str, unicode)): + name = service + else: + ansible_module.fail_json(msg="Service '%s' is not valid" % + repr(service)) res_find = find_service(ansible_module, name) res_principals = [] diff --git a/tests/service/create_services_json.yml b/tests/service/create_services_json.yml new file mode 100644 index 00000000..197648b5 --- /dev/null +++ b/tests/service/create_services_json.yml @@ -0,0 +1,22 @@ +--- +- name: Create services.json + hosts: localhost + + tasks: + - name: Check if services.json exists + ansible.builtin.stat: + path: services.json + register: register_stat_services + + - name: Create services.json + ansible.builtin.command: /bin/bash services.sh 500 + when: not register_stat_services.stat.exists + + - name: Check if hosts.json exists + ansible.builtin.stat: + path: hosts.json + register: register_stat_hosts + + - name: Create hosts.json + ansible.builtin.command: /bin/bash hosts.sh 500 + when: not register_stat_hosts.stat.exists diff --git a/tests/service/hosts.sh b/tests/service/hosts.sh new file mode 100644 index 00000000..2a64855c --- /dev/null +++ b/tests/service/hosts.sh @@ -0,0 +1,24 @@ +#!/bin/bash -eu + +NUM=${1-1000} +FILE="hosts.json" + +echo "{" > "$FILE" + +echo " \"host_list\": [" >> "$FILE" + +for i in $(seq 1 "$NUM"); do + { + echo " {" + echo " \"name\": \"www.example$i.com\"" + } >> "$FILE" + if [ "$i" -lt "$NUM" ]; then + echo " }," >> "$FILE" + else + echo " }" >> "$FILE" + fi +done + +echo " ]" >> "$FILE" + +echo "}" >> "$FILE" diff --git a/tests/service/services.sh b/tests/service/services.sh new file mode 100644 index 00000000..79f3b38a --- /dev/null +++ b/tests/service/services.sh @@ -0,0 +1,25 @@ +#!/bin/bash -eu + +NUM=${1-1000} +FILE="services.json" + +echo "{" > "$FILE" + +echo " \"service_list\": [" >> "$FILE" + +for i in $(seq 1 "$NUM"); do + { + echo " {" + echo " \"name\": \"HTTP/www.example$i.com\"," + echo " \"principal\": \"host/test.example$i.com\"" + } >> "$FILE" + if [ "$i" -lt "$NUM" ]; then + echo " }," >> "$FILE" + else + echo " }" >> "$FILE" + fi +done + +echo " ]" >> "$FILE" + +echo "}" >> "$FILE" diff --git a/tests/service/services_absent.sh b/tests/service/services_absent.sh new file mode 100644 index 00000000..80d0b796 --- /dev/null +++ b/tests/service/services_absent.sh @@ -0,0 +1,22 @@ +#!/bin/bash -eu + +NUM=1000 +FILE="services_absent.json" + +echo "{" > "$FILE" + +echo " \"services\": [" >> "$FILE" + +for i in $(seq 1 "$NUM"); do + echo " {" >> "$FILE" + echo " \"name\": \"HTTP/www.example$i.com\"," >> "$FILE" + if [ "$i" -lt "$NUM" ]; then + echo " }," >> "$FILE" + else + echo " }" >> "$FILE" + fi +done + +echo " ]" >> "$FILE" + +echo "}" >> "$FILE" diff --git a/tests/service/test_services_absent.yml b/tests/service/test_services_absent.yml new file mode 100644 index 00000000..a212ee24 --- /dev/null +++ b/tests/service/test_services_absent.yml @@ -0,0 +1,32 @@ +--- +- name: Include create_services_json.yml + ansible.builtin.import_playbook: create_services_json.yml + +- name: Test services absent + hosts: ipaserver + become: true + gather_facts: false + + tasks: + - name: Include services.json + ansible.builtin.include_vars: + file: services.json # noqa 505 + + - name: Create dict with service names + ansible.builtin.set_fact: + services_names: "{{ services_names | default([]) + [{'name': item.name}] }}" + loop: "{{ service_list }}" + + - name: Services absent len:{{ service_list | length }} + ipaservice: + ipaadmin_password: SomeADMINpassword + services: "{{ services_names }}" + state: absent + +- name: Remove services.json + hosts: localhost + tasks: + - name: Remove services.json + ansible.builtin.file: + state: absent + path: services.json diff --git a/tests/service/test_services_present.yml b/tests/service/test_services_present.yml new file mode 100644 index 00000000..b8491fb7 --- /dev/null +++ b/tests/service/test_services_present.yml @@ -0,0 +1,39 @@ +--- +- name: Include create_services_json.yml + ansible.builtin.import_playbook: create_services_json.yml + +- name: Test services present + hosts: ipaserver + become: true + gather_facts: false + + tasks: + - name: Include services.json + ansible.builtin.include_vars: + file: services.json # noqa 505 + + - name: Include hosts.json + ansible.builtin.include_vars: + file: hosts.json # noqa 505 + + - name: Hosts present len:{{ host_list | length }} + ipahost: + ipaadmin_password: SomeADMINpassword + hosts: "{{ host_list }}" + + - name: Services present len:{{ service_list | length }} + ipaservice: + ipaadmin_password: SomeADMINpassword + services: "{{ service_list }}" + +- name: Remove services.json + hosts: localhost + tasks: + - name: Remove services.json + ansible.builtin.file: + state: absent + path: services.json + - name: Remove hosts.json + ansible.builtin.file: + state: absent + path: hosts.json diff --git a/tests/service/test_services_present_slice.yml b/tests/service/test_services_present_slice.yml new file mode 100644 index 00000000..d9e7055d --- /dev/null +++ b/tests/service/test_services_present_slice.yml @@ -0,0 +1,46 @@ +--- +- name: Include create_services_json.yml + ansible.builtin.import_playbook: create_services_json.yml + +- name: Test services present slice + hosts: ipaserver + become: true + gather_facts: false + + vars: + slice_size: 500 + tasks: + - name: Include services.json + ansible.builtin.include_vars: + file: services.json # noqa 505 + - name: Include hosts.json + ansible.builtin.include_vars: + file: hosts.json # noqa 505 + - name: Size of services slice. + ansible.builtin.debug: + msg: "{{ service_list | length }}" + - name: Size of hosts slice. + ansible.builtin.debug: + msg: "{{ host_list | length }}" + - name: Hosts present + ipahost: + ipaadmin_password: SomeADMINpassword + hosts: "{{ host_list[item : item + slice_size] }}" + loop: "{{ range(0, service_list | length, slice_size) | list }}" + - name: Services present + ipaservice: + ipaadmin_password: SomeADMINpassword + services: "{{ service_list[item : item + slice_size] }}" + loop: "{{ range(0, service_list | length, slice_size) | list }}" + +- name: Remove services.json + hosts: localhost + tasks: + - name: Remove services.json + ansible.builtin.file: + state: absent + path: services.json + - name: Remove hosts.json + ansible.builtin.file: + state: absent + path: hosts.json diff --git a/tests/service/test_services_without_skip_host_check.yml b/tests/service/test_services_without_skip_host_check.yml new file mode 100644 index 00000000..0cb71281 --- /dev/null +++ b/tests/service/test_services_without_skip_host_check.yml @@ -0,0 +1,100 @@ +--- +- name: Test services without using option skip_host_check + hosts: ipaserver + become: true + + tasks: + # setup + - name: Test services without using option skip_host_check + block: + - name: Setup test environment + ansible.builtin.include_tasks: env_setup.yml + + - name: Services are present + ipaservice: + ipaadmin_password: SomeADMINpassword + services: + - name: "HTTP/{{ svc_fqdn }}" + principal: + - host/test.example.com + - name: "mysvc/{{ host1_fqdn }}" + pac_type: NONE + ok_as_delegate: yes + ok_to_auth_as_delegate: yes + - name: "HTTP/{{ host1_fqdn }}" + allow_create_keytab_user: + - user01 + - user02 + allow_create_keytab_group: + - group01 + - group02 + allow_create_keytab_host: + - "{{ host1_fqdn }}" + - "{{ host2_fqdn }}" + allow_create_keytab_hostgroup: + - hostgroup01 + - hostgroup02 + - name: "mysvc/{{ host2_fqdn }}" + auth_ind: otp,radius + + register: result + failed_when: not result.changed or result.failed + + - name: Services are present again + ipaservice: + ipaadmin_password: SomeADMINpassword + services: + - name: "HTTP/{{ svc_fqdn }}" + - name: "mysvc/{{ host1_fqdn }}" + - name: "HTTP/{{ host1_fqdn }}" + - name: "mysvc/{{ host2_fqdn }}" + register: result + failed_when: result.changed or result.failed + + # failed_when: not result.failed has been added as this test needs to + # fail because two services with the same name should be added in the same + # task. + - name: Duplicate names in services failure test + ipaservice: + ipaadmin_password: SomeADMINpassword + services: + - name: "HTTP/{{ svc_fqdn }}" + - name: "mysvc/{{ host1_fqdn }}" + - name: "HTTP/{{ nohost_fqdn }}" + - name: "HTTP/{{ svc_fqdn }}" + register: result + failed_when: result.changed or not result.failed or "is used more than once" not in result.msg + + - name: Services/name and name 'service' present + ipaservice: + ipaadmin_password: SomeADMINpassword + name: "HTTP/{{ svc_fqdn }}" + services: + - name: "HTTP/{{ svc_fqdn }}" + register: result + failed_when: result.changed or not result.failed or "parameters are mutually exclusive" not in result.msg + + - name: Services/name and name are absent + ipaservice: + ipaadmin_password: SomeADMINpassword + register: result + failed_when: result.changed or not result.failed or "one of the following is required" not in result.msg + + - name: Name is absent + ipaservice: + ipaadmin_password: SomeADMINpassword + name: + register: result + failed_when: result.changed or not result.failed or "At least one name or services is required" not in result.msg + + - name: Only one service can be added at a time using name. + ipaservice: + ipaadmin_password: SomeADMINpassword + name: example.com,example1.com + register: result + failed_when: result.changed or not result.failed or "Only one service can be added at a time using 'name'." not in result.msg + + always: + # cleanup + - name: Cleanup test environment + ansible.builtin.include_tasks: env_cleanup.yml From efe9c686007ac8d924162da3bc543a73d931d44b Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Tue, 6 Jun 2023 12:41:44 +0200 Subject: [PATCH 2/4] ipaservice: Properly Handle certs with leading or trailing white space Any leading or trailing whitespace is removed while adding the certificates with serive_add_cert. To be able to compare the results from service_show with the given certificates we have to remove the white space also. --- plugins/modules/ipaservice.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py index 5046144a..dc5236f2 100644 --- a/plugins/modules/ipaservice.py +++ b/plugins/modules/ipaservice.py @@ -590,6 +590,12 @@ def main(): # service attributes principal = ansible_module.params_get("principal") certificate = ansible_module.params_get("certificate") + # Any leading or trailing whitespace is removed while adding the + # certificate with serive_add_cert. To be able to compare the results + # from service_show with the given certificates we have to remove the + # white space also. + if certificate is not None: + certificate = [cert.strip() for cert in certificate] pac_type = ansible_module.params_get("pac_type", allow_empty_string=True) auth_ind = ansible_module.params_get("auth_ind", allow_empty_string=True) skip_host_check = ansible_module.params_get("skip_host_check") @@ -649,6 +655,12 @@ def main(): service_set.add(name) principal = service.get("principal") certificate = service.get("certificate") + # Any leading or trailing whitespace is removed while adding + # the certificate with serive_add_cert. To be able to compare + # the results from service_show with the given certificates + # we have to remove the white space also. + if certificate is not None: + certificate = [cert.strip() for cert in certificate] pac_type = service.get("pac_type") auth_ind = service.get("auth_ind") skip_host_check = service.get("skip_host_check") From 872c9e4cb253f0fd1ae1ebb0314b86f602dbd0dc Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Tue, 6 Jun 2023 12:45:34 +0200 Subject: [PATCH 3/4] ipaservice: Add Denis Karpelevich to the authors header Denis added the multi service handling code. Therefore he should be listed in the file header. --- plugins/modules/ipaservice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py index dc5236f2..209a0311 100644 --- a/plugins/modules/ipaservice.py +++ b/plugins/modules/ipaservice.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Authors: +# Denis Karpelevich # Rafael Guterres Jeffman # Thomas Woerner # From 09250cb2c5d8b26a45cfbb1e1f65aba9d71d2c82 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Tue, 6 Jun 2023 13:00:26 +0200 Subject: [PATCH 4/4] ipaservice: Updated and new tests for certificates and multi service handling The tests test_services_absent.yml, test_services_present.yml and test_services_present_slice.yml have been updated to use in memory data for testing instead of loading json files. This made is simpler to use variables from the playbook for example for fqdn host names. New tests for certificates with and without trailing new lines have been added for single service and multiple service handling. --- .../test_service_certificate_newline.yml | 200 +++++++++++ .../test_services_certificate_newline.yml | 314 ++++++++++++++++++ tests/service/create_services_json.yml | 22 -- tests/service/generate_test_data.yml | 98 ++++++ tests/service/hosts.sh | 24 -- tests/service/services.sh | 25 -- tests/service/services_absent.sh | 22 -- tests/service/test_services_absent.yml | 23 +- tests/service/test_services_present.yml | 72 ++-- tests/service/test_services_present_slice.yml | 91 +++-- 10 files changed, 735 insertions(+), 156 deletions(-) create mode 100644 tests/service/certificate/test_service_certificate_newline.yml create mode 100644 tests/service/certificate/test_services_certificate_newline.yml delete mode 100644 tests/service/create_services_json.yml create mode 100644 tests/service/generate_test_data.yml delete mode 100644 tests/service/hosts.sh delete mode 100644 tests/service/services.sh delete mode 100644 tests/service/services_absent.sh diff --git a/tests/service/certificate/test_service_certificate_newline.yml b/tests/service/certificate/test_service_certificate_newline.yml new file mode 100644 index 00000000..01f7c536 --- /dev/null +++ b/tests/service/certificate/test_service_certificate_newline.yml @@ -0,0 +1,200 @@ +--- +- name: Test service with certificates with and without trailing new line + hosts: ipaserver + become: true + + tasks: + - name: Include tasks ../../env_freeipa_facts.yml + ansible.builtin.include_tasks: ../../env_freeipa_facts.yml + + - name: Setup test environment + ansible.builtin.include_tasks: ../env_vars.yml + + - name: Generate self-signed certificates. + ansible.builtin.shell: + cmd: | + openssl req -x509 -newkey rsa:2048 -days 365 -nodes -keyout "private{{ item }}.key" -out "cert{{ item }}.pem" -subj '/CN=test' + openssl x509 -outform der -in "cert{{ item }}.pem" -out "cert{{ item }}.der" + base64 "cert{{ item }}.der" -w5000 > "cert{{ item }}.b64" + with_items: [1, 2, 3] + become: no + delegate_to: localhost + + # The rstrip=False for lookup will add keep the newline at the end of the + # cert and this is automatically revoved in IPA, This is an additional + # test of ipaservice later on to behave correctly in both cases. + - name: Set fact cert1,2,3 from lookup + ansible.builtin.set_fact: + cert1: "{{ lookup('file', 'cert1.b64', rstrip=False) }}" + cert2: "{{ lookup('file', 'cert2.b64', rstrip=True) }}" + cert3: "{{ lookup('file', 'cert3.b64', rstrip=False) }}" + + - name: Host {{ svc_fqdn }} absent + ipahost: + ipaadmin_password: SomeADMINpassword + name: "{{ svc_fqdn }}" + state: absent + + - name: Host {{ svc_fqdn }} present + ipahost: + ipaadmin_password: SomeADMINpassword + name: "{{ svc_fqdn }}" + force: true + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} absent + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + continue: true + state: absent + + - name: Service FOO/{{ svc_fqdn }} present + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + force: yes + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} certs 1,2 members present + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert1 }}" + - "{{ cert2 }}" + action: member + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} certs 1,2 members present again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert1 }}" + - "{{ cert2 }}" + action: member + register: result + failed_when: result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} certs 1,2,3 members present + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert1 }}" + - "{{ cert2 }}" + - "{{ cert3 }}" + action: member + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} certs 1,2,3 members present again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert1 }}" + - "{{ cert2 }}" + - "{{ cert3 }}" + action: member + register: result + failed_when: result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} certs 2,3 member absent + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert2 }}" + - "{{ cert3 }}" + state: absent + action: member + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} certs 2,3 member absent again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert2 }}" + - "{{ cert3 }}" + action: member + state: absent + register: result + failed_when: result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} certs 1,2,3 members absent + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert1 }}" + - "{{ cert2 }}" + - "{{ cert3 }}" + action: member + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} certs 1,2,3 members absent again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert1 }}" + - "{{ cert2 }}" + - "{{ cert3 }}" + action: member + state: absent + register: result + failed_when: result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} absent + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + continue: true + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO/{{ svc_fqdn }} absent again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "FOO/{{ svc_fqdn }}" + continue: true + state: absent + register: result + failed_when: result.changed or result.failed + + - name: Host {{ svc_fqdn }} absent + ipahost: + ipaadmin_password: SomeADMINpassword + name: "{{ svc_fqdn }}" + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Remove certificate files. # noqa: deprecated-command-syntax + ansible.builtin.shell: + cmd: rm -f "private{{ item }}.key" "cert{{ item }}.pem" "cert{{ item }}.der" "cert{{ item }}.b64" + with_items: [1, 2, 3] + become: no + delegate_to: localhost diff --git a/tests/service/certificate/test_services_certificate_newline.yml b/tests/service/certificate/test_services_certificate_newline.yml new file mode 100644 index 00000000..33815a01 --- /dev/null +++ b/tests/service/certificate/test_services_certificate_newline.yml @@ -0,0 +1,314 @@ +--- +- name: Test services with certificates with and without trailing new line + hosts: ipaserver + become: true + + tasks: + - name: Include tasks ../../env_freeipa_facts.yml + ansible.builtin.include_tasks: ../../env_freeipa_facts.yml + + - name: Setup test environment + ansible.builtin.include_tasks: ../env_vars.yml + + - name: Generate self-signed certificates. + ansible.builtin.shell: + cmd: | + openssl req -x509 -newkey rsa:2048 -days 365 -nodes -keyout "private{{ item }}.key" -out "cert{{ item }}.pem" -subj '/CN=test' + openssl x509 -outform der -in "cert{{ item }}.pem" -out "cert{{ item }}.der" + base64 "cert{{ item }}.der" -w5000 > "cert{{ item }}.b64" + with_items: [11, 12, 13, 21, 22, 23, 31, 32, 33] + become: no + delegate_to: localhost + + # The rstrip=False for lookup will add keep the newline at the end of the + # cert and this is automatically revoved in IPA, This is an additional + # test of ipaservice later on to behave correctly in both cases. + - name: Set fact for certs 11,12,13,21,22,23,31,32,33 from lookup + ansible.builtin.set_fact: + cert11: "{{ lookup('file', 'cert11.b64', rstrip=True) }}" + cert12: "{{ lookup('file', 'cert12.b64', rstrip=False) }}" + cert13: "{{ lookup('file', 'cert13.b64', rstrip=True) }}" + cert21: "{{ lookup('file', 'cert21.b64', rstrip=False) }}" + cert22: "{{ lookup('file', 'cert22.b64', rstrip=False) }}" + cert23: "{{ lookup('file', 'cert23.b64', rstrip=True) }}" + cert31: "{{ lookup('file', 'cert31.b64', rstrip=False) }}" + cert32: "{{ lookup('file', 'cert32.b64', rstrip=True) }}" + cert33: "{{ lookup('file', 'cert33.b64', rstrip=False) }}" + + - name: Services FOO,BAR,BAZ/{{ svc_fqdn }} absent + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: + - "FOO/{{ svc_fqdn }}" + - "BAR/{{ svc_fqdn }}" + - "BAZ/{{ svc_fqdn }}" + continue: true + state: absent + + - name: Host {{ svc_fqdn }} absent + ipahost: + ipaadmin_password: SomeADMINpassword + name: "{{ svc_fqdn }}" + state: absent + + - name: Host {{ svc_fqdn }} present + ipahost: + ipaadmin_password: SomeADMINpassword + name: "{{ svc_fqdn }}" + force: true + register: result + failed_when: not result.changed or result.failed + + - name: Services FOO,BAR,BAZ/{{ svc_fqdn }} present + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + force: yes + - name: "BAR/{{ svc_fqdn }}" + force: yes + - name: "BAZ/{{ svc_fqdn }}" + force: yes + register: result + failed_when: not result.changed or result.failed + + - name: Services FOO,BAR,BAZ/{{ svc_fqdn }} present + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + force: yes + - name: "BAR/{{ svc_fqdn }}" + force: yes + - name: "BAZ/{{ svc_fqdn }}" + force: yes + register: result + failed_when: result.changed or result.failed + + - name: Service FOO,BAR,BAZ/{{ svc_fqdn }} certs x1,x2 members present + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert11 }}" + - "{{ cert12 }}" + - name: "BAR/{{ svc_fqdn }}" + certificate: + - "{{ cert21 }}" + - "{{ cert22 }}" + - name: "BAZ/{{ svc_fqdn }}" + certificate: + - "{{ cert31 }}" + - "{{ cert32 }}" + action: member + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO,BAR,BAZ/{{ svc_fqdn }} certs x1,x2 members present again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert11 }}" + - "{{ cert12 }}" + - name: "BAR/{{ svc_fqdn }}" + certificate: + - "{{ cert21 }}" + - "{{ cert22 }}" + - name: "BAZ/{{ svc_fqdn }}" + certificate: + - "{{ cert31 }}" + - "{{ cert32 }}" + action: member + register: result + failed_when: result.changed or result.failed + + - name: Service FOO,BAR,BAZ/{{ svc_fqdn }} certs x1,x2,x3 members present + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert11 }}" + - "{{ cert12 }}" + - "{{ cert13 }}" + - name: "BAR/{{ svc_fqdn }}" + certificate: + - "{{ cert21 }}" + - "{{ cert22 }}" + - "{{ cert23 }}" + - name: "BAZ/{{ svc_fqdn }}" + certificate: + - "{{ cert31 }}" + - "{{ cert32 }}" + - "{{ cert33 }}" + action: member + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO,BAR,BAZ/{{ svc_fqdn }} certs x1,x2,x3 members present again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert11 }}" + - "{{ cert12 }}" + - "{{ cert13 }}" + - name: "BAR/{{ svc_fqdn }}" + certificate: + - "{{ cert21 }}" + - "{{ cert22 }}" + - "{{ cert23 }}" + - name: "BAZ/{{ svc_fqdn }}" + certificate: + - "{{ cert31 }}" + - "{{ cert32 }}" + - "{{ cert33 }}" + action: member + register: result + failed_when: result.changed or result.failed + + - name: Service FOO,BAR,BAZ/{{ svc_fqdn }} certs x2,x3 members absent + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert12 }}" + - "{{ cert13 }}" + - name: "BAR/{{ svc_fqdn }}" + certificate: + - "{{ cert22 }}" + - "{{ cert23 }}" + - name: "BAZ/{{ svc_fqdn }}" + certificate: + - "{{ cert32 }}" + - "{{ cert33 }}" + action: member + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO,BAR,BAZ/{{ svc_fqdn }} certs x2,x3 members absent, again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert12 }}" + - "{{ cert13 }}" + - name: "BAR/{{ svc_fqdn }}" + certificate: + - "{{ cert22 }}" + - "{{ cert23 }}" + - name: "BAZ/{{ svc_fqdn }}" + certificate: + - "{{ cert32 }}" + - "{{ cert33 }}" + action: member + state: absent + register: result + failed_when: result.changed or result.failed + + - name: Service FOO,BAR,BAZ/{{ svc_fqdn }} certs x1,x2,x3 members absent + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert11 }}" + - "{{ cert12 }}" + - "{{ cert13 }}" + - name: "BAR/{{ svc_fqdn }}" + certificate: + - "{{ cert21 }}" + - "{{ cert22 }}" + - "{{ cert23 }}" + - name: "BAZ/{{ svc_fqdn }}" + certificate: + - "{{ cert31 }}" + - "{{ cert32 }}" + - "{{ cert33 }}" + action: member + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Service FOO,BAR,BAZ/{{ svc_fqdn }} certs x1,x2,x3 members absent, again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + services: + - name: "FOO/{{ svc_fqdn }}" + certificate: + - "{{ cert11 }}" + - "{{ cert12 }}" + - "{{ cert13 }}" + - name: "BAR/{{ svc_fqdn }}" + certificate: + - "{{ cert21 }}" + - "{{ cert22 }}" + - "{{ cert23 }}" + - name: "BAZ/{{ svc_fqdn }}" + certificate: + - "{{ cert31 }}" + - "{{ cert32 }}" + - "{{ cert33 }}" + action: member + state: absent + register: result + failed_when: result.changed or result.failed + + - name: Services FOO,BAR,BAZ/{{ svc_fqdn }} absent + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: + - "FOO/{{ svc_fqdn }}" + - "BAR/{{ svc_fqdn }}" + - "BAZ/{{ svc_fqdn }}" + continue: true + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Services FOO,BAR,BAZ/{{ svc_fqdn }} absent, again + ipaservice: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: + - "FOO/{{ svc_fqdn }}" + - "BAR/{{ svc_fqdn }}" + - "BAZ/{{ svc_fqdn }}" + continue: true + state: absent + register: result + failed_when: result.changed or result.failed + + - name: Host {{ svc_fqdn }} absent + ipahost: + ipaadmin_password: SomeADMINpassword + name: "{{ svc_fqdn }}" + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Remove certificate files. # noqa: deprecated-command-syntax + ansible.builtin.shell: + cmd: rm -f "private{{ item }}.key" "cert{{ item }}.pem" "cert{{ item }}.der" "cert{{ item }}.b64" + with_items: [11, 12, 13, 21, 22, 23, 31, 32, 33] + become: no + delegate_to: localhost diff --git a/tests/service/create_services_json.yml b/tests/service/create_services_json.yml deleted file mode 100644 index 197648b5..00000000 --- a/tests/service/create_services_json.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -- name: Create services.json - hosts: localhost - - tasks: - - name: Check if services.json exists - ansible.builtin.stat: - path: services.json - register: register_stat_services - - - name: Create services.json - ansible.builtin.command: /bin/bash services.sh 500 - when: not register_stat_services.stat.exists - - - name: Check if hosts.json exists - ansible.builtin.stat: - path: hosts.json - register: register_stat_hosts - - - name: Create hosts.json - ansible.builtin.command: /bin/bash hosts.sh 500 - when: not register_stat_hosts.stat.exists diff --git a/tests/service/generate_test_data.yml b/tests/service/generate_test_data.yml new file mode 100644 index 00000000..b046617f --- /dev/null +++ b/tests/service/generate_test_data.yml @@ -0,0 +1,98 @@ +# Generate lists for hosts and services +--- +- name: Get Domain from server name + ansible.builtin.set_fact: + ipaserver_domain: "{{ ansible_facts['fqdn'].split('.')[1:] | join('.') }}" + when: ipaserver_domain is not defined + +- name: Create present services.json data + ansible.builtin.shell: | + echo "[" + for i in $(seq 1 "{{ NUM }}"); do + echo " {" + echo " \"name\": \"HTTP/www$i.{{ DOMAIN }}\"," + echo " \"principal\": \"host/test$i.{{ DOMAIN }}\"," + echo " \"force\": \"true\"" + if [ "$i" -lt "{{ NUM }}" ]; then + echo " }," + else + echo " }" + fi + done + echo "]" + vars: + NUM: 500 + DOMAIN: "{{ ipaserver_domain }}" + register: command + +- name: Set service_list + ansible.builtin.set_fact: + service_list: "{{ command.stdout | from_json }}" + +- name: Create absent services.json data + ansible.builtin.shell: | + echo "[" + for i in $(seq 1 "{{ NUM }}"); do + echo " {" + echo " \"name\": \"HTTP/www$i.{{ DOMAIN }}\"," + echo " \"continue\": \"true\"" + if [ "$i" -lt "{{ NUM }}" ]; then + echo " }," + else + echo " }" + fi + done + echo "]" + vars: + NUM: 500 + DOMAIN: "{{ ipaserver_domain }}" + register: command + +- name: Set service_absent_list + ansible.builtin.set_fact: + service_absent_list: "{{ command.stdout | from_json }}" + +- name: Create present hosts.json data + ansible.builtin.shell: | + echo "[" + for i in $(seq 1 "{{ NUM }}"); do + echo " {" + echo " \"name\": \"www$i.{{ DOMAIN }}\"," + echo " \"force\": \"true\"" + if [ "$i" -lt "{{ NUM }}" ]; then + echo " }," + else + echo " }" + fi + done + echo "]" + vars: + NUM: 500 + DOMAIN: "{{ ipaserver_domain }}" + register: command + +- name: Set host_list + ansible.builtin.set_fact: + host_list: "{{ command.stdout | from_json }}" + +- name: Create absent hosts.json data + ansible.builtin.shell: | + echo "[" + for i in $(seq 1 "{{ NUM }}"); do + echo " {" + echo " \"name\": \"www$i.{{ DOMAIN }}\"" + if [ "$i" -lt "{{ NUM }}" ]; then + echo " }," + else + echo " }" + fi + done + echo "]" + vars: + NUM: 500 + DOMAIN: "{{ ipaserver_domain }}" + register: command + +- name: Set host_absent_list + ansible.builtin.set_fact: + host_absent_list: "{{ command.stdout | from_json }}" diff --git a/tests/service/hosts.sh b/tests/service/hosts.sh deleted file mode 100644 index 2a64855c..00000000 --- a/tests/service/hosts.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -eu - -NUM=${1-1000} -FILE="hosts.json" - -echo "{" > "$FILE" - -echo " \"host_list\": [" >> "$FILE" - -for i in $(seq 1 "$NUM"); do - { - echo " {" - echo " \"name\": \"www.example$i.com\"" - } >> "$FILE" - if [ "$i" -lt "$NUM" ]; then - echo " }," >> "$FILE" - else - echo " }" >> "$FILE" - fi -done - -echo " ]" >> "$FILE" - -echo "}" >> "$FILE" diff --git a/tests/service/services.sh b/tests/service/services.sh deleted file mode 100644 index 79f3b38a..00000000 --- a/tests/service/services.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -eu - -NUM=${1-1000} -FILE="services.json" - -echo "{" > "$FILE" - -echo " \"service_list\": [" >> "$FILE" - -for i in $(seq 1 "$NUM"); do - { - echo " {" - echo " \"name\": \"HTTP/www.example$i.com\"," - echo " \"principal\": \"host/test.example$i.com\"" - } >> "$FILE" - if [ "$i" -lt "$NUM" ]; then - echo " }," >> "$FILE" - else - echo " }" >> "$FILE" - fi -done - -echo " ]" >> "$FILE" - -echo "}" >> "$FILE" diff --git a/tests/service/services_absent.sh b/tests/service/services_absent.sh deleted file mode 100644 index 80d0b796..00000000 --- a/tests/service/services_absent.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -eu - -NUM=1000 -FILE="services_absent.json" - -echo "{" > "$FILE" - -echo " \"services\": [" >> "$FILE" - -for i in $(seq 1 "$NUM"); do - echo " {" >> "$FILE" - echo " \"name\": \"HTTP/www.example$i.com\"," >> "$FILE" - if [ "$i" -lt "$NUM" ]; then - echo " }," >> "$FILE" - else - echo " }" >> "$FILE" - fi -done - -echo " ]" >> "$FILE" - -echo "}" >> "$FILE" diff --git a/tests/service/test_services_absent.yml b/tests/service/test_services_absent.yml index a212ee24..473cc8e6 100644 --- a/tests/service/test_services_absent.yml +++ b/tests/service/test_services_absent.yml @@ -1,32 +1,15 @@ --- -- name: Include create_services_json.yml - ansible.builtin.import_playbook: create_services_json.yml - - name: Test services absent hosts: ipaserver become: true gather_facts: false tasks: - - name: Include services.json - ansible.builtin.include_vars: - file: services.json # noqa 505 - - - name: Create dict with service names - ansible.builtin.set_fact: - services_names: "{{ services_names | default([]) + [{'name': item.name}] }}" - loop: "{{ service_list }}" + - name: Include generate_test_data.yml + ansible.builtin.include_tasks: generate_test_data.yml - name: Services absent len:{{ service_list | length }} ipaservice: ipaadmin_password: SomeADMINpassword - services: "{{ services_names }}" + services: "{{ service_absent_list }}" state: absent - -- name: Remove services.json - hosts: localhost - tasks: - - name: Remove services.json - ansible.builtin.file: - state: absent - path: services.json diff --git a/tests/service/test_services_present.yml b/tests/service/test_services_present.yml index b8491fb7..338737eb 100644 --- a/tests/service/test_services_present.yml +++ b/tests/service/test_services_present.yml @@ -1,39 +1,71 @@ --- -- name: Include create_services_json.yml - ansible.builtin.import_playbook: create_services_json.yml - - name: Test services present hosts: ipaserver become: true - gather_facts: false + gather_facts: true tasks: - - name: Include services.json - ansible.builtin.include_vars: - file: services.json # noqa 505 - - - name: Include hosts.json - ansible.builtin.include_vars: - file: hosts.json # noqa 505 + - name: Include generate_test_data.yml + ansible.builtin.include_tasks: generate_test_data.yml - name: Hosts present len:{{ host_list | length }} ipahost: ipaadmin_password: SomeADMINpassword hosts: "{{ host_list }}" + force: true + register: result + failed_when: not result.changed or result.failed + + - name: Hosts present len:{{ host_list | length }}, again + ipahost: + ipaadmin_password: SomeADMINpassword + hosts: "{{ host_list }}" + force: true + register: result + failed_when: result.changed or result.failed - name: Services present len:{{ service_list | length }} ipaservice: ipaadmin_password: SomeADMINpassword services: "{{ service_list }}" + register: result + failed_when: not result.changed or result.failed -- name: Remove services.json - hosts: localhost - tasks: - - name: Remove services.json - ansible.builtin.file: + - name: Services present len:{{ service_list | length }}, again + ipaservice: + ipaadmin_password: SomeADMINpassword + services: "{{ service_list }}" + register: result + failed_when: result.changed or result.failed + + - name: Services absent len:{{ service_list | length }} + ipaservice: + ipaadmin_password: SomeADMINpassword + services: "{{ service_absent_list }}" state: absent - path: services.json - - name: Remove hosts.json - ansible.builtin.file: + register: result + failed_when: not result.changed or result.failed + + - name: Services absent len:{{ service_list | length }}, again + ipaservice: + ipaadmin_password: SomeADMINpassword + services: "{{ service_absent_list }}" state: absent - path: hosts.json + register: result + failed_when: result.changed or result.failed + + - name: Hosts absent len:{{ host_list | length }} + ipahost: + ipaadmin_password: SomeADMINpassword + hosts: "{{ host_absent_list }}" + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Hosts absent len:{{ host_list | length }}, again + ipahost: + ipaadmin_password: SomeADMINpassword + hosts: "{{ host_absent_list }}" + state: absent + register: result + failed_when: result.changed or result.failed diff --git a/tests/service/test_services_present_slice.yml b/tests/service/test_services_present_slice.yml index d9e7055d..6a500a39 100644 --- a/tests/service/test_services_present_slice.yml +++ b/tests/service/test_services_present_slice.yml @@ -1,46 +1,91 @@ --- -- name: Include create_services_json.yml - ansible.builtin.import_playbook: create_services_json.yml - - name: Test services present slice hosts: ipaserver become: true - gather_facts: false + gather_facts: true vars: - slice_size: 500 + slice_size: 100 tasks: - - name: Include services.json - ansible.builtin.include_vars: - file: services.json # noqa 505 - - name: Include hosts.json - ansible.builtin.include_vars: - file: hosts.json # noqa 505 - - name: Size of services slice. + - name: Include generate_test_data.yml + ansible.builtin.include_tasks: generate_test_data.yml + + - name: Size of slice + ansible.builtin.debug: + msg: "{{ slice_size }}" + + - name: Size of services list ansible.builtin.debug: msg: "{{ service_list | length }}" - - name: Size of hosts slice. + + - name: Size of hosts list ansible.builtin.debug: msg: "{{ host_list | length }}" + - name: Hosts present ipahost: ipaadmin_password: SomeADMINpassword hosts: "{{ host_list[item : item + slice_size] }}" - loop: "{{ range(0, service_list | length, slice_size) | list }}" + loop: "{{ range(0, host_list | length, slice_size) | list }}" + register: result + failed_when: not result.changed or result.failed + + - name: Hosts present, again + ipahost: + ipaadmin_password: SomeADMINpassword + hosts: "{{ host_list[item : item + slice_size] }}" + loop: "{{ range(0, host_list | length, slice_size) | list }}" + register: result + failed_when: result.changed or result.failed + - name: Services present ipaservice: ipaadmin_password: SomeADMINpassword services: "{{ service_list[item : item + slice_size] }}" loop: "{{ range(0, service_list | length, slice_size) | list }}" + register: result + failed_when: not result.changed or result.failed -- name: Remove services.json - hosts: localhost - tasks: - - name: Remove services.json - ansible.builtin.file: + - name: Services present, again + ipaservice: + ipaadmin_password: SomeADMINpassword + services: "{{ service_list[item : item + slice_size] }}" + loop: "{{ range(0, service_list | length, slice_size) | list }}" + register: result + failed_when: result.changed or result.failed + + - name: Services absent + ipaservice: + ipaadmin_password: SomeADMINpassword + services: "{{ service_absent_list[item : item + slice_size] }}" state: absent - path: services.json - - name: Remove hosts.json - ansible.builtin.file: + loop: "{{ range(0, service_absent_list | length, slice_size) | list }}" + register: result + failed_when: not result.changed or result.failed + + - name: Services absent, again + ipaservice: + ipaadmin_password: SomeADMINpassword + services: "{{ service_absent_list[item : item + slice_size] }}" state: absent - path: hosts.json + loop: "{{ range(0, service_absent_list | length, slice_size) | list }}" + register: result + failed_when: result.changed or result.failed + + - name: Hosts absent + ipahost: + ipaadmin_password: SomeADMINpassword + hosts: "{{ host_absent_list[item : item + slice_size] }}" + state: absent + loop: "{{ range(0, host_absent_list | length, slice_size) | list }}" + register: result + failed_when: not result.changed or result.failed + + - name: Hosts absent, again + ipahost: + ipaadmin_password: SomeADMINpassword + hosts: "{{ host_absent_list[item : item + slice_size] }}" + state: absent + loop: "{{ range(0, host_absent_list | length, slice_size) | list }}" + register: result + failed_when: result.changed or result.failed