From e76d240dbd2aab9bd0596534949fe6a2603ca901 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Wed, 18 Mar 2026 17:55:19 +0100 Subject: [PATCH 01/49] test --- .github/workflows/test_plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 743f9600..f045b10e 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -23,7 +23,7 @@ on: - 'molecule/plugins/**' - '.config/pep8.yml' - 'tests/**' - +# test jobs: pep8: runs-on: ubuntu-latest From 91cbd14a6fb2281179981f8d45d98d36780d41d3 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Wed, 18 Mar 2026 18:31:15 +0100 Subject: [PATCH 02/49] test2 --- .github/workflows/test_plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index f045b10e..b57a8c45 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -34,7 +34,7 @@ jobs: - name: Set up Python 3. uses: actions/setup-python@v6 with: - python-version: '3.x' + python-version: '3.9' - name: Install test dependencies. run: | From 87dcb2f8259553f47305da4380c5ba83bc29b1da Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Wed, 18 Mar 2026 19:16:51 +0100 Subject: [PATCH 03/49] test3 --- .github/workflows/test_plugins.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index b57a8c45..a42a260e 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -17,12 +17,12 @@ on: - 'feature/**' - 'fix/**' - '!doc/**' - paths: - - 'plugins/**' - - '.github/workflows/test_plugins.yml' - - 'molecule/plugins/**' - - '.config/pep8.yml' - - 'tests/**' +# paths: +# - 'plugins/**' +# - '.github/workflows/test_plugins.yml' +# - 'molecule/plugins/**' +# - '.config/pep8.yml' +# - 'tests/**' # test jobs: pep8: From cb5dda442eff0e6bc186f9f08192052c6365e139 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 08:50:11 +0100 Subject: [PATCH 04/49] Test 4 --- .github/workflows/test_roles_pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_roles_pr.yml b/.github/workflows/test_roles_pr.yml index 7493fa47..173e26c4 100644 --- a/.github/workflows/test_roles_pr.yml +++ b/.github/workflows/test_roles_pr.yml @@ -12,8 +12,8 @@ on: - info - warning - debug - pull_request: - merge_group: +# pull_request: +# merge_group: jobs: lint_full: From 81ae8ca0cb81f1365b0936667aee428e91843a34 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 09:01:06 +0100 Subject: [PATCH 05/49] test5 --- .github/workflows/test_plugins.yml | 12 ++++++------ plugins/module_utils/api.py | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index a42a260e..b57a8c45 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -17,12 +17,12 @@ on: - 'feature/**' - 'fix/**' - '!doc/**' -# paths: -# - 'plugins/**' -# - '.github/workflows/test_plugins.yml' -# - 'molecule/plugins/**' -# - '.config/pep8.yml' -# - 'tests/**' + paths: + - 'plugins/**' + - '.github/workflows/test_plugins.yml' + - 'molecule/plugins/**' + - '.config/pep8.yml' + - 'tests/**' # test jobs: pep8: diff --git a/plugins/module_utils/api.py b/plugins/module_utils/api.py index 6dada178..1a74d9b3 100644 --- a/plugins/module_utils/api.py +++ b/plugins/module_utils/api.py @@ -13,3 +13,5 @@ def new_client_basic_auth(host, auth_user, auth_pass, ca_certs, verify_certs) -> ctx.check_hostname = False ctx.verify_mode = False return Elasticsearch(hosts=[host], basic_auth=(auth_user, auth_pass), ssl_context=ctx, verify_certs=verify_certs) + +# test From b220d0192a1f97319dee192a92ab3999b011e0b9 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 09:03:25 +0100 Subject: [PATCH 06/49] test 6 --- .github/workflows/test_plugins.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index b57a8c45..278f3894 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -13,16 +13,16 @@ on: - warning - debug pull_request: - branches: - - 'feature/**' - - 'fix/**' - - '!doc/**' - paths: - - 'plugins/**' - - '.github/workflows/test_plugins.yml' - - 'molecule/plugins/**' - - '.config/pep8.yml' - - 'tests/**' +# branches: +# - 'feature/**' +# - 'fix/**' +# - '!doc/**' +# paths: +# - 'plugins/**' +# - '.github/workflows/test_plugins.yml' +# - 'molecule/plugins/**' +# - '.config/pep8.yml' +# - 'tests/**' # test jobs: pep8: From 13f36f63a318f56466de64b617ec7a3e8b18a44b Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 09:07:38 +0100 Subject: [PATCH 07/49] test 7 --- .github/workflows/test_plugins.yml | 12 ++++++------ plugins/module_utils/api.py | 2 -- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 278f3894..439830a0 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -17,12 +17,12 @@ on: # - 'feature/**' # - 'fix/**' # - '!doc/**' -# paths: -# - 'plugins/**' -# - '.github/workflows/test_plugins.yml' -# - 'molecule/plugins/**' -# - '.config/pep8.yml' -# - 'tests/**' + paths: + - 'plugins/**' + - '.github/workflows/test_plugins.yml' + - 'molecule/plugins/**' + - '.config/pep8.yml' + - 'tests/**' # test jobs: pep8: diff --git a/plugins/module_utils/api.py b/plugins/module_utils/api.py index 1a74d9b3..6dada178 100644 --- a/plugins/module_utils/api.py +++ b/plugins/module_utils/api.py @@ -13,5 +13,3 @@ def new_client_basic_auth(host, auth_user, auth_pass, ca_certs, verify_certs) -> ctx.check_hostname = False ctx.verify_mode = False return Elasticsearch(hosts=[host], basic_auth=(auth_user, auth_pass), ssl_context=ctx, verify_certs=verify_certs) - -# test From 5a34d290ff070b2f1524eb522b399f3df0b37b23 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 09:09:42 +0100 Subject: [PATCH 08/49] test 8 --- .github/workflows/test_plugins.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 439830a0..1f902de1 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -24,6 +24,7 @@ on: - '.config/pep8.yml' - 'tests/**' # test +# test 8 jobs: pep8: runs-on: ubuntu-latest From 62a6c770202777a86845cc1828f52842f37bebba Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 09:12:11 +0100 Subject: [PATCH 09/49] Test 19 --- .github/workflows/test_plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 1f902de1..8fad754a 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -13,8 +13,8 @@ on: - warning - debug pull_request: -# branches: -# - 'feature/**' + branches: + - 'feature/**' # - 'fix/**' # - '!doc/**' paths: From e67c82d71f08448a37aef590304afcaf763b591d Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 09:14:21 +0100 Subject: [PATCH 10/49] Test 20 --- .github/workflows/test_plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 8fad754a..662db801 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -14,7 +14,7 @@ on: - debug pull_request: branches: - - 'feature/**' + - 'main/**' # - 'fix/**' # - '!doc/**' paths: From 06cb93e2d3f229f1f1baf0a3d330e3f418c12b45 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 09:31:39 +0100 Subject: [PATCH 11/49] test 21 --- .github/workflows/test_plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 662db801..1fed83bc 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -12,9 +12,9 @@ on: - info - warning - debug - pull_request: + push: branches: - - 'main/**' + - 'feature/**' # - 'fix/**' # - '!doc/**' paths: From 272e95d9cf52a226a0da3c19931995cc1f033e54 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 09:44:44 +0100 Subject: [PATCH 12/49] test 22 --- .github/workflows/test_plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 1fed83bc..168831e4 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -12,9 +12,9 @@ on: - info - warning - debug - push: + pull_request: branches: - - 'feature/**' + - 'main' # - 'fix/**' # - '!doc/**' paths: From 80768281ebef16803c4f4b86b8d299eb4db635fd Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 09:54:09 +0100 Subject: [PATCH 13/49] Test 23 --- .github/workflows/test_plugins.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 168831e4..f9ebd79e 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -15,16 +15,14 @@ on: pull_request: branches: - 'main' -# - 'fix/**' -# - '!doc/**' + paths: - 'plugins/**' - '.github/workflows/test_plugins.yml' - 'molecule/plugins/**' - '.config/pep8.yml' - 'tests/**' -# test -# test 8 + jobs: pep8: runs-on: ubuntu-latest From 72a58a5328c2bd2912ddd81e8b093e6858e5fefd Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 11:36:48 +0100 Subject: [PATCH 14/49] Accept long lines in plugins --- .config/pep8.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/pep8.cfg b/.config/pep8.cfg index 3faf701d..8a0496d8 100644 --- a/.config/pep8.cfg +++ b/.config/pep8.cfg @@ -1,4 +1,4 @@ [pep8] ignore = E402, E123 # It's fine to have line-length of 99 -max-line-length = 99 +max-line-length = 150 From d958e993cd0ba8fb085f4ee3b2fc968b5989b7cc Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 11:39:14 +0100 Subject: [PATCH 15/49] Fix white spaces --- plugins/module_utils/elasticsearch_role.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/module_utils/elasticsearch_role.py b/plugins/module_utils/elasticsearch_role.py index 77504af1..bbf7d97f 100644 --- a/plugins/module_utils/elasticsearch_role.py +++ b/plugins/module_utils/elasticsearch_role.py @@ -23,12 +23,12 @@ def __init__(self, result, role_name, cluster, indicies, state, host, auth_user, def return_result(self) -> dict: return self.result - + def handle(self): if self.state == 'absent': - self.handle_absent() + self.handle_absent() elif self.state == 'present': self.handle_present() From f5d2faf4e99fa7a3709278b4ce7fb669a8e868ba Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 11:39:46 +0100 Subject: [PATCH 16/49] Fix comparison statement --- plugins/module_utils/elasticsearch_role.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/module_utils/elasticsearch_role.py b/plugins/module_utils/elasticsearch_role.py index bbf7d97f..31f9906b 100644 --- a/plugins/module_utils/elasticsearch_role.py +++ b/plugins/module_utils/elasticsearch_role.py @@ -40,7 +40,7 @@ def handle_absent(self): return res = self.delete() - if res['found'] == True: + if res['found'] is True: self.result['changed'] = True self.result['msg'] = self.role_name + " has been deleted" From 7d9672a8791f2bc41364e2fec20c092416ea9ca8 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 11:40:44 +0100 Subject: [PATCH 17/49] Fix comparison statement --- plugins/module_utils/elasticsearch_role.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/module_utils/elasticsearch_role.py b/plugins/module_utils/elasticsearch_role.py index 31f9906b..19e81586 100644 --- a/plugins/module_utils/elasticsearch_role.py +++ b/plugins/module_utils/elasticsearch_role.py @@ -60,7 +60,7 @@ def handle_present(self): self.result['msg'] = self.role_name + " has been created" return - if pre_role == None: + if pre_role is None: return if pre_role.raw != self.get().raw: From c007b87a59806b1af026767af92b7575242e30fd Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 12:35:57 +0100 Subject: [PATCH 18/49] Fix sapaces --- plugins/module_utils/api.py | 1 + plugins/module_utils/elasticsearch_role.py | 1 + plugins/module_utils/elasticsearch_user.py | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/module_utils/api.py b/plugins/module_utils/api.py index 6dada178..d5f9dbcf 100644 --- a/plugins/module_utils/api.py +++ b/plugins/module_utils/api.py @@ -7,6 +7,7 @@ from elasticsearch import Elasticsearch import ssl + class Api(): def new_client_basic_auth(host, auth_user, auth_pass, ca_certs, verify_certs) -> Elasticsearch: ctx = ssl.create_default_context(cafile=ca_certs) diff --git a/plugins/module_utils/elasticsearch_role.py b/plugins/module_utils/elasticsearch_role.py index 19e81586..c40d1784 100644 --- a/plugins/module_utils/elasticsearch_role.py +++ b/plugins/module_utils/elasticsearch_role.py @@ -8,6 +8,7 @@ Api ) + class Role(): def __init__(self, result, role_name, cluster, indicies, state, host, auth_user, auth_pass, verify_certs, ca_certs): self.role_name = role_name diff --git a/plugins/module_utils/elasticsearch_user.py b/plugins/module_utils/elasticsearch_user.py index c84da609..bb60e881 100644 --- a/plugins/module_utils/elasticsearch_user.py +++ b/plugins/module_utils/elasticsearch_user.py @@ -8,6 +8,7 @@ Api ) + class User(): def __init__(self, result, user_name, full_name, password, email, roles, enabled, state, host, auth_user, auth_pass, verify_certs, ca_certs): self.user_name = user_name @@ -85,4 +86,4 @@ def put(self): def delete(self): - return self.client.security.delete_user(username=self.user_name) \ No newline at end of file + return self.client.security.delete_user(username=self.user_name) From fbc830c773c545e14bf57161aa8c532a9d07b698 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 14:39:49 +0100 Subject: [PATCH 19/49] Fix syntax errors in plugins --- .config/pep8.cfg | 2 +- plugins/module_utils/api.py | 1 + plugins/module_utils/certs.py | 26 +++++++++---------- plugins/module_utils/elasticsearch_role.py | 20 ++++---------- plugins/module_utils/elasticsearch_user.py | 15 +++-------- plugins/modules/elasticsearch_role.py | 10 +++---- plugins/modules/elasticsearch_user.py | 8 +++--- tests/unit/plugins/module_utils/test_certs.py | 12 ++++----- tests/unit/plugins/modules/test_cert_info.py | 5 ++-- 9 files changed, 41 insertions(+), 58 deletions(-) diff --git a/.config/pep8.cfg b/.config/pep8.cfg index 8a0496d8..7717b0f6 100644 --- a/.config/pep8.cfg +++ b/.config/pep8.cfg @@ -1,4 +1,4 @@ [pep8] ignore = E402, E123 # It's fine to have line-length of 99 -max-line-length = 150 +max-line-length = 200 diff --git a/plugins/module_utils/api.py b/plugins/module_utils/api.py index d5f9dbcf..bc571031 100644 --- a/plugins/module_utils/api.py +++ b/plugins/module_utils/api.py @@ -9,6 +9,7 @@ class Api(): + def new_client_basic_auth(host, auth_user, auth_pass, ca_certs, verify_certs) -> Elasticsearch: ctx = ssl.create_default_context(cafile=ca_certs) ctx.check_hostname = False diff --git a/plugins/module_utils/certs.py b/plugins/module_utils/certs.py index 5c5eecf0..74b3c318 100644 --- a/plugins/module_utils/certs.py +++ b/plugins/module_utils/certs.py @@ -29,16 +29,16 @@ 'basicConstraints': [ '_ca', '_path_length' - ], + ], 'subjectKeyIdentifier': [ '_digest' - ], + ], 'authorityKeyIdentifier': [ '_authority_cert_issuer', '_authority_cert_serial_number', '_key_identifier' - ] - } + ] +} # function returns and converts bytes to a hex string separated with ":" @@ -50,7 +50,7 @@ def bytes_to_hex(bytes_str): # seperate by ":" every two characters and upper() value = ':'.join( ascii_hex_str[i:i + 2] for i in range(0, len(ascii_hex_str), 2) - ).upper() + ).upper() return value @@ -91,7 +91,7 @@ def load_certificate(self): if not HAS_CRYPTOGRAPHY_PKCS12: self.module.fail_json( msg=missing_required_lib('cryptography >= 2.5') - ) + ) # read the pkcs12 file try: with open(self.__path, 'rb') as f: @@ -99,19 +99,19 @@ def load_certificate(self): except IOError as e: self.module.fail_json( msg='IOError: %s' % (to_native(e)) - ) + ) # try to load with 2 parameters # for cryptography >= 3.1.x try: __pkcs12_tuple = pkcs12.load_key_and_certificates( pkcs12_data, to_bytes(self.__passphrase), - ) + ) loaded = True except Exception: self.module.log( msg="Couldn't load certificate without backend. Trying with backend." - ) + ) # try to load with 3 parameters for # cryptography >= 2.5.x and <= 3.0.x if not loaded: @@ -122,10 +122,10 @@ def load_certificate(self): pkcs12_data, to_bytes(self.__passphrase), backend - ) + ) self.module.log( msg="Loaded certificate with backend." - ) + ) # map loaded certificate to object self.__private_key = __pkcs12_tuple[0] self.__cert = __pkcs12_tuple[1] @@ -139,10 +139,10 @@ def general_info(self): # map object values to result dict issuer = to_text(self.__cert.issuer.get_attributes_for_oid( NameOID.COMMON_NAME)[0].value - ) + ) subject = to_text(self.__cert.subject.get_attributes_for_oid( NameOID.COMMON_NAME)[0].value - ) + ) self.result['issuer'] = to_text(issuer) self.result['subject'] = to_text(subject) self.result['not_valid_after'] = to_text(self.__cert.not_valid_after) diff --git a/plugins/module_utils/elasticsearch_role.py b/plugins/module_utils/elasticsearch_role.py index c40d1784..efc74e6e 100644 --- a/plugins/module_utils/elasticsearch_role.py +++ b/plugins/module_utils/elasticsearch_role.py @@ -10,43 +10,37 @@ class Role(): - def __init__(self, result, role_name, cluster, indicies, state, host, auth_user, auth_pass, verify_certs, ca_certs): + def __init__(self, result, role_name, cluster, indicies, state, host, auth_user, auth_pass, verify_certs, ca_certs): self.role_name = role_name self.cluster = cluster self.indicies = indicies self.state = state self.result = result - self.client = Api.new_client_basic_auth(host=host, auth_user=auth_user, auth_pass=auth_pass, verify_certs=verify_certs, ca_certs=ca_certs) - self.handle() - def return_result(self) -> dict: return self.result - def handle(self): if self.state == 'absent': self.handle_absent() elif self.state == 'present': self.handle_present() - - return + return def handle_absent(self): if self.role_name not in self.get_all().raw: return - + res = self.delete() if res['found'] is True: self.result['changed'] = True self.result['msg'] = self.role_name + " has been deleted" - - return + return def handle_present(self): if self.role_name in self.get_all().raw: @@ -56,7 +50,7 @@ def handle_present(self): res = self.put() - if res.raw['role']['created'] == True: + if res.raw['role']['created'] is True: self.result['changed'] = True self.result['msg'] = self.role_name + " has been created" return @@ -69,19 +63,15 @@ def handle_present(self): self.result['msg'] = self.role_name + " has been updated" return - def get_all(self): return self.client.security.get_role() - def get(self): return self.client.security.get_role(name=self.role_name) - def put(self): return self.client.security.put_role(name=self.role_name, cluster=self.cluster, indices=self.indicies) - def delete(self): return self.client.security.delete_role(name=self.role_name) diff --git a/plugins/module_utils/elasticsearch_user.py b/plugins/module_utils/elasticsearch_user.py index bb60e881..d066b0a2 100644 --- a/plugins/module_utils/elasticsearch_user.py +++ b/plugins/module_utils/elasticsearch_user.py @@ -24,32 +24,27 @@ def __init__(self, result, user_name, full_name, password, email, roles, enabled self.handle() - def return_result(self) -> dict: return self.result - def handle(self): if self.state == 'absent': self.handle_absent() elif self.state == 'present': self.handle_present() - return - def handle_absent(self): if self.user_name not in self.get_all().raw: return res = self.delete() - if res['found'] == True: + if res['found'] is True: self.result['changed'] = True self.result['msg'] = self.user_name + " has been deleted" return - def handle_present(self): if self.user_name in self.get_all().raw: pre_user = self.get() @@ -58,12 +53,12 @@ def handle_present(self): res = self.put() - if res.raw['created'] == True: + if res.raw['created'] is True: self.result['changed'] = True self.result['msg'] = self.user_name + " has been created" return - if pre_user == None: + if pre_user is None: return if pre_user.raw != self.get().raw: @@ -72,18 +67,14 @@ def handle_present(self): return - def get_all(self): return self.client.security.get_user() - def get(self): return self.client.security.get_user(username=self.user_name) - def put(self): return self.client.security.put_user(username=self.user_name, password=self.password, email=self.email, full_name=self.full_name, enabled=self.enabled, roles=self.roles) - def delete(self): return self.client.security.delete_user(username=self.user_name) diff --git a/plugins/modules/elasticsearch_role.py b/plugins/modules/elasticsearch_role.py index 050a907a..4ceedaf4 100644 --- a/plugins/modules/elasticsearch_role.py +++ b/plugins/modules/elasticsearch_role.py @@ -12,6 +12,7 @@ Role ) + def run_module(): ''' Elasticsearch user management. @@ -68,13 +69,12 @@ def run_module(): if module.params['state'] != 'absent' and module.params['state'] != 'present': result['stderr'] = "Invalid state given. Please use 'absent' or 'present'" result['failed'] = True - - module.exit_json(**result) + module.exit_json(**result) role = Role( - result=result, - role_name=module.params['name'], + result=result, + role_name=module.params['name'], cluster=module.params['cluster'], indicies=module.params['indicies'], state=module.params['state'], @@ -91,4 +91,4 @@ def run_module(): if __name__ == "__main__": - run_module() \ No newline at end of file + run_module() diff --git a/plugins/modules/elasticsearch_user.py b/plugins/modules/elasticsearch_user.py index 3e05e91b..5d9c5172 100644 --- a/plugins/modules/elasticsearch_user.py +++ b/plugins/modules/elasticsearch_user.py @@ -45,7 +45,7 @@ def run_module(): roles=dict(type=list, required=True), enabled=dict(type=bool, required=False, default=True), state=dict(type=str, required=False, default="present"), - + # Auth args host=dict(type=str, required=True), auth_user=dict(type=str, required=True), @@ -63,9 +63,8 @@ def run_module(): if module.params['state'] != 'absent' and module.params['state'] != 'present': result['stderr'] = "Invalid state given. Please use 'absent' or 'present'" result['failed'] = True - - module.exit_json(**result) + module.exit_json(**result) user = User( result=result, @@ -87,5 +86,6 @@ def run_module(): module.exit_json(**result) + if __name__ == "__main__": - run_module() \ No newline at end of file + run_module() diff --git a/tests/unit/plugins/module_utils/test_certs.py b/tests/unit/plugins/module_utils/test_certs.py index 4a691e8c..0c09b318 100644 --- a/tests/unit/plugins/module_utils/test_certs.py +++ b/tests/unit/plugins/module_utils/test_certs.py @@ -15,29 +15,29 @@ class TestCerts(unittest.TestCase): def test_bytes_to_hex_byte_string(self): bytes_string = unhexlify('82532011c773a75e2a77c1df22e423b4c450bacf') # or - #bytes_string = b'\x82S \x11\xc7s\xa7^*w\xc1\xdf"\xe4#\xb4\xc4P\xba\xcf' + # bytes_string = b'\x82S \x11\xc7s\xa7^*w\xc1\xdf"\xe4#\xb4\xc4P\xba\xcf' result = bytes_to_hex(bytes_str=bytes_string) - #print("Bytes converted: " + str(result)) + # print("Bytes converted: " + str(result)) self.assertEqual(result, '82:53:20:11:C7:73:A7:5E:2A:77:C1:DF:22:E4:23:B4:C4:50:BA:CF') def test_check_supported_extensions_with_supported_extension(self): result = check_supported_extensions(extension_name='authorityKeyIdentifier') - #print("Extension is supported: " + str(result)) + # print("Extension is supported: " + str(result)) self.assertEqual(result, True) def test_check_supported_extensions_with_unknown_extension(self): result = check_supported_extensions(extension_name='UnknownExtension') - #print("Extension is supported: " + str(result)) + # print("Extension is supported: " + str(result)) self.assertEqual(result, False) def test_check_supported_keys_with_known_key(self): result = check_supported_keys(key='_key_identifier', extension_name='authorityKeyIdentifier') - #print("Key is supported: " + str(result)) + # print("Key is supported: " + str(result)) self.assertEqual(result, True) def test_check_supported_keys_with_unknown_key(self): result = check_supported_keys(key='_unknown', extension_name='authorityKeyIdentifier') - #print("Key is supported: " + str(result)) + # print("Key is supported: " + str(result)) self.assertEqual(result, False) diff --git a/tests/unit/plugins/modules/test_cert_info.py b/tests/unit/plugins/modules/test_cert_info.py index 4151576e..1af8457d 100644 --- a/tests/unit/plugins/modules/test_cert_info.py +++ b/tests/unit/plugins/modules/test_cert_info.py @@ -42,7 +42,7 @@ "serial_number": "719770426243590812378787092632593850366518596520", "subject": "Elastic Certificate Tool Autogenerated CA", "version": "Version.v3" - } +} def set_module_args(args): @@ -55,6 +55,7 @@ class AnsibleExitJson(Exception): """Exception class to be raised by module.exit_json and caught by the test case""" pass + class AnsibleFailJson(Exception): """Exception class to be raised by module.fail_json and caught by the test case""" pass @@ -75,7 +76,7 @@ def exit_json(*args, **kwargs): for item in certificate: if certificate[item] != kwargs[item]: checks_passed = False - + if checks_passed: raise AnsibleExitJson(kwargs) From f8e04aa7f7f9bd442a89d2375ac0c5b218accf5e Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 17:09:48 +0100 Subject: [PATCH 20/49] Update runner version to latest --- .github/workflows/test_plugins.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index f9ebd79e..92649aaa 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -46,7 +46,7 @@ jobs: unit-test: needs: pep8 - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest env: COLLECTION_NAMESPACE: netways @@ -90,7 +90,7 @@ jobs: python: needs: unit-test - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest env: COLLECTION_NAMESPACE: netways @@ -129,7 +129,7 @@ jobs: ansible-core: needs: python - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest env: COLLECTION_NAMESPACE: netways @@ -168,7 +168,7 @@ jobs: python-cryptography: needs: ansible-core - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest env: COLLECTION_NAMESPACE: netways From 4e944e1fcb4fc76ec98e6c8557abc593f77815d7 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Thu, 19 Mar 2026 17:39:25 +0100 Subject: [PATCH 21/49] Add sanity test --- .github/workflows/test_plugins.yml | 37 ++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 92649aaa..21ac2dcd 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -24,25 +24,38 @@ on: - 'tests/**' jobs: - pep8: + sanity: runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + python_version: + - "3.11" + - "3.12" + - "3.13" + ansible_version: + - "ansible-core>=2.18,<2.21" #Correspond ansible>=11.0,<12.0 + - "ansible-core>=2.19,<2.20" #Correspond ansible>=12.0,<13.0 + - "ansible-core>=2.20,<2.21" #Correspond ansible>=13.0,<14.0 + steps: - - name: Check out the codebase. + - name: Check out code uses: actions/checkout@v6 - - name: Set up Python 3. + - name: Set up Python uses: actions/setup-python@v6 with: - python-version: '3.9' + python-version: ${{ matrix.python_version }} - - name: Install test dependencies. + - name: Install Ansible run: | python3 -m pip install --upgrade pip - python3 -m pip install pep8 + python3 -m pip install "${{ matrix.ansible_version }}" - - name: Lint code. + - name: Run sanity tests run: | - pep8 plugins/ --config=.config/pep8.cfg --statistics --count + ansible-test sanity --python "${{ matrix.python_version }}" -v unit-test: needs: pep8 @@ -66,8 +79,8 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip - python -m pip install install ansible + python3 -m pip install --upgrade pip + #python3 -m pip install install ansible - name: Install collection run: | @@ -76,14 +89,14 @@ jobs: - name: Test `cert_info` module run: | - python tests/unit/plugins/modules/test_cert_info.py + python3 tests/unit/plugins/modules/test_cert_info.py env: PY_COLORS: '1' ANSIBLE_FORCE_COLOR: '1' - name: Test `certs` module util run: | - python tests/unit/plugins/module_utils/test_certs.py + python3 tests/unit/plugins/module_utils/test_certs.py env: PY_COLORS: '1' ANSIBLE_FORCE_COLOR: '1' From a0969732d08c590e334bb865c123bf7446ad0a64 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Fri, 20 Mar 2026 13:46:13 +0100 Subject: [PATCH 22/49] Remove spaces --- .github/workflows/test_plugins.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 21ac2dcd..95da88bb 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -15,7 +15,6 @@ on: pull_request: branches: - 'main' - paths: - 'plugins/**' - '.github/workflows/test_plugins.yml' @@ -26,7 +25,6 @@ on: jobs: sanity: runs-on: ubuntu-latest - strategy: fail-fast: false matrix: @@ -38,7 +36,6 @@ jobs: - "ansible-core>=2.18,<2.21" #Correspond ansible>=11.0,<12.0 - "ansible-core>=2.19,<2.20" #Correspond ansible>=12.0,<13.0 - "ansible-core>=2.20,<2.21" #Correspond ansible>=13.0,<14.0 - steps: - name: Check out code uses: actions/checkout@v6 From 13c4ad40f3187b42a389a6953d5fc672fe73ab4f Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Fri, 20 Mar 2026 14:04:50 +0100 Subject: [PATCH 23/49] Change needs --- .github/workflows/test_plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 95da88bb..bc920e71 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -23,7 +23,7 @@ on: - 'tests/**' jobs: - sanity: + ansible_sanity: runs-on: ubuntu-latest strategy: fail-fast: false @@ -55,7 +55,7 @@ jobs: ansible-test sanity --python "${{ matrix.python_version }}" -v unit-test: - needs: pep8 + needs: ansible_sanity runs-on: ubuntu-latest env: From 160aca8b219396fa3f7a435a8af62c3db7278828 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Fri, 20 Mar 2026 14:19:51 +0100 Subject: [PATCH 24/49] Change directory for sanity test --- .github/workflows/test_plugins.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index bc920e71..370995ca 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -25,6 +25,10 @@ on: jobs: ansible_sanity: runs-on: ubuntu-latest + env: + COLLECTION_NAMESPACE: netways + COLLECTION_NAME: elasticstack + COLLECTION_PATH: ansible_collections/netways/elasticstack strategy: fail-fast: false matrix: @@ -51,6 +55,7 @@ jobs: python3 -m pip install "${{ matrix.ansible_version }}" - name: Run sanity tests + working-directory: ${{ env.COLLECTION_PATH }} run: | ansible-test sanity --python "${{ matrix.python_version }}" -v From 806aeacdaae534accb82c3fbdac2b9332ba74fac Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Fri, 20 Mar 2026 16:36:38 +0100 Subject: [PATCH 25/49] Add path --- .github/workflows/test_plugins.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 370995ca..a3bdb7b0 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -28,7 +28,6 @@ jobs: env: COLLECTION_NAMESPACE: netways COLLECTION_NAME: elasticstack - COLLECTION_PATH: ansible_collections/netways/elasticstack strategy: fail-fast: false matrix: @@ -54,9 +53,14 @@ jobs: python3 -m pip install --upgrade pip python3 -m pip install "${{ matrix.ansible_version }}" + - name: Install collection + run: | + mkdir -p ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE + cp -a ../ansible-collection-$COLLECTION_NAME ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE/$COLLECTION_NAME + - name: Run sanity tests - working-directory: ${{ env.COLLECTION_PATH }} run: | + cd ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE/$COLLECTION_NAME ansible-test sanity --python "${{ matrix.python_version }}" -v unit-test: From 871c96f7c5e3949723072ce96c175ed6dc4e58d8 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 15:33:29 +0100 Subject: [PATCH 26/49] Add better import error for modules --- plugins/module_utils/api.py | 9 ++++++++- plugins/modules/elasticsearch_role.py | 11 ++++++++++- plugins/modules/elasticsearch_user.py | 11 ++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/plugins/module_utils/api.py b/plugins/module_utils/api.py index bc571031..f228d9d7 100644 --- a/plugins/module_utils/api.py +++ b/plugins/module_utils/api.py @@ -4,9 +4,16 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or # https://www.gnu.org/licenses/gpl-3.0.txt) -from elasticsearch import Elasticsearch import ssl +try: + from elasticsearch import Elasticsearch + HAS_ELASTICSEARCH = True + ELASTICSEARCH_IMPORT_ERROR = None +except ImportError as import_error: + HAS_ELASTICSEARCH = False + ELASTICSEARCH_IMPORT_ERROR = import_error + class Api(): diff --git a/plugins/modules/elasticsearch_role.py b/plugins/modules/elasticsearch_role.py index 4ceedaf4..2ee09a9d 100644 --- a/plugins/modules/elasticsearch_role.py +++ b/plugins/modules/elasticsearch_role.py @@ -7,7 +7,10 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.basic import AnsibleModule, missing_required_lib +from ansible_collections.netways.elasticstack.plugins.module_utils.api import ( + HAS_ELASTICSEARCH, ELASTICSEARCH_IMPORT_ERROR +) from ansible_collections.netways.elasticstack.plugins.module_utils.elasticsearch_role import ( Role ) @@ -66,6 +69,12 @@ def run_module(): changed=False ) + if not HAS_ELASTICSEARCH: + module.fail_json( + msg=missing_required_lib('elasticsearch'), + exception=ELASTICSEARCH_IMPORT_ERROR + ) + if module.params['state'] != 'absent' and module.params['state'] != 'present': result['stderr'] = "Invalid state given. Please use 'absent' or 'present'" result['failed'] = True diff --git a/plugins/modules/elasticsearch_user.py b/plugins/modules/elasticsearch_user.py index 5d9c5172..31d6fca1 100644 --- a/plugins/modules/elasticsearch_user.py +++ b/plugins/modules/elasticsearch_user.py @@ -7,7 +7,10 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.basic import AnsibleModule, missing_required_lib +from ansible_collections.netways.elasticstack.plugins.module_utils.api import ( + HAS_ELASTICSEARCH, ELASTICSEARCH_IMPORT_ERROR +) from ansible_collections.netways.elasticstack.plugins.module_utils.elasticsearch_user import ( User ) @@ -60,6 +63,12 @@ def run_module(): changed=False ) + if not HAS_ELASTICSEARCH: + module.fail_json( + msg=missing_required_lib('elasticsearch'), + exception=ELASTICSEARCH_IMPORT_ERROR + ) + if module.params['state'] != 'absent' and module.params['state'] != 'present': result['stderr'] = "Invalid state given. Please use 'absent' or 'present'" result['failed'] = True From fc880e56d41847923a2256169806f510a33c5445 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 16:06:22 +0100 Subject: [PATCH 27/49] Use traceback module --- plugins/module_utils/api.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/plugins/module_utils/api.py b/plugins/module_utils/api.py index f228d9d7..847e0c52 100644 --- a/plugins/module_utils/api.py +++ b/plugins/module_utils/api.py @@ -4,21 +4,35 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or # https://www.gnu.org/licenses/gpl-3.0.txt) +import traceback import ssl +from ansible.module_utils.basic import missing_required_lib + try: from elasticsearch import Elasticsearch +except ImportError: + HAS_ELASTICSEARCH = False + ELASTICSEARCH_IMPORT_ERROR = traceback.format_exc() +else: HAS_ELASTICSEARCH = True ELASTICSEARCH_IMPORT_ERROR = None -except ImportError as import_error: - HAS_ELASTICSEARCH = False - ELASTICSEARCH_IMPORT_ERROR = import_error class Api(): - def new_client_basic_auth(host, auth_user, auth_pass, ca_certs, verify_certs) -> Elasticsearch: + @staticmethod + def new_client_basic_auth(host, auth_user, auth_pass, ca_certs, verify_certs): + if not HAS_ELASTICSEARCH: + raise ImportError(missing_required_lib('elasticsearch')) + ctx = ssl.create_default_context(cafile=ca_certs) ctx.check_hostname = False ctx.verify_mode = False - return Elasticsearch(hosts=[host], basic_auth=(auth_user, auth_pass), ssl_context=ctx, verify_certs=verify_certs) + + return Elasticsearch( + hosts=[host], + basic_auth=(auth_user, auth_pass), + ssl_context=ctx, + verify_certs=verify_certs + ) From 23d6682e17d67ea0bdb3fe3ec0976bcd0965630c Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 16:12:04 +0100 Subject: [PATCH 28/49] Do not use shebang in module unites --- plugins/module_utils/elasticsearch_role.py | 2 -- plugins/module_utils/elasticsearch_user.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/plugins/module_utils/elasticsearch_role.py b/plugins/module_utils/elasticsearch_role.py index efc74e6e..772eaa71 100644 --- a/plugins/module_utils/elasticsearch_role.py +++ b/plugins/module_utils/elasticsearch_role.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2024, Tobias Bauriedel # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or # https://www.gnu.org/licenses/gpl-3.0.txt) diff --git a/plugins/module_utils/elasticsearch_user.py b/plugins/module_utils/elasticsearch_user.py index d066b0a2..5a5a452b 100644 --- a/plugins/module_utils/elasticsearch_user.py +++ b/plugins/module_utils/elasticsearch_user.py @@ -1,5 +1,3 @@ -#!/usr/bin/python - # Copyright (c) 2024, Tobias Bauriedel # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or # https://www.gnu.org/licenses/gpl-3.0.txt) From b9b8628ac7d1f48ef37399b0c4d901a3eefec30b Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 16:13:51 +0100 Subject: [PATCH 29/49] Fix E501 line too long --- plugins/module_utils/elasticsearch_user.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/module_utils/elasticsearch_user.py b/plugins/module_utils/elasticsearch_user.py index 5a5a452b..455724b5 100644 --- a/plugins/module_utils/elasticsearch_user.py +++ b/plugins/module_utils/elasticsearch_user.py @@ -72,7 +72,14 @@ def get(self): return self.client.security.get_user(username=self.user_name) def put(self): - return self.client.security.put_user(username=self.user_name, password=self.password, email=self.email, full_name=self.full_name, enabled=self.enabled, roles=self.roles) + return self.client.security.put_user( + username=self.user_name, + password=self.password, + email=self.email, + full_name=self.full_name, + enabled=self.enabled, + roles=self.roles + ) def delete(self): return self.client.security.delete_user(username=self.user_name) From 76f628963a3064a7cf978141afb3ee72ed8a21b2 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 16:16:41 +0100 Subject: [PATCH 30/49] Use ASCII quotes instead of Unicode quotes --- docs/role-elasticsearch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/role-elasticsearch.md b/docs/role-elasticsearch.md index 3676a644..1be8ce3b 100644 --- a/docs/role-elasticsearch.md +++ b/docs/role-elasticsearch.md @@ -51,7 +51,7 @@ This variable activates a workaround to start on systems that have certain harde * *elasticsearch_ssl_verification_mode*: Defines how to verify the certificates presented by another party in the TLS connection * *elasticsearch_transport_port*: The port to bind for communication between nodes * *elasticsearch_seed_hosts*: Set elasticsearch seed hosts -* *elasticsearch_security_enrollment*: Controls enrollment (of nodes and Kibana) to a local node that’s been autoconfigured for security. +* *elasticsearch_security_enrollment*: Controls enrollment (of nodes and Kibana) to a local node that's been autoconfigured for security. The following variable was only integrated to speed up upgrades of non-production clusters. Use with caution and at your own risk: From eea4bfe20894d5b9a0af2617376acd3c0851e26e Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 16:33:01 +0100 Subject: [PATCH 31/49] * private_key and additional_certs are extract but not used any were in the code. The certificate is the only used tuple. So we ignore the not used ones. --- plugins/module_utils/certs.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/module_utils/certs.py b/plugins/module_utils/certs.py index 74b3c318..b38ddb0b 100644 --- a/plugins/module_utils/certs.py +++ b/plugins/module_utils/certs.py @@ -79,8 +79,6 @@ def __init__(self, module, result): self.__passphrase = self.module.params['passphrase'] self.__path = self.module.params['path'] self.__cert = None - self.__private_key = None - self.__additional_certs = None self.load_certificate() self.load_info() @@ -127,9 +125,7 @@ def load_certificate(self): msg="Loaded certificate with backend." ) # map loaded certificate to object - self.__private_key = __pkcs12_tuple[0] - self.__cert = __pkcs12_tuple[1] - self.__additional_certs = __pkcs12_tuple[2] + _, self.__cert, _ = __pkcs12_tuple def load_info(self): self.general_info() From ba7e4d752426bd985d1b637b0509b2ddc17d6c31 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 16:55:12 +0100 Subject: [PATCH 32/49] Add documentation for every module --- plugins/modules/cert_info.py | 77 ++++++++++++++++++ plugins/modules/elasticsearch_role.py | 96 +++++++++++++++++++++++ plugins/modules/elasticsearch_user.py | 109 ++++++++++++++++++++++++++ 3 files changed, 282 insertions(+) diff --git a/plugins/modules/cert_info.py b/plugins/modules/cert_info.py index 3dc5163b..41cb73a4 100644 --- a/plugins/modules/cert_info.py +++ b/plugins/modules/cert_info.py @@ -9,6 +9,83 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +DOCUMENTATION = r''' +--- +module: cert_info +short_description: Retrieve information from a PKCS12 certificate +description: + - Reads a PKCS12 certificate file and returns details such as issuer, subject, + validity dates, serial number, and supported X.509 extensions. + - Requires the C(cryptography) Python library (>= 2.5) on the target host. +version_added: "1.0.0" +author: + - Daniel Patrick (@dpatrick) +requirements: + - cryptography >= 2.5 +options: + path: + description: Absolute path to the PKCS12 certificate file on the target host. + type: str + required: true + passphrase: + description: Passphrase to decrypt the PKCS12 file. Omit if the file is not encrypted. + type: str + required: false + default: null +''' + +EXAMPLES = r''' +- name: Get certificate information + netways.elasticstack.cert_info: + path: /etc/elasticsearch/certs/elastic-certificates.p12 + register: cert + +- name: Get certificate info with passphrase + netways.elasticstack.cert_info: + path: /etc/elasticsearch/certs/elastic-certificates.p12 + passphrase: "{{ cert_passphrase }}" + register: cert + +- name: Show certificate expiry date + ansible.builtin.debug: + msg: "Certificate expires: {{ cert.not_valid_after }}" +''' + +RETURN = r''' +changed: + description: Always false, this module does not modify anything. + type: bool + returned: always +issuer: + description: Common name of the certificate issuer. + type: str + returned: success +subject: + description: Common name of the certificate subject. + type: str + returned: success +not_valid_before: + description: Start of the certificate validity period. + type: str + returned: success +not_valid_after: + description: End of the certificate validity period. + type: str + returned: success +serial_number: + description: Serial number of the certificate. + type: str + returned: success +version: + description: X.509 version of the certificate. + type: str + returned: success +extensions: + description: Dictionary of supported X.509 extensions and their values. + type: dict + returned: success +''' + from ansible.module_utils.basic import ( AnsibleModule, to_native diff --git a/plugins/modules/elasticsearch_role.py b/plugins/modules/elasticsearch_role.py index 2ee09a9d..afc17fab 100644 --- a/plugins/modules/elasticsearch_role.py +++ b/plugins/modules/elasticsearch_role.py @@ -7,6 +7,102 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +DOCUMENTATION = r''' +--- +module: elasticsearch_role +short_description: Manage Elasticsearch roles +description: + - Creates, updates, or deletes Elasticsearch roles using the Security API. + - Requires the C(elasticsearch) Python library on the target host. +version_added: "1.0.0" +author: + - Tobias Bauriedel (@tbauriedel) +requirements: + - elasticsearch < 9 +options: + name: + description: Name of the Elasticsearch role. + type: str + required: true + cluster: + description: List of cluster-level privileges assigned to the role. + type: list + elements: str + required: false + indicies: + description: List of index permission objects defining index patterns and privileges. + type: list + elements: dict + required: false + state: + description: Whether the role should exist or not. + type: str + required: false + default: present + choices: [present, absent] + host: + description: URL of the Elasticsearch host, including protocol and port. + type: str + required: true + auth_user: + description: Username for authentication. + type: str + required: true + auth_pass: + description: Password for authentication. + type: str + required: true + no_log: true + ca_certs: + description: Path to the CA certificate file for TLS verification. + type: str + required: false + verify_certs: + description: Whether to verify TLS certificates. + type: bool + required: false + default: true +''' + +EXAMPLES = r''' +- name: Create an Elasticsearch role + netways.elasticstack.elasticsearch_role: + name: my-role + cluster: + - manage_own_api_key + indicies: + - names: + - my-index-* + privileges: + - read + - write + state: present + host: https://localhost:9200 + auth_user: elastic + auth_pass: "{{ elastic_password }}" + ca_certs: /etc/elasticsearch/certs/http_ca.crt + +- name: Delete an Elasticsearch role + netways.elasticstack.elasticsearch_role: + name: my-role + state: absent + host: https://localhost:9200 + auth_user: elastic + auth_pass: "{{ elastic_password }}" + verify_certs: false +''' + +RETURN = r''' +changed: + description: Whether the role was created, updated, or deleted. + type: bool + returned: always +msg: + description: Human-readable status message. + type: str + returned: on change +''' + from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible_collections.netways.elasticstack.plugins.module_utils.api import ( HAS_ELASTICSEARCH, ELASTICSEARCH_IMPORT_ERROR diff --git a/plugins/modules/elasticsearch_user.py b/plugins/modules/elasticsearch_user.py index 31d6fca1..c3622e56 100644 --- a/plugins/modules/elasticsearch_user.py +++ b/plugins/modules/elasticsearch_user.py @@ -7,6 +7,115 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +DOCUMENTATION = r''' +--- +module: elasticsearch_user +short_description: Manage Elasticsearch users +description: + - Creates, updates, or deletes Elasticsearch native users using the Security API. + - Requires the C(elasticsearch) Python library on the target host. +version_added: "1.0.0" +author: + - Tobias Bauriedel (@tbauriedel) +requirements: + - elasticsearch < 9 +options: + name: + description: Username of the Elasticsearch user. + type: str + required: true + fullname: + description: Full display name of the user. + type: str + required: false + password: + description: Password for the user. + type: str + required: true + no_log: true + email: + description: Email address of the user. + type: str + required: false + roles: + description: List of roles assigned to the user. + type: list + elements: str + required: true + enabled: + description: Whether the user account is enabled. + type: bool + required: false + default: true + state: + description: Whether the user should exist or not. + type: str + required: false + default: present + choices: [present, absent] + host: + description: URL of the Elasticsearch host, including protocol and port. + type: str + required: true + auth_user: + description: Username for authentication. + type: str + required: true + auth_pass: + description: Password for authentication. + type: str + required: true + no_log: true + ca_certs: + description: Path to the CA certificate file for TLS verification. + type: str + required: false + verify_certs: + description: Whether to verify TLS certificates. + type: bool + required: false + default: true +''' + +EXAMPLES = r''' +- name: Create an Elasticsearch user + netways.elasticstack.elasticsearch_user: + name: john + fullname: John Doe + password: "{{ user_password }}" + email: john@example.com + roles: + - my-role + enabled: true + state: present + host: https://localhost:9200 + auth_user: elastic + auth_pass: "{{ elastic_password }}" + ca_certs: /etc/elasticsearch/certs/http_ca.crt + +- name: Delete an Elasticsearch user + netways.elasticstack.elasticsearch_user: + name: john + password: "{{ user_password }}" + roles: [] + state: absent + host: https://localhost:9200 + auth_user: elastic + auth_pass: "{{ elastic_password }}" + verify_certs: false +''' + +RETURN = r''' +changed: + description: Whether the user was created, updated, or deleted. + type: bool + returned: always +msg: + description: Human-readable status message. + type: str + returned: on change +''' + from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible_collections.netways.elasticstack.plugins.module_utils.api import ( HAS_ELASTICSEARCH, ELASTICSEARCH_IMPORT_ERROR From 69534f28c0fb1ad9a9bb0709b88049d0dbb6e12e Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 16:59:10 +0100 Subject: [PATCH 33/49] * _ ist not allowd in Ansible s wa varible name Anyway we do not use or validate private key and additional certs so we do not need them --- plugins/module_utils/certs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/module_utils/certs.py b/plugins/module_utils/certs.py index b38ddb0b..7efbbee3 100644 --- a/plugins/module_utils/certs.py +++ b/plugins/module_utils/certs.py @@ -125,7 +125,7 @@ def load_certificate(self): msg="Loaded certificate with backend." ) # map loaded certificate to object - _, self.__cert, _ = __pkcs12_tuple + self.__cert = __pkcs12_tuple[1] def load_info(self): self.general_info() From d076d3d12647ac14e790916a3420013bf148d0c4 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 17:32:28 +0100 Subject: [PATCH 34/49] * Remove the wronge documentation style * enable colored output for sanity test --- .github/workflows/test_plugins.yml | 3 ++ plugins/modules/elasticsearch_role.py | 49 +++++-------------------- plugins/modules/elasticsearch_user.py | 52 +++++++-------------------- 3 files changed, 24 insertions(+), 80 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index a3bdb7b0..9aef0a76 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -62,6 +62,9 @@ jobs: run: | cd ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE/$COLLECTION_NAME ansible-test sanity --python "${{ matrix.python_version }}" -v + env: + PY_COLORS: '1' + ANSIBLE_FORCE_COLOR: '1' unit-test: needs: ansible_sanity diff --git a/plugins/modules/elasticsearch_role.py b/plugins/modules/elasticsearch_role.py index afc17fab..05db0f33 100644 --- a/plugins/modules/elasticsearch_role.py +++ b/plugins/modules/elasticsearch_role.py @@ -52,7 +52,6 @@ description: Password for authentication. type: str required: true - no_log: true ca_certs: description: Path to the CA certificate file for TLS verification. type: str @@ -113,30 +112,6 @@ def run_module(): - ''' - Elasticsearch user management. - - ``` - netways.elasticstack.elasticsearch_role: - name: new-role - cluster: - - manage_own_api_key - - delegate_pki - indicies: - - names: - - foobar - privileges: - - read - - write - state: present - host: https://localhost:9200 - auth_user: elastic - auth_pass: changeMe123! - verify_certs: false - ca_certs: /etc/elasticsearch/certs/http_ca.crt - ``` - ''' - # get role # https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-role.html @@ -146,17 +121,17 @@ def run_module(): module = AnsibleModule( argument_spec=dict( # User args - name=dict(type=str, required=True), - cluster=dict(type=list, required=False), - indicies=dict(type=list, required=False), - state=dict(type=str, required=False, default='present'), + name=dict(type='str', required=True), + cluster=dict(type='list', elements='str', required=False), + indicies=dict(type='list', elements='dict', required=False), + state=dict(type='str', required=False, default='present', choices=['present', 'absent']), # Auth args - host=dict(type=str, required=True), - auth_user=dict(type=str, required=True), - auth_pass=dict(type=str, required=True, no_log=True), - ca_certs=dict(type=str, required=False), - verify_certs=dict(type=bool, required=False, default=True) + host=dict(type='str', required=True), + auth_user=dict(type='str', required=True), + auth_pass=dict(type='str', required=True, no_log=True), + ca_certs=dict(type='str', required=False), + verify_certs=dict(type='bool', required=False, default=True) ) ) @@ -171,12 +146,6 @@ def run_module(): exception=ELASTICSEARCH_IMPORT_ERROR ) - if module.params['state'] != 'absent' and module.params['state'] != 'present': - result['stderr'] = "Invalid state given. Please use 'absent' or 'present'" - result['failed'] = True - - module.exit_json(**result) - role = Role( result=result, role_name=module.params['name'], diff --git a/plugins/modules/elasticsearch_user.py b/plugins/modules/elasticsearch_user.py index c3622e56..f3a58fb4 100644 --- a/plugins/modules/elasticsearch_user.py +++ b/plugins/modules/elasticsearch_user.py @@ -32,7 +32,6 @@ description: Password for the user. type: str required: true - no_log: true email: description: Email address of the user. type: str @@ -65,7 +64,6 @@ description: Password for authentication. type: str required: true - no_log: true ca_certs: description: Path to the CA certificate file for TLS verification. type: str @@ -126,44 +124,24 @@ def run_module(): - ''' - Elasticsearch user management. - - ``` - netways.elasticstack.elasticsearch_user: - name: new-user1 - fullname: New User - password: changeMe123! - email: "new@user.de" - roles: - - new-role1 - enabled: true - state: absent - host: https://localhost:9200 - auth_user: elastic - auth_pass: "{{ elasticstack_password.stdout }}" - verify_certs: false - ca_certs: /etc/elasticsearch/certs/http_ca.crt - ``` - ''' module = AnsibleModule( argument_spec=dict( # User args - name=dict(type=str, required=True), - fullname=dict(type=str, required=False), - password=dict(type=str, required=True, no_log=True), - email=dict(type=str, required=False), - roles=dict(type=list, required=True), - enabled=dict(type=bool, required=False, default=True), - state=dict(type=str, required=False, default="present"), + name=dict(type='str', required=True), + fullname=dict(type='str', required=False), + password=dict(type='str', required=True, no_log=True), + email=dict(type='str', required=False), + roles=dict(type='list', elements='str', required=True), + enabled=dict(type='bool', required=False, default=True), + state=dict(type='str', required=False, default='present', choices=['present', 'absent']), # Auth args - host=dict(type=str, required=True), - auth_user=dict(type=str, required=True), - auth_pass=dict(type=str, required=True, no_log=True), - ca_certs=dict(type=str, required=False), - verify_certs=dict(type=bool, required=False, default=True) + host=dict(type='str', required=True), + auth_user=dict(type='str', required=True), + auth_pass=dict(type='str', required=True, no_log=True), + ca_certs=dict(type='str', required=False), + verify_certs=dict(type='bool', required=False, default=True) ) ) @@ -178,12 +156,6 @@ def run_module(): exception=ELASTICSEARCH_IMPORT_ERROR ) - if module.params['state'] != 'absent' and module.params['state'] != 'present': - result['stderr'] = "Invalid state given. Please use 'absent' or 'present'" - result['failed'] = True - - module.exit_json(**result) - user = User( result=result, user_name=module.params['name'], From 3248590aba4635a090b14d669efc01d98fe5271e Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 18:00:49 +0100 Subject: [PATCH 35/49] Update sanity test --- .github/workflows/test_plugins.yml | 48 ++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 9aef0a76..73162ef0 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -23,7 +23,7 @@ on: - 'tests/**' jobs: - ansible_sanity: + sanity_ansible_18_19: runs-on: ubuntu-latest env: COLLECTION_NAMESPACE: netways @@ -36,8 +36,48 @@ jobs: - "3.12" - "3.13" ansible_version: - - "ansible-core>=2.18,<2.21" #Correspond ansible>=11.0,<12.0 + - "ansible-core>=2.18,<2.19" #Correspond ansible>=11.0,<12.0 - "ansible-core>=2.19,<2.20" #Correspond ansible>=12.0,<13.0 + steps: + - name: Check out code + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python_version }} + + - name: Install Ansible + run: | + python3 -m pip install --upgrade pip + python3 -m pip install "${{ matrix.ansible_version }}" + + - name: Install collection + run: | + mkdir -p ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE + cp -a ../ansible-collection-$COLLECTION_NAME ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE/$COLLECTION_NAME + + - name: Run sanity tests + run: | + cd ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE/$COLLECTION_NAME + ansible-test sanity --python "${{ matrix.python_version }}" -v + env: + PY_COLORS: '1' + ANSIBLE_FORCE_COLOR: '1' + + sanity_ansible_20: + runs-on: ubuntu-latest + env: + COLLECTION_NAMESPACE: netways + COLLECTION_NAME: elasticstack + strategy: + fail-fast: false + matrix: + python_version: + - "3.12" + - "3.13" + - "3.14" + ansible_version: - "ansible-core>=2.20,<2.21" #Correspond ansible>=13.0,<14.0 steps: - name: Check out code @@ -67,7 +107,9 @@ jobs: ANSIBLE_FORCE_COLOR: '1' unit-test: - needs: ansible_sanity + needs: + - sanity_ansible_18_19 + - sanity_ansible_20 runs-on: ubuntu-latest env: From 42055cdff9c3e5ea66d525445faccff80809b092 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 18:17:05 +0100 Subject: [PATCH 36/49] Correct the needs --- .github/workflows/test_plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 73162ef0..d7c0f285 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -107,7 +107,7 @@ jobs: ANSIBLE_FORCE_COLOR: '1' unit-test: - needs: + needs: - sanity_ansible_18_19 - sanity_ansible_20 runs-on: ubuntu-latest From 4a0534767033dd9e30aa4586f861c3beecdf3767 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 18:28:22 +0100 Subject: [PATCH 37/49] Add python path --- .github/workflows/test_plugins.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index d7c0f285..8008ceef 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -107,9 +107,9 @@ jobs: ANSIBLE_FORCE_COLOR: '1' unit-test: - needs: - - sanity_ansible_18_19 - - sanity_ansible_20 +# needs: +# - sanity_ansible_18_19 +# - sanity_ansible_20 runs-on: ubuntu-latest env: @@ -123,15 +123,15 @@ jobs: - name: Check out code uses: actions/checkout@v6 - - name: Set up Python 3.9.14 + - name: Set up Python 3.11 uses: actions/setup-python@v6 with: - python-version: 3.9.14 + python-version: 3.11 - name: Install dependencies run: | python3 -m pip install --upgrade pip - #python3 -m pip install install ansible + python3 -m pip install "ansible-core>=2.19,<2.20" - name: Install collection run: | @@ -140,14 +140,14 @@ jobs: - name: Test `cert_info` module run: | - python3 tests/unit/plugins/modules/test_cert_info.py + PYTHONPATH=$HOME/.ansible/collections python3 tests/unit/plugins/modules/test_cert_info.py env: PY_COLORS: '1' ANSIBLE_FORCE_COLOR: '1' - name: Test `certs` module util run: | - python3 tests/unit/plugins/module_utils/test_certs.py + PYTHONPATH=$HOME/.ansible/collections python3 tests/unit/plugins/module_utils/test_certs.py env: PY_COLORS: '1' ANSIBLE_FORCE_COLOR: '1' From be20196edc312e0d84445f70f91a5a19159f3883 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 18:33:46 +0100 Subject: [PATCH 38/49] Replace deprecated methon --- tests/unit/plugins/modules/test_cert_info.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/unit/plugins/modules/test_cert_info.py b/tests/unit/plugins/modules/test_cert_info.py index 1af8457d..f5e9ab4d 100644 --- a/tests/unit/plugins/modules/test_cert_info.py +++ b/tests/unit/plugins/modules/test_cert_info.py @@ -1,9 +1,8 @@ -import json import sys import unittest from unittest.mock import patch from ansible.module_utils import basic -from ansible.module_utils.common.text.converters import to_bytes +from ansible.module_utils.testing import set_module_args sys.path.append('/home/runner/.ansible/collections/') from ansible_collections.netways.elasticstack.plugins.modules import cert_info @@ -45,11 +44,6 @@ } -def set_module_args(args): - """prepare arguments so that they will be picked up during module creation""" - args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) - basic._ANSIBLE_ARGS = to_bytes(args) - class AnsibleExitJson(Exception): """Exception class to be raised by module.exit_json and caught by the test case""" From e310197a9f122583758cc8ad44df88b5cf948346 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 18:40:26 +0100 Subject: [PATCH 39/49] Fix unit tests to work with ansible-core 2.19 The test helper for passing module arguments relied on an internal API that was removed in ansible-core 2.19. Replace it with a custom implementation that intercepts the parameter loading at a lower level, making the tests compatible with ansible-core 2.19 and future versions. --- tests/unit/plugins/modules/test_cert_info.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/unit/plugins/modules/test_cert_info.py b/tests/unit/plugins/modules/test_cert_info.py index f5e9ab4d..59fa1a21 100644 --- a/tests/unit/plugins/modules/test_cert_info.py +++ b/tests/unit/plugins/modules/test_cert_info.py @@ -2,10 +2,20 @@ import unittest from unittest.mock import patch from ansible.module_utils import basic -from ansible.module_utils.testing import set_module_args sys.path.append('/home/runner/.ansible/collections/') from ansible_collections.netways.elasticstack.plugins.modules import cert_info +_current_module_args = {} + + +def set_module_args(args): + global _current_module_args + _current_module_args = args + + +def _mock_load_params(): + return _current_module_args + certificate = { "changed": False, "extensions": { @@ -90,6 +100,11 @@ def setUp(self): self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) + self.load_params_patcher = patch('ansible.module_utils.basic._load_params', + side_effect=_mock_load_params) + self.load_params_patcher.start() + self.addCleanup(self.load_params_patcher.stop) + def test_module_fail_when_required_args_missing(self): with self.assertRaises(AnsibleFailJson): set_module_args({}) From e1534eb29bbade1213796e3b9daf3b3b677002b1 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Mon, 23 Mar 2026 18:46:18 +0100 Subject: [PATCH 40/49] Use up to date version in python kob --- .github/workflows/test_plugins.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 8008ceef..9dddcf24 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -163,7 +163,7 @@ jobs: strategy: fail-fast: false matrix: - python_version: [ 3.5.10, 3.6.15, 3.7.13, 3.8.16, 3.10.10 ] + python_version: [ "3.11", "3.12", "3.13" ] steps: - name: Check out code @@ -176,8 +176,8 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip - python -m pip install install ansible + python3 -m pip install --upgrade pip + python3 -m pip install "ansible-core>=2.19,<2.20" - name: Install collection run: | From 8ef47778b17e39f6cc9e419fb4aaf705c110aca9 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Tue, 24 Mar 2026 08:25:58 +0100 Subject: [PATCH 41/49] Fix spaces before class --- tests/unit/plugins/modules/test_cert_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/plugins/modules/test_cert_info.py b/tests/unit/plugins/modules/test_cert_info.py index 59fa1a21..6613bdbd 100644 --- a/tests/unit/plugins/modules/test_cert_info.py +++ b/tests/unit/plugins/modules/test_cert_info.py @@ -16,6 +16,7 @@ def set_module_args(args): def _mock_load_params(): return _current_module_args + certificate = { "changed": False, "extensions": { @@ -54,7 +55,6 @@ def _mock_load_params(): } - class AnsibleExitJson(Exception): """Exception class to be raised by module.exit_json and caught by the test case""" pass From 2ca99f152951851f28d883febc3d16f0a83c2f3d Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Tue, 24 Mar 2026 10:35:13 +0100 Subject: [PATCH 42/49] * Merge the elasticsearch_test_modules with plugin workflow Both workflows have the same goal. They should test our modules Testing cert_info in one workflow and the elastic modules in another one is not efficient * Put some verify tasks to test elasticsearch_role and elasticsearch_user * Remove pep8 config because we are using sanity now --- .config/pep8.cfg | 4 - .../workflows/test_elasticsearch_modules.yml | 71 --------- .github/workflows/test_plugins.yml | 111 ++++++-------- .../elasticsearch_test_modules/converge.yml | 64 -------- molecule/plugins/converge.yml | 102 ++++++++----- .../molecule.yml | 6 +- .../prepare.yml | 10 +- .../requirements.yml | 0 molecule/plugins/verify.yml | 137 ++++++++++++++++++ 9 files changed, 247 insertions(+), 258 deletions(-) delete mode 100644 .config/pep8.cfg delete mode 100644 .github/workflows/test_elasticsearch_modules.yml delete mode 100644 molecule/elasticsearch_test_modules/converge.yml rename molecule/{elasticsearch_test_modules => plugins}/molecule.yml (70%) rename molecule/{elasticsearch_test_modules => plugins}/prepare.yml (60%) rename molecule/{elasticsearch_test_modules => plugins}/requirements.yml (100%) create mode 100644 molecule/plugins/verify.yml diff --git a/.config/pep8.cfg b/.config/pep8.cfg deleted file mode 100644 index 7717b0f6..00000000 --- a/.config/pep8.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[pep8] -ignore = E402, E123 -# It's fine to have line-length of 99 -max-line-length = 200 diff --git a/.github/workflows/test_elasticsearch_modules.yml b/.github/workflows/test_elasticsearch_modules.yml deleted file mode 100644 index d1ea2675..00000000 --- a/.github/workflows/test_elasticsearch_modules.yml +++ /dev/null @@ -1,71 +0,0 @@ ---- -name: Test Elasticsearch modules -on: - workflow_dispatch: - inputs: - logLevel: - description: 'Log level' - required: true - default: 'warning' - type: choice - options: - - info - - warning - - debug - pull_request: - paths: - - '.github/workflows/test_elasticsearch_modules.yml' - - 'molecule/elasticsearch_test_modules/*' - -jobs: - molecule_elasticsearch_modules: - runs-on: ubuntu-latest - - env: - COLLECTION_NAMESPACE: netways - COLLECTION_NAME: elasticstack - - strategy: - fail-fast: false - matrix: - distro: - - ubuntu2204 - scenario: - - elasticsearch_test_modules - release: - - 8 - python_version: - - "3.11" - ansible_version: - - "ansible-core>=2.19,<2.20" #Correspond ansible>=12.0,<13.0 - - steps: - - name: Check out code - uses: actions/checkout@v6 - - - name: Set up Python ${{ matrix.python_version }} - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python_version }} - - - name: Install dependencies - run: | - python3 -m pip install --upgrade pip - python3 -m pip install "${{ matrix.ansible_version }}" - python3 -m pip install -r requirements-test.txt - - - name: Install collection - run: | - mkdir -p ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE - cp -a ../ansible-collection-$COLLECTION_NAME ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE/$COLLECTION_NAME - - - name: Test with molecule - run: | - ansible --version - molecule --version - molecule test -s ${{ matrix.scenario }} - env: - MOLECULE_DISTRO: ${{ matrix.distro }} - PY_COLORS: '1' - ANSIBLE_FORCE_COLOR: '1' - ELASTIC_RELEASE: ${{ matrix.release }} diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 9dddcf24..334f2817 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -17,10 +17,9 @@ on: - 'main' paths: - 'plugins/**' - - '.github/workflows/test_plugins.yml' - - 'molecule/plugins/**' - - '.config/pep8.yml' - 'tests/**' + - 'molecule/plugins/**' + - '.github/workflows/test_plugins.yml' jobs: sanity_ansible_18_19: @@ -42,7 +41,7 @@ jobs: - name: Check out code uses: actions/checkout@v6 - - name: Set up Python + - name: Set up Python ${{ matrix.python_version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python_version }} @@ -83,7 +82,7 @@ jobs: - name: Check out code uses: actions/checkout@v6 - - name: Set up Python + - name: Set up Python ${{ matrix.python_version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python_version }} @@ -107,18 +106,15 @@ jobs: ANSIBLE_FORCE_COLOR: '1' unit-test: -# needs: -# - sanity_ansible_18_19 -# - sanity_ansible_20 + needs: + - sanity_ansible_18_19 + - sanity_ansible_20 runs-on: ubuntu-latest - env: COLLECTION_NAMESPACE: netways COLLECTION_NAME: elasticstack - strategy: fail-fast: false - steps: - name: Check out code uses: actions/checkout@v6 @@ -152,19 +148,26 @@ jobs: PY_COLORS: '1' ANSIBLE_FORCE_COLOR: '1' - python: + molecule_plugins: needs: unit-test runs-on: ubuntu-latest - env: COLLECTION_NAMESPACE: netways COLLECTION_NAME: elasticstack - strategy: fail-fast: false matrix: - python_version: [ "3.11", "3.12", "3.13" ] - + distro: + - ubuntu2204 + scenario: + - plugins + release: + - 8 + # - 9 # add when elasticsearch>=9 is supported by this collection + python_version: + - "3.11" + ansible_version: + - "ansible-core>=2.19,<2.20" #Correspond ansible>=12.0,<13.0 steps: - name: Check out code uses: actions/checkout@v6 @@ -177,95 +180,63 @@ jobs: - name: Install dependencies run: | python3 -m pip install --upgrade pip - python3 -m pip install "ansible-core>=2.19,<2.20" - - - name: Install collection - run: | - mkdir -p ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE - cp -a ../ansible-collection-$COLLECTION_NAME ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE/$COLLECTION_NAME - - - name: Test with ansible-playbook - run: | - ansible-playbook molecule/plugins/converge.yml - env: - PY_COLORS: '1' - ANSIBLE_FORCE_COLOR: '1' - - ansible-core: - needs: python - runs-on: ubuntu-latest - - env: - COLLECTION_NAMESPACE: netways - COLLECTION_NAME: elasticstack - - strategy: - fail-fast: false - matrix: - ansible_core_version: [ 2.11.12, 2.12.10, 2.13.8, 2.14.4 ] - - steps: - - name: Check out code - uses: actions/checkout@v6 - - - name: Set up Python 3.9.14 - uses: actions/setup-python@v6 - with: - python-version: 3.9.14 - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install install ansible-core==${{ matrix.ansible_core_version }} + python3 -m pip install "${{ matrix.ansible_version }}" + python3 -m pip install -r requirements-test.txt - name: Install collection run: | mkdir -p ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE cp -a ../ansible-collection-$COLLECTION_NAME ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE/$COLLECTION_NAME - - name: Test with ansible-playbook + - name: Test with molecule run: | - ansible-playbook molecule/plugins/converge.yml + ansible --version + molecule --version + molecule test -s ${{ matrix.scenario }} env: + MOLECULE_DISTRO: ${{ matrix.distro }} PY_COLORS: '1' ANSIBLE_FORCE_COLOR: '1' + ELASTIC_RELEASE: ${{ matrix.release }} python-cryptography: - needs: ansible-core + needs: unit-test runs-on: ubuntu-latest - env: COLLECTION_NAMESPACE: netways COLLECTION_NAME: elasticstack - strategy: fail-fast: false matrix: - python_cryptography_version: [ 2.5, 3.0, 3.1, 3.2, 3.3, 3.4, 35.0.0, 36.0.0, 38.0.0, 40.0.1] - + python_cryptography_version: + - "38.0.0" + - "40.0.1" + - "41.0.0" + - "42.0.0" + - "43.0.0" steps: - name: Check out code uses: actions/checkout@v6 - - name: Set up Python 3.9.14 + - name: Set up Python 3.11 uses: actions/setup-python@v6 with: - python-version: 3.9.14 + python-version: 3.11 - name: Install dependencies run: | - python -m pip install --upgrade pip - python -m pip install install cryptography==${{ matrix.python_cryptography_version }} - python -m pip install install ansible + python3 -m pip install --upgrade pip + python3 -m pip install "ansible-core>=2.19,<2.20" + python3 -m pip install "cryptography==${{ matrix.python_cryptography_version }}" - name: Install collection run: | mkdir -p ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE cp -a ../ansible-collection-$COLLECTION_NAME ~/.ansible/collections/ansible_collections/$COLLECTION_NAMESPACE/$COLLECTION_NAME - - name: Test with ansible-playbook + - name: Test cert_info with cryptography ${{ matrix.python_cryptography_version }} run: | - ansible-playbook molecule/plugins/converge.yml + PYTHONPATH=$HOME/.ansible/collections python3 tests/unit/plugins/modules/test_cert_info.py env: PY_COLORS: '1' ANSIBLE_FORCE_COLOR: '1' diff --git a/molecule/elasticsearch_test_modules/converge.yml b/molecule/elasticsearch_test_modules/converge.yml deleted file mode 100644 index 77ffdd66..00000000 --- a/molecule/elasticsearch_test_modules/converge.yml +++ /dev/null @@ -1,64 +0,0 @@ ---- -# The workaround for arbitrarily named role directory is important because the git repo has one name and the role within it another -# Found at: https://github.com/ansible-community/molecule/issues/1567#issuecomment-436876722 -- name: Converge - collections: - - netways.elasticstack - hosts: all - vars: - elasticstack_full_stack: false - elasticsearch_jna_workaround: true - elasticsearch_disable_systemcallfilterchecks: true - elasticstack_release: "{{ lookup('env', 'ELASTIC_RELEASE') | int}}" - elasticsearch_heap: "1" - elasticstack_no_log: false - tasks: - - name: Include Elastics repos role - ansible.builtin.include_role: - name: repos - - name: Include Elasticsearch - ansible.builtin.include_role: - name: elasticsearch - - - name: Fetch Elastic password # noqa: risky-shell-pipe - ansible.builtin.shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - grep "PASSWORD elastic" /usr/share/elasticsearch/initial_passwords | - awk {' print $4 '} - register: elasticstack_password - changed_when: false - - - name: Create elasticsearch role 'new-role' - netways.elasticstack.elasticsearch_role: - name: new-role1 - cluster: - - manage_own_api_key - - delegate_pki - indicies: - - names: - - foobar321 - privileges: - - read - - write - state: present - host: https://localhost:9200 - auth_user: elastic - auth_pass: "{{ elasticstack_password.stdout }}" - verify_certs: false - - - name: Create elasticsearch user 'new-user' - netways.elasticstack.elasticsearch_user: - name: new-user1 - fullname: New User - password: "{{ lookup('community.general.random_string', length=12, min_lower=1, min_upper=1, min_numeric=1, min_special=1, override_special='-_=!') }}" - email: new@user.de - roles: - - new-role1 - - logstash-writer - enabled: true - state: present - host: https://localhost:9200 - auth_user: elastic - auth_pass: "{{ elasticstack_password.stdout }}" - verify_certs: false - ca_certs: /etc/elasticsearch/certs/http_ca.crt diff --git a/molecule/plugins/converge.yml b/molecule/plugins/converge.yml index 633082d5..13946437 100644 --- a/molecule/plugins/converge.yml +++ b/molecule/plugins/converge.yml @@ -1,44 +1,68 @@ --- -# The workaround for arbitrarily named role directory is important because the git repo has one name and the role within it another -# Found at: https://github.com/ansible-community/molecule/issues/1567#issuecomment-436876722 - name: Converge collections: - netways.elasticstack - hosts: localhost + hosts: all + vars: + elasticstack_full_stack: false + elasticsearch_jna_workaround: true + elasticsearch_disable_systemcallfilterchecks: true + elasticstack_release: "{{ lookup('env', 'ELASTIC_RELEASE') | int }}" + elasticsearch_heap: "1" + elasticstack_no_log: false tasks: - # - # Test modules - # - - name: Test - cert_info: - path: files/es-ca/elastic-stack-ca.p12 - passphrase: PleaseChangeMe - register: test - - name: Debug - ansible.builtin.debug: - msg: "{{ test }}" - - name: Test required parameters (missing path) - cert_info: - passphrase: PleaseChangeMe - ignore_errors: true - - name: Test wrong path - cert_info: - path: es-ca-wrong - passphrase: PleaseChangeMe - ignore_errors: true - - name: Debug with to_datetime() - (( test.not_valid_after | to_datetime()) - (ansible_date_time.date | to_datetime('%Y-%m-%d'))).days - ansible.builtin.debug: - msg: >- - "{{ (( test.not_valid_after | to_datetime()) - (ansible_date_time.date | to_datetime('%Y-%m-%d'))).days }}" - - name: Test wrong passphrase - cert_info: - path: files/es-ca/elastic-stack-ca.p12 - passphrase: PleaseChangeMe-wrong - ignore_errors: true - - name: Test no passphrase - cert_info: - path: files/es-ca/elastic-stack-ca.p12 - ignore_errors: true - - name: Test no parameters - cert_info: - ignore_errors: true + - name: Include Elastic repos role + ansible.builtin.include_role: + name: repos + + - name: Include Elasticsearch + ansible.builtin.include_role: + name: elasticsearch + + - name: Fetch Elastic password # noqa: risky-shell-pipe + ansible.builtin.shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + grep "PASSWORD elastic" /usr/share/elasticsearch/initial_passwords | + awk {' print $4 '} + register: elasticstack_password + changed_when: false + + - name: Create Elasticsearch role + netways.elasticstack.elasticsearch_role: + name: test-role + cluster: + - manage_own_api_key + - delegate_pki + indicies: + - names: + - test-index + privileges: + - read + - write + state: present + host: https://localhost:9200 + auth_user: elastic + auth_pass: "{{ elasticstack_password.stdout }}" + verify_certs: false + + - name: Create Elasticsearch user + netways.elasticstack.elasticsearch_user: + name: test-user + fullname: Test User + password: "{{ lookup('community.general.random_string', length=12, min_lower=1, min_upper=1, min_numeric=1, min_special=1, override_special='-_=!') }}" + email: test@example.com + roles: + - test-role + enabled: true + state: present + host: https://localhost:9200 + auth_user: elastic + auth_pass: "{{ elasticstack_password.stdout }}" + verify_certs: false + ca_certs: /etc/elasticsearch/certs/http_ca.crt + + - name: Copy PKCS12 certificate for cert_info testing + ansible.builtin.copy: + src: files/es-ca/elastic-stack-ca.p12 + dest: /tmp/elastic-stack-ca.p12 + mode: '0644' diff --git a/molecule/elasticsearch_test_modules/molecule.yml b/molecule/plugins/molecule.yml similarity index 70% rename from molecule/elasticsearch_test_modules/molecule.yml rename to molecule/plugins/molecule.yml index 80e445aa..90a13bde 100644 --- a/molecule/elasticsearch_test_modules/molecule.yml +++ b/molecule/plugins/molecule.yml @@ -6,7 +6,7 @@ dependency: driver: name: docker platforms: - - name: "elasticsearch_default-${MOLECULE_DISTRO:-debian13}" + - name: "plugins-${MOLECULE_DISTRO:-debian13}" groups: - elasticsearch image: "geerlingguy/docker-${MOLECULE_DISTRO:-debian13}-ansible:latest" @@ -18,9 +18,5 @@ platforms: pre_build_image: true provisioner: name: ansible - # Just enable temporarily. Sometimes it's useful, but most of the time it's - # overwhelming - #env: - # ANSIBLE_VERBOSITY: 3 verifier: name: ansible diff --git a/molecule/elasticsearch_test_modules/prepare.yml b/molecule/plugins/prepare.yml similarity index 60% rename from molecule/elasticsearch_test_modules/prepare.yml rename to molecule/plugins/prepare.yml index 3ba4785a..e4b02224 100644 --- a/molecule/elasticsearch_test_modules/prepare.yml +++ b/molecule/plugins/prepare.yml @@ -3,7 +3,7 @@ hosts: all tasks: - name: Show discovered interpreter - debug: + ansible.builtin.debug: var: ansible_facts.discovered_interpreter_python - name: Install packages for Debian @@ -19,8 +19,8 @@ update_cache: yes when: ansible_os_family == "Debian" - - name: Install python module dependencies + - name: Install Python dependencies ansible.builtin.pip: - name: "{{ item }}" - loop: - - elasticsearch + name: + - "elasticsearch<9" # Version 9+ has breaking API changes incompatible with this collection + - cryptography # latest version; version matrix is tested in the python-cryptography CI job diff --git a/molecule/elasticsearch_test_modules/requirements.yml b/molecule/plugins/requirements.yml similarity index 100% rename from molecule/elasticsearch_test_modules/requirements.yml rename to molecule/plugins/requirements.yml diff --git a/molecule/plugins/verify.yml b/molecule/plugins/verify.yml new file mode 100644 index 00000000..c97148a9 --- /dev/null +++ b/molecule/plugins/verify.yml @@ -0,0 +1,137 @@ +--- +- name: Verify all plugin modules + hosts: all + vars: + elasticstack_elasticsearch_http_port: 9200 + elasticstack_initial_passwords: /usr/share/elasticsearch/initial_passwords + tasks: + - name: Fetch Elastic password # noqa: risky-shell-pipe + ansible.builtin.shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + grep "PASSWORD elastic" {{ elasticstack_initial_passwords }} | + awk {' print $4 '} + register: elasticstack_password + changed_when: false + + # --- cert_info --- + + - name: Test cert_info with valid certificate and passphrase + netways.elasticstack.cert_info: + path: /tmp/elastic-stack-ca.p12 + passphrase: PleaseChangeMe + register: cert_result + + - name: Assert cert_info returns all expected fields + ansible.builtin.assert: + that: + - cert_result.issuer is defined + - cert_result.subject is defined + - cert_result.not_valid_after is defined + - cert_result.not_valid_before is defined + - cert_result.serial_number is defined + - cert_result.extensions is defined + - not cert_result.failed + fail_msg: "cert_info did not return all expected fields" + + - name: Test cert_info fails with wrong passphrase + netways.elasticstack.cert_info: + path: /tmp/elastic-stack-ca.p12 + passphrase: wrong-passphrase + register: cert_wrong_pass + ignore_errors: true + + - name: Assert cert_info failed with wrong passphrase + ansible.builtin.assert: + that: cert_wrong_pass.failed + fail_msg: "cert_info should fail with a wrong passphrase" + + - name: Test cert_info fails with non-existent path + netways.elasticstack.cert_info: + path: /tmp/does-not-exist.p12 + passphrase: PleaseChangeMe + register: cert_wrong_path + ignore_errors: true + + - name: Assert cert_info failed with non-existent path + ansible.builtin.assert: + that: cert_wrong_path.failed + fail_msg: "cert_info should fail with a non-existent path" + + # --- elasticsearch_role --- + + - name: Assert Elasticsearch role exists + ansible.builtin.uri: + url: "https://localhost:{{ elasticstack_elasticsearch_http_port }}/_security/role/test-role" + method: GET + force_basic_auth: true + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: false + status_code: 200 + register: role_result + + - name: Assert role has expected cluster privileges + ansible.builtin.assert: + that: + - "'manage_own_api_key' in role_result.json['test-role'].cluster" + - "'delegate_pki' in role_result.json['test-role'].cluster" + fail_msg: "Role does not have expected cluster privileges" + + # --- elasticsearch_user --- + + - name: Assert Elasticsearch user exists + ansible.builtin.uri: + url: "https://localhost:{{ elasticstack_elasticsearch_http_port }}/_security/user/test-user" + method: GET + force_basic_auth: true + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: false + status_code: 200 + register: user_result + + - name: Assert user has expected role assigned + ansible.builtin.assert: + that: + - "'test-role' in user_result.json['test-user'].roles" + fail_msg: "User does not have expected role assigned" + + # --- test state: absent (user first, then role due to dependency) --- + + - name: Delete Elasticsearch user + netways.elasticstack.elasticsearch_user: + name: test-user + state: absent + host: "https://localhost:{{ elasticstack_elasticsearch_http_port }}" + auth_user: elastic + auth_pass: "{{ elasticstack_password.stdout }}" + verify_certs: false + + - name: Assert Elasticsearch user was deleted + ansible.builtin.uri: + url: "https://localhost:{{ elasticstack_elasticsearch_http_port }}/_security/user/test-user" + method: GET + force_basic_auth: true + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: false + status_code: 404 + + - name: Delete Elasticsearch role + netways.elasticstack.elasticsearch_role: + name: test-role + state: absent + host: "https://localhost:{{ elasticstack_elasticsearch_http_port }}" + auth_user: elastic + auth_pass: "{{ elasticstack_password.stdout }}" + verify_certs: false + + - name: Assert Elasticsearch role was deleted + ansible.builtin.uri: + url: "https://localhost:{{ elasticstack_elasticsearch_http_port }}/_security/role/test-role" + method: GET + force_basic_auth: true + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: false + status_code: 404 From daff7830171f01fb69085317a5b77e4d2d3d934d Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Tue, 24 Mar 2026 11:24:30 +0100 Subject: [PATCH 43/49] * Make the password and user in elasticsearch_user module only required if the sate present is --- plugins/modules/elasticsearch_user.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/plugins/modules/elasticsearch_user.py b/plugins/modules/elasticsearch_user.py index f3a58fb4..d5b1dd4b 100644 --- a/plugins/modules/elasticsearch_user.py +++ b/plugins/modules/elasticsearch_user.py @@ -29,18 +29,20 @@ type: str required: false password: - description: Password for the user. + description: Password for the user. Required when (state=present). type: str - required: true + required: false + default: null email: description: Email address of the user. type: str required: false roles: - description: List of roles assigned to the user. + description: List of roles assigned to the user. Required when (state=present). type: list elements: str - required: true + required: false + default: null enabled: description: Whether the user account is enabled. type: bool @@ -94,8 +96,6 @@ - name: Delete an Elasticsearch user netways.elasticstack.elasticsearch_user: name: john - password: "{{ user_password }}" - roles: [] state: absent host: https://localhost:9200 auth_user: elastic @@ -130,9 +130,9 @@ def run_module(): # User args name=dict(type='str', required=True), fullname=dict(type='str', required=False), - password=dict(type='str', required=True, no_log=True), + password=dict(type='str', required=False, no_log=True), email=dict(type='str', required=False), - roles=dict(type='list', elements='str', required=True), + roles=dict(type='list', elements='str', required=False), enabled=dict(type='bool', required=False, default=True), state=dict(type='str', required=False, default='present', choices=['present', 'absent']), @@ -142,7 +142,10 @@ def run_module(): auth_pass=dict(type='str', required=True, no_log=True), ca_certs=dict(type='str', required=False), verify_certs=dict(type='bool', required=False, default=True) - ) + ), + required_if=[ + ('state', 'present', ['password', 'roles']), + ] ) result = dict( From d79a11f908fef066b99d9044fad03b3185befa31 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Tue, 24 Mar 2026 12:23:32 +0100 Subject: [PATCH 44/49] Reactivate test_roles_pr --- .github/workflows/test_roles_pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_roles_pr.yml b/.github/workflows/test_roles_pr.yml index 9e3386c1..f0d0245e 100644 --- a/.github/workflows/test_roles_pr.yml +++ b/.github/workflows/test_roles_pr.yml @@ -12,8 +12,8 @@ on: - info - warning - debug -# pull_request: -# merge_group: + pull_request: + merge_group: jobs: lint_full: From bce2e7cd06cb5a134666797fdc618f421870c3f5 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Tue, 24 Mar 2026 16:03:32 +0100 Subject: [PATCH 45/49] Use static ansible and python version In some plaxes we don't nedd to test using multiple ansible and python version, so let the version static not variable --- .github/workflows/test_plugins.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 334f2817..659f0916 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -164,23 +164,19 @@ jobs: release: - 8 # - 9 # add when elasticsearch>=9 is supported by this collection - python_version: - - "3.11" - ansible_version: - - "ansible-core>=2.19,<2.20" #Correspond ansible>=12.0,<13.0 steps: - name: Check out code uses: actions/checkout@v6 - - name: Set up Python ${{ matrix.python_version }} + - name: Set up Python 3.11 uses: actions/setup-python@v6 with: - python-version: ${{ matrix.python_version }} + python-version: "3.11" - name: Install dependencies run: | python3 -m pip install --upgrade pip - python3 -m pip install "${{ matrix.ansible_version }}" + python3 -m pip install "ansible-core>=2.19,<2.20" python3 -m pip install -r requirements-test.txt - name: Install collection From 0f7af31cdef08da4b0fd5e933ab6ca81e7897353 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Tue, 24 Mar 2026 16:37:51 +0100 Subject: [PATCH 46/49] Use sensible cryptography version --- .github/workflows/test_plugins.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index 659f0916..feb31f5b 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -205,11 +205,10 @@ jobs: fail-fast: false matrix: python_cryptography_version: - - "38.0.0" - - "40.0.1" - - "41.0.0" - - "42.0.0" - - "43.0.0" + - "3.4.8" # Test loading certificate with 3 parameter (old way). + - "41.0.0" # Last version before not_valid_after() and not_valid_before() deprecation. + - "42.0.0" # First release with deprecation https://cryptography.io/en/latest/changelog/ + - "47.0.0" # Latest release steps: - name: Check out code uses: actions/checkout@v6 From dca251637ef8cc6588b7f39984d2eb112a9b0834 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Tue, 24 Mar 2026 16:53:04 +0100 Subject: [PATCH 47/49] Correct the cryptography versions --- .github/workflows/test_plugins.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_plugins.yml b/.github/workflows/test_plugins.yml index feb31f5b..0045ca84 100644 --- a/.github/workflows/test_plugins.yml +++ b/.github/workflows/test_plugins.yml @@ -206,9 +206,9 @@ jobs: matrix: python_cryptography_version: - "3.4.8" # Test loading certificate with 3 parameter (old way). - - "41.0.0" # Last version before not_valid_after() and not_valid_before() deprecation. + - "41.0.7" # Last version before not_valid_after() and not_valid_before() deprecation. - "42.0.0" # First release with deprecation https://cryptography.io/en/latest/changelog/ - - "47.0.0" # Latest release + - "46.0.5" # Latest release steps: - name: Check out code uses: actions/checkout@v6 From c6db7f27b1b5533f1da04712609c0c3588ca7b2a Mon Sep 17 00:00:00 2001 From: Afeef Ghannam <39904920+afeefghannam89@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:18:06 +0100 Subject: [PATCH 48/49] Update molecule/plugins/verify.yml Co-authored-by: Thomas Widhalm --- molecule/plugins/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molecule/plugins/verify.yml b/molecule/plugins/verify.yml index c97148a9..1799c898 100644 --- a/molecule/plugins/verify.yml +++ b/molecule/plugins/verify.yml @@ -18,7 +18,7 @@ - name: Test cert_info with valid certificate and passphrase netways.elasticstack.cert_info: path: /tmp/elastic-stack-ca.p12 - passphrase: PleaseChangeMe + passphrase: PleaseChangeMe # uses default value. Change if default is changed register: cert_result - name: Assert cert_info returns all expected fields From 83d7844fa5963658bf1bf6353e5df940db180eaf Mon Sep 17 00:00:00 2001 From: Afeef Ghannam <39904920+afeefghannam89@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:19:17 +0100 Subject: [PATCH 49/49] Update molecule/plugins/verify.yml Add clarification to a variable usage Co-authored-by: Thomas Widhalm