From e50d82dc7eac4dcadbf738d2bbf80baa4d0c4744 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Fri, 18 Feb 2022 14:52:59 +0100 Subject: [PATCH 01/71] Add github workflow for integration tests. --- .github/workflows/integration.yaml | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/integration.yaml diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml new file mode 100644 index 000000000..4c6c67082 --- /dev/null +++ b/.github/workflows/integration.yaml @@ -0,0 +1,48 @@ +--- +name: Ansible Collection Integration Tests +on: # yamllint disable-line rule:truthy + push: + branches: + - 'main' + - 'devel' + +jobs: + ansible-test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [2.6, 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Dependencies. + run: | + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: Copy Files and Directories to Source + run: | + mkdir -p ansible_collections/$namespace/$collection/ + cp $files ansible_collections/$namespace/$collection/ + cp -rf $directories ansible_collections/$namespace/$collection/ + env: + # Keep in sync with release.yaml! + files: "README.md ansible.cfg galaxy.yml" + directories: "changelogs docs meta playbooks plugins" + namespace: "tribe29" + collection: "checkmk" + + - name: Run Sanity Checks. + run: cd ansible_collections/$namespace/$collection/ && ansible-test integration + env: + namespace: "tribe29" + collection: "checkmk" + +# ToDo: Automatically raise issue? From 8557b0f86eadf334310d0e2936adfcee93709f34 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Fri, 18 Feb 2022 15:24:19 +0100 Subject: [PATCH 02/71] Add full integration test. --- tests/integration/targets/full/tasks/main.yml | 10 ++ tests/integration/targets/full/tasks/prep.yml | 20 +++ tests/integration/targets/full/tasks/test.yml | 163 ++++++++++++++++++ tests/integration/targets/full/vars/main.yml | 42 +++++ 4 files changed, 235 insertions(+) create mode 100644 tests/integration/targets/full/tasks/main.yml create mode 100644 tests/integration/targets/full/tasks/prep.yml create mode 100644 tests/integration/targets/full/tasks/test.yml create mode 100644 tests/integration/targets/full/vars/main.yml diff --git a/tests/integration/targets/full/tasks/main.yml b/tests/integration/targets/full/tasks/main.yml new file mode 100644 index 000000000..22c526e43 --- /dev/null +++ b/tests/integration/targets/full/tasks/main.yml @@ -0,0 +1,10 @@ +--- +- name: "Run preparations." + ansible.builtin.import_tasks: prep.yml + +- name: "Wait for site to be ready." + ansible.builtin.pause: + seconds: 5 + +- name: "Run tests." + ansible.builtin.import_tasks: test.yml diff --git a/tests/integration/targets/full/tasks/prep.yml b/tests/integration/targets/full/tasks/prep.yml new file mode 100644 index 000000000..802f81517 --- /dev/null +++ b/tests/integration/targets/full/tasks/prep.yml @@ -0,0 +1,20 @@ +--- +- name: "Download Checkmk." + ansible.builtin.get_url: + url: "{{ download_url }}" + dest: /tmp/checkmk-server.deb + +- name: "Install Checkmk." + ansible.builtin.apt: + deb: /tmp/checkmk-server.deb + state: present + +- name: "Create site." + ansible.builtin.command: "omd create --no-tmpfs --admin-password {{ automation_secret }} {{ site }}" + args: + creates: "/omd/sites/{{ site }}" + register: omd_state_create + +- name: "Start site." + ansible.builtin.command: "omd start {{ site }}" + register: omd_state_start diff --git a/tests/integration/targets/full/tasks/test.yml b/tests/integration/targets/full/tasks/test.yml new file mode 100644 index 000000000..7bbfdec7e --- /dev/null +++ b/tests/integration/targets/full/tasks/test.yml @@ -0,0 +1,163 @@ +--- +# Take this from playbooks/test-full.yml to ensure full coverage! +# Be sure to remove header! + +# - name: "Test all modules." +# hosts: all +# gather_facts: 'no' +# vars_files: +# - ./vars/config.yml +# tasks: + - name: "Run activation module - 1." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' + + - name: "Create folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "present" + register: testout + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + + - name: "Create hosts." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + attributes: + site: "{{ site }}" + ipaddress: 127.0.0.1 + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + + - name: "Discover hosts." + discovery: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + state: "fix_all" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + + - name: "Run activation module - 2." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' + + - name: "Change host attributes." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + attributes: + site: "{{ site }}" + alias: "Important Server" + ipaddress: 127.0.0.2 + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + + - name: "Run activation module - 3." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' + + - name: "Move host to another folder." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "/bar" + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + + - name: "Run activation module - 4." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' + + - name: "Delete hosts." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + state: "absent" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + + - name: "Delete folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "absent" + register: testout + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + + - name: "Run activation module - 5." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' diff --git a/tests/integration/targets/full/vars/main.yml b/tests/integration/targets/full/vars/main.yml new file mode 100644 index 000000000..be2ed01c6 --- /dev/null +++ b/tests/integration/targets/full/vars/main.yml @@ -0,0 +1,42 @@ +--- +download_url: "https://download.checkmk.com/checkmk/2.0.0p20/check-mk-raw-2.0.0p20_0.bionic_amd64.deb" +site: "test" +server_url: "http://127.0.0.1/" +automation_user: "cmkadmin" +automation_secret: "d7586df1-01db-3eda-9858-dbcf18d0c361" + +checkmk_folders: + - path: /test + title: Test + - path: /foo + title: Foo + - path: /bar + title: Bar + - path: /foo/bar + title: Bar + - path: /bar/foo + title: Foo + - path: /foo/bar/treasure + title: Treasure + +checkmk_hosts: + - name: test1.tld + folder: "/test" + - name: test2.tld + folder: "/foo" + - name: test3.tld + folder: "/bar" + - name: test4.tld + folder: "/" + - name: test5.tld + folder: "/foo/bar" + - name: test6.tld + folder: "/bar/foo" + - name: test7.tld + folder: "/" + - name: test8.tld + folder: "/test" + - name: test9.tld + folder: "/bar" + - name: test10.tld + folder: "/foo" From 162cdfd1efa43040c22bd6474e0cfb5d9d546a62 Mon Sep 17 00:00:00 2001 From: Lars Getwan Date: Fri, 11 Mar 2022 08:14:48 +0100 Subject: [PATCH 03/71] Indent cosmetics and update of Ansible examples. --- plugins/modules/host.py | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/plugins/modules/host.py b/plugins/modules/host.py index 817c95246..bb8f81c6d 100644 --- a/plugins/modules/host.py +++ b/plugins/modules/host.py @@ -27,14 +27,6 @@ description: The host you want to manage. required: true type: str - ip_address: - description: The IP address of your host. - required: false - type: str - monitored_on_site: - description: The site on which your host should be monitored. - required: false - type: str folder: description: The folder your host is located in. type: str @@ -76,7 +68,6 @@ attributes: alias: "My Host" ip_address: "x.x.x.x" - site: "NAME_OF_DISTRIBUTED_HOST" folder: "/" state: "present" @@ -88,7 +79,9 @@ automation_user: "automation" automation_secret: "$SECRET" host_name: "my_host" - monitored_on_site: "NAME_OF_DISTRIBUTED_HOST" + attributes: + alias: "My Host" + site: "NAME_OF_DISTRIBUTED_HOST" folder: "/" state: "present" """ @@ -164,36 +157,28 @@ def set_host_attributes(module, attributes, base_url, headers): } url = base_url + api_endpoint - response, info = fetch_url( - module, url, module.jsonify(params), headers=headers, method="PUT" - ) + response, info = fetch_url(module, url, module.jsonify(params), headers=headers, method="PUT") if info["status"] != 200: exit_failed( module, - "Error calling API. HTTP code %d. Details: %s, " - % (info["status"], info["body"]), + "Error calling API. HTTP code %d. Details: %s, " % (info["status"], info["body"]), ) def move_host(module, base_url, headers): - api_endpoint = "/objects/host_config/%s/actions/move/invoke" % module.params.get( - "host_name" - ) + api_endpoint = "/objects/host_config/%s/actions/move/invoke" % module.params.get("host_name") params = { "target_folder": module.params.get("folder", "/"), } url = base_url + api_endpoint - response, info = fetch_url( - module, url, module.jsonify(params), headers=headers, method="POST" - ) + response, info = fetch_url(module, url, module.jsonify(params), headers=headers, method="POST") if info["status"] != 200: exit_failed( module, - "Error calling API. HTTP code %d. Details: %s, " - % (info["status"], info["body"]), + "Error calling API. HTTP code %d. Details: %s, " % (info["status"], info["body"]), ) @@ -206,15 +191,12 @@ def create_host(module, attributes, base_url, headers): } url = base_url + api_endpoint - response, info = fetch_url( - module, url, module.jsonify(params), headers=headers, method="POST" - ) + response, info = fetch_url(module, url, module.jsonify(params), headers=headers, method="POST") if info["status"] != 200: exit_failed( module, - "Error calling API. HTTP code %d. Details: %s, " - % (info["status"], info["body"]), + "Error calling API. HTTP code %d. Details: %s, " % (info["status"], info["body"]), ) @@ -227,8 +209,7 @@ def delete_host(module, base_url, headers): if info["status"] != 204: exit_failed( module, - "Error calling API. HTTP code %d. Details: %s, " - % (info["status"], info["body"]), + "Error calling API. HTTP code %d. Details: %s, " % (info["status"], info["body"]), ) From efd27821642850db23faa820ef98e74495c056f8 Mon Sep 17 00:00:00 2001 From: Lars Getwan Date: Fri, 11 Mar 2022 08:15:22 +0100 Subject: [PATCH 04/71] Revert "Indent cosmetics and update of Ansible examples." This was intended to go into the devel branch. :-( This reverts commit 162cdfd1efa43040c22bd6474e0cfb5d9d546a62. --- plugins/modules/host.py | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/plugins/modules/host.py b/plugins/modules/host.py index bb8f81c6d..817c95246 100644 --- a/plugins/modules/host.py +++ b/plugins/modules/host.py @@ -27,6 +27,14 @@ description: The host you want to manage. required: true type: str + ip_address: + description: The IP address of your host. + required: false + type: str + monitored_on_site: + description: The site on which your host should be monitored. + required: false + type: str folder: description: The folder your host is located in. type: str @@ -68,6 +76,7 @@ attributes: alias: "My Host" ip_address: "x.x.x.x" + site: "NAME_OF_DISTRIBUTED_HOST" folder: "/" state: "present" @@ -79,9 +88,7 @@ automation_user: "automation" automation_secret: "$SECRET" host_name: "my_host" - attributes: - alias: "My Host" - site: "NAME_OF_DISTRIBUTED_HOST" + monitored_on_site: "NAME_OF_DISTRIBUTED_HOST" folder: "/" state: "present" """ @@ -157,28 +164,36 @@ def set_host_attributes(module, attributes, base_url, headers): } url = base_url + api_endpoint - response, info = fetch_url(module, url, module.jsonify(params), headers=headers, method="PUT") + response, info = fetch_url( + module, url, module.jsonify(params), headers=headers, method="PUT" + ) if info["status"] != 200: exit_failed( module, - "Error calling API. HTTP code %d. Details: %s, " % (info["status"], info["body"]), + "Error calling API. HTTP code %d. Details: %s, " + % (info["status"], info["body"]), ) def move_host(module, base_url, headers): - api_endpoint = "/objects/host_config/%s/actions/move/invoke" % module.params.get("host_name") + api_endpoint = "/objects/host_config/%s/actions/move/invoke" % module.params.get( + "host_name" + ) params = { "target_folder": module.params.get("folder", "/"), } url = base_url + api_endpoint - response, info = fetch_url(module, url, module.jsonify(params), headers=headers, method="POST") + response, info = fetch_url( + module, url, module.jsonify(params), headers=headers, method="POST" + ) if info["status"] != 200: exit_failed( module, - "Error calling API. HTTP code %d. Details: %s, " % (info["status"], info["body"]), + "Error calling API. HTTP code %d. Details: %s, " + % (info["status"], info["body"]), ) @@ -191,12 +206,15 @@ def create_host(module, attributes, base_url, headers): } url = base_url + api_endpoint - response, info = fetch_url(module, url, module.jsonify(params), headers=headers, method="POST") + response, info = fetch_url( + module, url, module.jsonify(params), headers=headers, method="POST" + ) if info["status"] != 200: exit_failed( module, - "Error calling API. HTTP code %d. Details: %s, " % (info["status"], info["body"]), + "Error calling API. HTTP code %d. Details: %s, " + % (info["status"], info["body"]), ) @@ -209,7 +227,8 @@ def delete_host(module, base_url, headers): if info["status"] != 204: exit_failed( module, - "Error calling API. HTTP code %d. Details: %s, " % (info["status"], info["body"]), + "Error calling API. HTTP code %d. Details: %s, " + % (info["status"], info["body"]), ) From 57285894f200eae5144614bd548a7c8b4ed60ace Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Mon, 14 Mar 2022 14:15:59 +0100 Subject: [PATCH 05/71] Bugfix gh action integration test. --- .github/workflows/integration.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 4c6c67082..cdb3a12cc 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.6, 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10] + python-version: [3.9] steps: - name: Checkout code @@ -39,8 +39,8 @@ jobs: namespace: "tribe29" collection: "checkmk" - - name: Run Sanity Checks. - run: cd ansible_collections/$namespace/$collection/ && ansible-test integration + - name: Run Integration Checks. + run: cd ansible_collections/$namespace/$collection/ && ansible-test integration full env: namespace: "tribe29" collection: "checkmk" From 31acc5b41fc1434759293536444328435e60305b Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Wed, 16 Mar 2022 17:03:49 +0100 Subject: [PATCH 06/71] Add LICENSE note to README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 85f0ad98f..20485adf2 100644 --- a/README.md +++ b/README.md @@ -160,4 +160,4 @@ Please do **not** consider a concrete planning document for the near future! - [Ansible Community code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html) ## Licensing -To be done. +See [LICENSE](LICENSE). From b61b9048aee904d8c0d6fa970432ab047ee34e8e Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Wed, 16 Mar 2022 17:04:16 +0100 Subject: [PATCH 07/71] Try out CodeQL analysis. --- .github/workflows/codeql-analysis.yaml | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yaml diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml new file mode 100644 index 000000000..38656d947 --- /dev/null +++ b/.github/workflows/codeql-analysis.yaml @@ -0,0 +1,70 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main, devel ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '23 4 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 62ba154ba57468e6f1b2da1dae22128515fbb283 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 10:35:18 +0100 Subject: [PATCH 08/71] Clean up and update integration.yml. And add python versions. --- .github/workflows/integration.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index cdb3a12cc..1fdecbdd4 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -1,5 +1,5 @@ --- -name: Ansible Collection Integration Tests +name: Ansible Collection Full Integration Test on: # yamllint disable-line rule:truthy push: branches: @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.9] + python-version: ['2.6', '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10'] steps: - name: Checkout code @@ -44,5 +44,3 @@ jobs: env: namespace: "tribe29" collection: "checkmk" - -# ToDo: Automatically raise issue? From 5deb02cd631f03ff60d03e6dcb05af0d53f3504b Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 10:58:27 +0100 Subject: [PATCH 09/71] Try to fix pathlib import error. --- .github/workflows/release.yaml | 3 +- .github/workflows/sanity.yaml | 1 + playbooks/demo.yml | 3 +- playbooks/test-full.yml | 6 +- plugins/modules/discovery.py | 2 - plugins/modules/folder.py | 25 +- requirements.txt | 3 +- tests/integration/targets/full/tasks/test.yml | 288 +++++++++--------- 8 files changed, 166 insertions(+), 165 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5cebb3cc3..b86db93a7 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,10 +6,9 @@ on: # yamllint disable-line rule:truthy jobs: releaseanddeploy: runs-on: ubuntu-latest - # if: github.repository == 'tribe29/ansible-checkmk' strategy: matrix: - python-version: [3.8] + python-version: [3.9] steps: - name: Checkout code diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml index 5fdf95bac..39fd0c71a 100644 --- a/.github/workflows/sanity.yaml +++ b/.github/workflows/sanity.yaml @@ -1,4 +1,5 @@ --- +# Interesting? https://github.com/marketplace/actions/ansible-test name: Ansible Collection Sanity Tests on: # yamllint disable-line rule:truthy push: diff --git a/playbooks/demo.yml b/playbooks/demo.yml index fcb0090b7..c13ee0ce5 100644 --- a/playbooks/demo.yml +++ b/playbooks/demo.yml @@ -24,8 +24,7 @@ path: "{{ item.path }}" title: "{{ item.title }}" state: "present" - register: testout - delegate_to: localhost + delegate_to: localhost run_once: 'yes' loop: "{{ checkmk_folders }}" diff --git a/playbooks/test-full.yml b/playbooks/test-full.yml index 308cb5e13..bf12099ac 100644 --- a/playbooks/test-full.yml +++ b/playbooks/test-full.yml @@ -24,8 +24,7 @@ path: "{{ item.path }}" title: "{{ item.title }}" state: "present" - register: testout - delegate_to: localhost + delegate_to: localhost run_once: 'yes' loop: "{{ checkmk_folders }}" @@ -134,8 +133,7 @@ path: "{{ item.path }}" title: "{{ item.title }}" state: "absent" - register: testout - delegate_to: localhost + delegate_to: localhost run_once: 'yes' loop: "{{ checkmk_folders }}" diff --git a/plugins/modules/discovery.py b/plugins/modules/discovery.py index a68c065a2..2b8be0232 100644 --- a/plugins/modules/discovery.py +++ b/plugins/modules/discovery.py @@ -73,8 +73,6 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import fetch_url -import pprint - def run_module(): module_args = dict( diff --git a/plugins/modules/folder.py b/plugins/modules/folder.py index 66a8ed17d..c02864517 100644 --- a/plugins/modules/folder.py +++ b/plugins/modules/folder.py @@ -72,11 +72,6 @@ """ RETURN = r""" -http_code: - description: The HTTP code the Checkmk API returns. - type: int - returned: always - sample: '200' message: description: The output message that the module generates. type: str @@ -86,7 +81,17 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import fetch_url -from pathlib import Path +# https://docs.ansible.com/ansible/latest/dev_guide/testing/sanity/import.html +from ansible.module_utils.basic import missing_required_lib +import traceback +try: + from pathlib import Path +except ImportError: + from pathlib2 import Path + HAS_ANOTHER_LIBRARY = False + ANOTHER_LIBRARY_IMPORT_ERROR = traceback.format_exc() +else: + HAS_ANOTHER_LIBRARY = True import json @@ -249,6 +254,14 @@ def run_module(): etag, ) = get_current_folder_state(module, base_url, headers) + # Handle library import error according to the following link: + # https://docs.ansible.com/ansible/latest/dev_guide/testing/sanity/import.html + if not HAS_ANOTHER_LIBRARY: + # Needs: from ansible.module_utils.basic import missing_required_lib + module.fail_json( + msg=missing_required_lib('pathlib2'), + exception=ANOTHER_LIBRARY_IMPORT_ERROR) + # Handle the folder accordingly to above findings and desired state if state == "present" and current_state == "present": headers["If-Match"] = etag diff --git a/requirements.txt b/requirements.txt index 7f15f17bf..73ae5ae19 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ ansible >= 5.1.0 -antsibull-changelog >= 0.12.0 \ No newline at end of file +antsibull-changelog >= 0.12.0 +pathlib2 \ No newline at end of file diff --git a/tests/integration/targets/full/tasks/test.yml b/tests/integration/targets/full/tasks/test.yml index 7bbfdec7e..10c113b41 100644 --- a/tests/integration/targets/full/tasks/test.yml +++ b/tests/integration/targets/full/tasks/test.yml @@ -2,162 +2,154 @@ # Take this from playbooks/test-full.yml to ensure full coverage! # Be sure to remove header! -# - name: "Test all modules." -# hosts: all -# gather_facts: 'no' -# vars_files: -# - ./vars/config.yml -# tasks: - - name: "Run activation module - 1." - activation: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - force_foreign_changes: 'true' - sites: "test" - delegate_to: localhost - run_once: 'true' +- name: "Run activation module - 1." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' - - name: "Create folders." - folder: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - path: "{{ item.path }}" - title: "{{ item.title }}" - state: "present" - register: testout - delegate_to: localhost - run_once: 'yes' - loop: "{{ checkmk_folders }}" +- name: "Create folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" - - name: "Create hosts." - host: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - host_name: "{{ item.name }}" - folder: "{{ item.folder }}" - attributes: - site: "{{ site }}" - ipaddress: 127.0.0.1 - state: "present" - delegate_to: localhost - run_once: 'yes' - loop: "{{ checkmk_hosts }}" +- name: "Create hosts." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + attributes: + site: "{{ site }}" + ipaddress: 127.0.0.1 + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" - - name: "Discover hosts." - discovery: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - host_name: "{{ item.name }}" - state: "fix_all" - delegate_to: localhost - run_once: 'yes' - loop: "{{ checkmk_hosts }}" +- name: "Discover hosts." + discovery: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + state: "fix_all" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" - - name: "Run activation module - 2." - activation: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - force_foreign_changes: 'true' - sites: "test" - delegate_to: localhost - run_once: 'true' +- name: "Run activation module - 2." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' - - name: "Change host attributes." - host: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - host_name: "{{ item.name }}" - folder: "{{ item.folder }}" - attributes: - site: "{{ site }}" - alias: "Important Server" - ipaddress: 127.0.0.2 - state: "present" - delegate_to: localhost - run_once: 'yes' - loop: "{{ checkmk_hosts }}" +- name: "Change host attributes." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + attributes: + site: "{{ site }}" + alias: "Important Server" + ipaddress: 127.0.0.2 + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" - - name: "Run activation module - 3." - activation: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - force_foreign_changes: 'true' - sites: "test" - delegate_to: localhost - run_once: 'true' +- name: "Run activation module - 3." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' - - name: "Move host to another folder." - host: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - host_name: "{{ item.name }}" - folder: "/bar" - state: "present" - delegate_to: localhost - run_once: 'yes' - loop: "{{ checkmk_hosts }}" +- name: "Move host to another folder." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "/bar" + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" - - name: "Run activation module - 4." - activation: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - force_foreign_changes: 'true' - sites: "test" - delegate_to: localhost - run_once: 'true' +- name: "Run activation module - 4." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' - - name: "Delete hosts." - host: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - host_name: "{{ item.name }}" - folder: "{{ item.folder }}" - state: "absent" - delegate_to: localhost - run_once: 'yes' - loop: "{{ checkmk_hosts }}" +- name: "Delete hosts." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + state: "absent" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" - - name: "Delete folders." - folder: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - path: "{{ item.path }}" - title: "{{ item.title }}" - state: "absent" - register: testout - delegate_to: localhost - run_once: 'yes' - loop: "{{ checkmk_folders }}" +- name: "Delete folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "absent" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" - - name: "Run activation module - 5." - activation: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - force_foreign_changes: 'true' - sites: "test" - delegate_to: localhost - run_once: 'true' +- name: "Run activation module - 5." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: "test" + delegate_to: localhost + run_once: 'true' From fe6fd9e44789615158865453e648a3f81abb5735 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:05:16 +0100 Subject: [PATCH 10/71] Add ansible-test workflow. Taken from the ansible-collections project itself: https://github.com/ansible-collections/collection_template/blob/main/.github/workflows/ansible-test.yml --- .github/workflows/ansible-test.yaml | 210 ++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 .github/workflows/ansible-test.yaml diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml new file mode 100644 index 000000000..3551364e5 --- /dev/null +++ b/.github/workflows/ansible-test.yaml @@ -0,0 +1,210 @@ +# https://github.com/ansible-collections/collection_template/blob/main/.github/workflows/ansible-test.yml +# README FIRST +# 1. replace "NAMESPACE" and "COLLECTION_NAME" with the correct name in the env section (e.g. with 'community' and 'mycollection') +# 2. If you don't have unit tests remove that section +# 3. If your collection depends on other collections ensure they are installed, see "Install collection dependencies" +# If you need help please ask in #ansible-community on the Libera.chat IRC network + +name: CI +on: + # Run CI against all pushes (direct commits, also merged PRs), Pull Requests + push: + branches: + - main + - devel +env: + NAMESPACE: tribe29 + COLLECTION_NAME: checkmk + +jobs: + +### +# Sanity tests (REQUIRED) +# +# https://docs.ansible.com/ansible/latest/dev_guide/testing_sanity.html + + sanity: + name: Sanity (Ⓐ${{ matrix.ansible }}) + strategy: + matrix: + ansible: + # It's important that Sanity is tested against all stable-X.Y branches + # Testing against `devel` may fail as new tests are added. + # - stable-2.9 # Only if your collection supports Ansible 2.9 + - stable-2.10 + - stable-2.11 + - stable-2.12 + - devel + runs-on: ubuntu-latest + steps: + + # ansible-test requires the collection to be in a directory in the form + # .../ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}}/ + + - name: Check out code + uses: actions/checkout@v2 + with: + path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + - name: Set up Python + uses: actions/setup-python@v2 + with: + # it is just required to run that once as "ansible-test sanity" in the docker image + # will run on all python versions it supports. + python-version: 3.8 + + # Install the head of the given branch (devel, stable-2.10) + - name: Install ansible-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + # run ansible-test sanity inside of Docker. + # The docker container has all the pinned dependencies that are required + # and all python versions ansible supports. + - name: Run sanity tests + run: ansible-test sanity --docker -v --color --coverage + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # ansible-test support producing code coverage date + - name: Generate coverage report + run: ansible-test coverage xml -v --requirements --group-by command --group-by version + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + - uses: codecov/codecov-action@v2 + with: + fail_ci_if_error: false + +### +# Unit tests (OPTIONAL) +# +# https://docs.ansible.com/ansible/latest/dev_guide/testing_units.html + + # units: + # runs-on: ubuntu-latest + # name: Units (Ⓐ${{ matrix.ansible }}) + # strategy: + # # As soon as the first unit test fails, cancel the others to free up the CI queue + # fail-fast: true + # matrix: + # ansible: + # # - stable-2.9 # Only if your collection supports Ansible 2.9 + # - stable-2.10 + # - stable-2.11 + # - stable-2.12 + # - devel + + # steps: + # - name: Check out code + # uses: actions/checkout@v2 + # with: + # path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # - name: Set up Python + # uses: actions/setup-python@v2 + # with: + # # it is just required to run that once as "ansible-test units" in the docker image + # # will run on all python versions it supports. + # python-version: 3.8 + + # - name: Install ansible-base (${{ matrix.ansible }}) + # run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + # # OPTIONAL If your unit test requires Python libraries from other collections + # # Install them like this + # - name: Install collection dependencies + # run: ansible-galaxy collection install ansible.netcommon ansible.utils -p . + + # # Run the unit tests + # - name: Run unit test + # run: ansible-test units -v --color --docker --coverage + # working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # # ansible-test support producing code coverage date + # - name: Generate coverage report + # run: ansible-test coverage xml -v --requirements --group-by command --group-by version + # working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + # - uses: codecov/codecov-action@v2 + # with: + # fail_ci_if_error: false + +### +# Integration tests (RECOMMENDED) +# +# https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html + + +# If the application you are testing is available as a docker container and you want to test +# multiple versions see the following for an example: +# https://github.com/ansible-collections/community.zabbix/tree/master/.github/workflows + + integration: + runs-on: ubuntu-latest + name: I (Ⓐ${{ matrix.ansible }}+py${{ matrix.python }}) + strategy: + fail-fast: false + matrix: + ansible: + # - stable-2.9 # Only if your collection supports Ansible 2.9 + - stable-2.10 + - stable-2.11 + - stable-2.12 + - devel + python: + - '2.6' + - '2.7' + - '3.5' + - '3.6' + - '3.7' + - '3.8' + - '3.9' + - '3.10' + exclude: + # Because ansible-test doesn't support Python 3.9 for Ansible 2.9 + # and Python 3.10 is supported in 2.12 or later. + - ansible: stable-2.9 + python: '3.9' + - ansible: stable-2.9 + python: '3.10' + - ansible: stable-2.10 + python: '3.10' + - ansible: stable-2.11 + python: '3.10' + + + steps: + - name: Check out code + uses: actions/checkout@v2 + with: + path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + - name: Set up Python + uses: actions/setup-python@v2 + with: + # it is just required to run that once as "ansible-test integration" in the docker image + # will run on all python versions it supports. + python-version: 3.8 + + - name: Install ansible-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + # OPTIONAL If your integration test requires Python libraries or modules from other collections + # Install them like this + # - name: Install collection dependencies + # run: ansible-galaxy collection install ansible.netcommon -p . + + # Run the integration tests + - name: Run integration test + run: ansible-test integration -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker --coverage + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # ansible-test support producing code coverage date + - name: Generate coverage report + run: ansible-test coverage xml -v --requirements --group-by command --group-by version + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + - uses: codecov/codecov-action@v2 + with: + fail_ci_if_error: false From 5d4a555066357fa17d96fafb9095e724a2b29392 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:05:31 +0100 Subject: [PATCH 11/71] Make CodeQL run only once a day. --- .github/workflows/codeql-analysis.yaml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 38656d947..ae999a8e1 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -12,13 +12,8 @@ name: "CodeQL" on: - push: - branches: [ main, devel ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ main ] schedule: - - cron: '23 4 * * 3' + - cron: '0 0 * * *' jobs: analyze: From 2abafece71541d14bae51ee053301e8fdba4ecd8 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:25:08 +0100 Subject: [PATCH 12/71] Update release.yml. Add automatic changelog building. --- .github/workflows/release.yaml | 39 ++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5cebb3cc3..be7700550 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,12 +4,11 @@ on: # yamllint disable-line rule:truthy workflow_dispatch: jobs: - releaseanddeploy: + release: runs-on: ubuntu-latest - # if: github.repository == 'tribe29/ansible-checkmk' strategy: matrix: - python-version: [3.8] + python-version: [3.9] steps: - name: Checkout code @@ -29,20 +28,42 @@ jobs: python -m pip install --upgrade pip if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Compile Collection Changelog + run: antsibull-changelog release + + - name: Commit build changelogs + run: | + cd build/src + git config --local user.email "actions@github.com" + git config --local user.name "GitHub Actions" + git checkout -B changelogs-compile + git add -f changelogs/ CHANGELOG.rst + git commit -m "Add built front-end assets" + git push -f origin changelogs-compile + + - name: Merge changelogs back into main + uses: devmasx/merge-branch@master + with: + type: now + target_branch: main + github_token: ${{ github.token }} + + - name: Delete changelogs-compile branch + uses: dawidd6/action-delete-branch@v3 + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + branches: changelogs-compile + - name: Copy Files and Directories to Source run: | mkdir -p build/src cp $files build/src cp -rf $directories build/src env: - # files: "README.md LICENSE ansible.cfg galaxy.yml" - files: "README.md ansible.cfg galaxy.yml" + files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" # directories: "playbooks plugins roles vars" directories: "changelogs docs meta playbooks plugins" - - name: Compile Collection Changelog - run: cd build/src && antsibull-changelog release - - name: Build Ansible Collection run: ansible-galaxy collection build build/src --force @@ -84,5 +105,5 @@ jobs: asset_name: tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz asset_content_type: application/tar+gzip - # - name: Deploy Ansible collection to Galaxy + # - name: changelogs-compile Ansible collection to Galaxy # run: ansible-galaxy collection publish tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz --api-key ${{ secrets.GALAXY_API_KEY }} From f15840475c8a4e4f42f8b49dc034fed50126b385 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:25:24 +0100 Subject: [PATCH 13/71] Align tests ENV with release.yml. --- .github/workflows/integration.yaml | 2 +- .github/workflows/sanity.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 1fdecbdd4..d9156f05e 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -34,7 +34,7 @@ jobs: cp -rf $directories ansible_collections/$namespace/$collection/ env: # Keep in sync with release.yaml! - files: "README.md ansible.cfg galaxy.yml" + files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" directories: "changelogs docs meta playbooks plugins" namespace: "tribe29" collection: "checkmk" diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml index 5fdf95bac..f0a3ad64e 100644 --- a/.github/workflows/sanity.yaml +++ b/.github/workflows/sanity.yaml @@ -34,7 +34,7 @@ jobs: cp -rf $directories ansible_collections/$namespace/$collection/ env: # Keep in sync with release.yaml! - files: "README.md ansible.cfg galaxy.yml" + files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" directories: "changelogs docs meta playbooks plugins" namespace: "tribe29" collection: "checkmk" From 9deb8814f5dcf3bc0ef87d20fd88c99e4ba420e7 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:25:52 +0100 Subject: [PATCH 14/71] Add CHANGELOG.rst. --- CHANGELOG.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 CHANGELOG.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 000000000..5444ce479 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,24 @@ +============================= +tribe29.checkmk Release Notes +============================= + +.. contents:: Topics + + +v0.0.1 +====== + +Major Changes +------------- + +- Add activation module. +- Add discovery module. +- Add folder module. +- Add host module. +- Initial creation of collection structure and layout. + +Known Issues +------------ + +- Discovery is not feature complete yet. +- This release is still in development and a heavy work in progress. From 0b0afd28e9b0467267a6f7082d908a58c67ca49e Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:32:07 +0100 Subject: [PATCH 15/71] Bugfix and reorder release logic. --- .github/workflows/release.yaml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index be7700550..e835233f1 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -33,7 +33,6 @@ jobs: - name: Commit build changelogs run: | - cd build/src git config --local user.email "actions@github.com" git config --local user.name "GitHub Actions" git checkout -B changelogs-compile @@ -47,12 +46,6 @@ jobs: type: now target_branch: main github_token: ${{ github.token }} - - - name: Delete changelogs-compile branch - uses: dawidd6/action-delete-branch@v3 - with: - github_token: "${{ secrets.GITHUB_TOKEN }}" - branches: changelogs-compile - name: Copy Files and Directories to Source run: | @@ -107,3 +100,9 @@ jobs: # - name: changelogs-compile Ansible collection to Galaxy # run: ansible-galaxy collection publish tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz --api-key ${{ secrets.GALAXY_API_KEY }} + + - name: Delete changelogs-compile branch + uses: dawidd6/action-delete-branch@v3 + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + branches: changelogs-compile From 6e8908164e7912d660da301b4c359fd855e9adc0 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:49:33 +0100 Subject: [PATCH 16/71] Change destination branch. --- .github/workflows/release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e835233f1..590b777df 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,11 +40,11 @@ jobs: git commit -m "Add built front-end assets" git push -f origin changelogs-compile - - name: Merge changelogs back into main + - name: Merge changelogs back into devel uses: devmasx/merge-branch@master with: type: now - target_branch: main + target_branch: devel github_token: ${{ github.token }} - name: Copy Files and Directories to Source From 82eeab6de279023ad9601a654a3ba58575af41ad Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:51:07 +0100 Subject: [PATCH 17/71] Rename release action. --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 590b777df..e7e377845 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,5 +1,5 @@ --- -name: Release new Collection Version +name: Release Collection on: # yamllint disable-line rule:truthy workflow_dispatch: From ed1e76b6db7a612b2bc0e953cff8d74849de9cc9 Mon Sep 17 00:00:00 2001 From: Lars Getwan Date: Thu, 17 Mar 2022 11:57:36 +0100 Subject: [PATCH 18/71] Made sanity test 'import' happy even when using smelly Python 2. --- plugins/modules/folder.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/plugins/modules/folder.py b/plugins/modules/folder.py index c02864517..f6207173d 100644 --- a/plugins/modules/folder.py +++ b/plugins/modules/folder.py @@ -84,14 +84,19 @@ # https://docs.ansible.com/ansible/latest/dev_guide/testing/sanity/import.html from ansible.module_utils.basic import missing_required_lib import traceback -try: +import sys + +if sys.version[0] == '3': from pathlib import Path -except ImportError: - from pathlib2 import Path - HAS_ANOTHER_LIBRARY = False - ANOTHER_LIBRARY_IMPORT_ERROR = traceback.format_exc() else: - HAS_ANOTHER_LIBRARY = True + try: + from pathlib2 import Path + except ImportError: + HAS_PATHLIB2_LIBRARY = False + PATHLIB2_LIBRARY_IMPORT_ERROR = traceback.format_exc() + else: + HAS_PATHLIB2_LIBRARY = True + import json @@ -225,6 +230,14 @@ def run_module(): module = AnsibleModule(argument_spec=module_args, supports_check_mode=False) + # Handle library import error according to the following link: + # https://docs.ansible.com/ansible/latest/dev_guide/testing/sanity/import.html + if not HAS_PATHLIB2_LIBRARY: + # Needs: from ansible.module_utils.basic import missing_required_lib + module.fail_json( + msg=missing_required_lib('pathlib2'), + exception=PATHLIB2_LIBRARY_IMPORT_ERROR) + # Use the parameters to initialize some common variables headers = { "Accept": "application/json", @@ -254,14 +267,6 @@ def run_module(): etag, ) = get_current_folder_state(module, base_url, headers) - # Handle library import error according to the following link: - # https://docs.ansible.com/ansible/latest/dev_guide/testing/sanity/import.html - if not HAS_ANOTHER_LIBRARY: - # Needs: from ansible.module_utils.basic import missing_required_lib - module.fail_json( - msg=missing_required_lib('pathlib2'), - exception=ANOTHER_LIBRARY_IMPORT_ERROR) - # Handle the folder accordingly to above findings and desired state if state == "present" and current_state == "present": headers["If-Match"] = etag From d5d0bc1a116886eb3eab332bcf631f108ffc7776 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 12:07:32 +0100 Subject: [PATCH 19/71] Change logic to a pull request. Automatic merging or force pushing does not work on protected branches. --- .github/workflows/release.yaml | 43 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e7e377845..3a40b5f18 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -30,22 +30,29 @@ jobs: - name: Compile Collection Changelog run: antsibull-changelog release - - - name: Commit build changelogs - run: | - git config --local user.email "actions@github.com" - git config --local user.name "GitHub Actions" - git checkout -B changelogs-compile - git add -f changelogs/ CHANGELOG.rst - git commit -m "Add built front-end assets" - git push -f origin changelogs-compile - - - name: Merge changelogs back into devel - uses: devmasx/merge-branch@master + + - name: Create Pull Request for docs + uses: peter-evans/create-pull-request@v3 with: - type: now - target_branch: devel - github_token: ${{ github.token }} + commit-message: Update Changelogs + committer: GitHub + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + signoff: false + branch: changelogs-compile + delete-branch: true + title: '[Auto] Update changelogs' + body: | + Update changelogs report + - Changelogs updated during *today's* release + - Auto-generated by [create-pull-request][1] + + [1]: https://github.com/peter-evans/create-pull-request + assignees: robin-tribe29 + reviewers: robin-tribe29 + team-reviewers: | + owners + maintainers + draft: false - name: Copy Files and Directories to Source run: | @@ -100,9 +107,3 @@ jobs: # - name: changelogs-compile Ansible collection to Galaxy # run: ansible-galaxy collection publish tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz --api-key ${{ secrets.GALAXY_API_KEY }} - - - name: Delete changelogs-compile branch - uses: dawidd6/action-delete-branch@v3 - with: - github_token: "${{ secrets.GITHUB_TOKEN }}" - branches: changelogs-compile From 9f8f124db0355cf0a851378c2113f755ed718b28 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 12:14:24 +0100 Subject: [PATCH 20/71] Bump collection version for testing. --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index 8bfbeb4ed..2181b3b1c 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -9,7 +9,7 @@ namespace: tribe29 name: checkmk # The version of the collection. Must be compatible with semantic versioning -version: 0.0.1 +version: 0.0.2 # The path to the Markdown (.md) readme file. This path is relative to the root of the collection readme: README.md From b93b5d31e777bc8771ca397ca171f418de3903fd Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 12:14:56 +0100 Subject: [PATCH 21/71] Clean up action. --- .github/workflows/release.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3a40b5f18..5d89c1280 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,9 +6,6 @@ on: # yamllint disable-line rule:truthy jobs: release: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.9] steps: - name: Checkout code @@ -18,10 +15,10 @@ jobs: id: current_version run: echo "::set-output name=version::$(grep 'version:' galaxy.yml | cut -d ' ' -f 2)" - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python. uses: actions/setup-python@v2 with: - python-version: ${{ matrix.python-version }} + python-version: 3.9 - name: Install Dependencies run: | From 3e351a6df14988a4bf5cef08509426650d7c78b1 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 12:41:06 +0100 Subject: [PATCH 22/71] Cleanup changelogs and release action. --- .github/workflows/release.yaml | 14 +++------ changelogs/changelog.yaml | 30 +++++++++++++++++++ .../fragments/{initial.yml => v0.0.2.yml} | 7 ++--- 3 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 changelogs/changelog.yaml rename changelogs/fragments/{initial.yml => v0.0.2.yml} (93%) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5d89c1280..aff6747de 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -27,7 +27,8 @@ jobs: - name: Compile Collection Changelog run: antsibull-changelog release - + + # https://github.com/marketplace/actions/create-pull-request - name: Create Pull Request for docs uses: peter-evans/create-pull-request@v3 with: @@ -35,20 +36,13 @@ jobs: committer: GitHub author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> signoff: false - branch: changelogs-compile + branch: changelogs-update delete-branch: true title: '[Auto] Update changelogs' body: | - Update changelogs report - - Changelogs updated during *today's* release - - Auto-generated by [create-pull-request][1] - - [1]: https://github.com/peter-evans/create-pull-request + Changelogs updated during *${{ steps.current_version.outputs.version }}* release. assignees: robin-tribe29 reviewers: robin-tribe29 - team-reviewers: | - owners - maintainers draft: false - name: Copy Files and Directories to Source diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml new file mode 100644 index 000000000..484c7e49d --- /dev/null +++ b/changelogs/changelog.yaml @@ -0,0 +1,30 @@ +ancestor: null +releases: + 0.0.1: + changes: + known_issues: + - Activation is not site aware yet. All sites will be activated. + - Discovery is not feature complete yet. + - This release is still in development and a heavy work in progress. + major_changes: + - Add activation module. + - Add discovery module. + - Add folder module. + - Add host module. + - Initial creation of collection structure and layout. + fragments: + - initial.yml + modules: + - description: Activate changes in Checkmk. + name: activation + namespace: '' + - description: discovery services in Checkmk. + name: discovery + namespace: '' + - description: Manage folders in Checkmk. + name: folder + namespace: '' + - description: Manage hosts in Checkmk. + name: host + namespace: '' + release_date: '2022-02-14' diff --git a/changelogs/fragments/initial.yml b/changelogs/fragments/v0.0.2.yml similarity index 93% rename from changelogs/fragments/initial.yml rename to changelogs/fragments/v0.0.2.yml index 6bc7a9a96..b4e3d5f74 100644 --- a/changelogs/fragments/initial.yml +++ b/changelogs/fragments/v0.0.2.yml @@ -1,10 +1,7 @@ # https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to major_changes: - - Initial creation of collection structure and layout. - - Add activation module. - - Add discovery module. - - Add folder module. - - Add host module. + - Major overhaul of folder module. + - Major overhaul of host module. known_issues: - This release is still in development and a heavy work in progress. From 13b731aed9a98fa25e453a22142243771e51d85d Mon Sep 17 00:00:00 2001 From: robin-tribe29 Date: Thu, 17 Mar 2022 11:43:00 +0000 Subject: [PATCH 23/71] Update Changelogs --- CHANGELOG.rst | 25 +++++++++++++ changelogs/.plugin-cache.yaml | 37 +++++++++++++++++++ .../{fragments => archive/0.0.2}/v0.0.2.yml | 0 changelogs/changelog.yaml | 12 ++++++ 4 files changed, 74 insertions(+) create mode 100644 changelogs/.plugin-cache.yaml rename changelogs/{fragments => archive/0.0.2}/v0.0.2.yml (100%) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5444ce479..b8b386a3e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,22 @@ tribe29.checkmk Release Notes .. contents:: Topics +v0.0.2 +====== + +Major Changes +------------- + +- Major overhaul of folder module. +- Major overhaul of host module. + +Known Issues +------------ + +- Activation is not site aware yet. All sites will be activated. +- Discovery is not feature complete yet. +- This release is still in development and a heavy work in progress. + v0.0.1 ====== @@ -20,5 +36,14 @@ Major Changes Known Issues ------------ +- Activation is not site aware yet. All sites will be activated. - Discovery is not feature complete yet. - This release is still in development and a heavy work in progress. + +New Modules +----------- + +- tribe29.checkmk.activation - Activate changes in Checkmk. +- tribe29.checkmk.discovery - discovery services in Checkmk. +- tribe29.checkmk.folder - Manage folders in Checkmk. +- tribe29.checkmk.host - Manage hosts in Checkmk. diff --git a/changelogs/.plugin-cache.yaml b/changelogs/.plugin-cache.yaml new file mode 100644 index 000000000..2e416df3c --- /dev/null +++ b/changelogs/.plugin-cache.yaml @@ -0,0 +1,37 @@ +objects: + role: {} +plugins: + become: {} + cache: {} + callback: {} + cliconf: {} + connection: {} + httpapi: {} + inventory: {} + lookup: {} + module: + activation: + description: Activate changes in Checkmk. + name: activation + namespace: '' + version_added: 0.0.1 + discovery: + description: Discover services in Checkmk. + name: discovery + namespace: '' + version_added: 0.0.1 + folder: + description: Manage folders in Checkmk. + name: folder + namespace: '' + version_added: 0.0.1 + host: + description: Manage hosts in Checkmk. + name: host + namespace: '' + version_added: 0.0.1 + netconf: {} + shell: {} + strategy: {} + vars: {} +version: 0.0.2 diff --git a/changelogs/fragments/v0.0.2.yml b/changelogs/archive/0.0.2/v0.0.2.yml similarity index 100% rename from changelogs/fragments/v0.0.2.yml rename to changelogs/archive/0.0.2/v0.0.2.yml diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 484c7e49d..35144dc8a 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -28,3 +28,15 @@ releases: name: host namespace: '' release_date: '2022-02-14' + 0.0.2: + changes: + known_issues: + - Activation is not site aware yet. All sites will be activated. + - Discovery is not feature complete yet. + - This release is still in development and a heavy work in progress. + major_changes: + - Major overhaul of folder module. + - Major overhaul of host module. + fragments: + - v0.0.2.yml + release_date: '2022-03-17' From 1fd06d589f39ffa50a2ad80b129d93b3c2ff23db Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 12:59:17 +0100 Subject: [PATCH 24/71] Remove single tests in favor of ansible-test.yml --- .github/workflows/ansible-test.yaml | 35 ++++++++++----------- .github/workflows/integration.yaml | 46 --------------------------- .github/workflows/sanity.yaml | 49 ----------------------------- 3 files changed, 17 insertions(+), 113 deletions(-) delete mode 100644 .github/workflows/integration.yaml delete mode 100644 .github/workflows/sanity.yaml diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index 3551364e5..5a7d9dc0f 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -5,9 +5,8 @@ # 3. If your collection depends on other collections ensure they are installed, see "Install collection dependencies" # If you need help please ask in #ansible-community on the Libera.chat IRC network -name: CI +name: Ansible Tests on: - # Run CI against all pushes (direct commits, also merged PRs), Pull Requests push: branches: - main @@ -64,15 +63,15 @@ jobs: run: ansible-test sanity --docker -v --color --coverage working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - # ansible-test support producing code coverage date - - name: Generate coverage report - run: ansible-test coverage xml -v --requirements --group-by command --group-by version - working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + # # ansible-test support producing code coverage date + # - name: Generate coverage report + # run: ansible-test coverage xml -v --requirements --group-by command --group-by version + # working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME - - uses: codecov/codecov-action@v2 - with: - fail_ci_if_error: false + # # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + # - uses: codecov/codecov-action@v2 + # with: + # fail_ci_if_error: false ### # Unit tests (OPTIONAL) @@ -199,12 +198,12 @@ jobs: run: ansible-test integration -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker --coverage working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - # ansible-test support producing code coverage date - - name: Generate coverage report - run: ansible-test coverage xml -v --requirements --group-by command --group-by version - working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + # # ansible-test support producing code coverage date + # - name: Generate coverage report + # run: ansible-test coverage xml -v --requirements --group-by command --group-by version + # working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME - - uses: codecov/codecov-action@v2 - with: - fail_ci_if_error: false + # # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + # - uses: codecov/codecov-action@v2 + # with: + # fail_ci_if_error: false diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml deleted file mode 100644 index d9156f05e..000000000 --- a/.github/workflows/integration.yaml +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Ansible Collection Full Integration Test -on: # yamllint disable-line rule:truthy - push: - branches: - - 'main' - - 'devel' - -jobs: - ansible-test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['2.6', '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10'] - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install Dependencies. - run: | - python -m pip install --upgrade pip - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - - name: Copy Files and Directories to Source - run: | - mkdir -p ansible_collections/$namespace/$collection/ - cp $files ansible_collections/$namespace/$collection/ - cp -rf $directories ansible_collections/$namespace/$collection/ - env: - # Keep in sync with release.yaml! - files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" - directories: "changelogs docs meta playbooks plugins" - namespace: "tribe29" - collection: "checkmk" - - - name: Run Integration Checks. - run: cd ansible_collections/$namespace/$collection/ && ansible-test integration full - env: - namespace: "tribe29" - collection: "checkmk" diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml deleted file mode 100644 index 741c4e6c5..000000000 --- a/.github/workflows/sanity.yaml +++ /dev/null @@ -1,49 +0,0 @@ ---- -# Interesting? https://github.com/marketplace/actions/ansible-test -name: Ansible Collection Sanity Tests -on: # yamllint disable-line rule:truthy - push: - branches: - - 'main' - - 'devel' - -jobs: - ansible-test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.9] - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install Dependencies. - run: | - python -m pip install --upgrade pip - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - - name: Copy Files and Directories to Source - run: | - mkdir -p ansible_collections/$namespace/$collection/ - cp $files ansible_collections/$namespace/$collection/ - cp -rf $directories ansible_collections/$namespace/$collection/ - env: - # Keep in sync with release.yaml! - files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" - directories: "changelogs docs meta playbooks plugins" - namespace: "tribe29" - collection: "checkmk" - - - name: Run Sanity Checks. - run: cd ansible_collections/$namespace/$collection/ && ansible-test sanity - env: - namespace: "tribe29" - collection: "checkmk" - -# ToDo: Automatically raise issue? From 08f8bcd99c5874bb13668c15d391b85b37503fcc Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:02:02 +0100 Subject: [PATCH 25/71] Update requirements.txt. --- requirements.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 73ae5ae19..7cc4172cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -ansible >= 5.1.0 -antsibull-changelog >= 0.12.0 -pathlib2 \ No newline at end of file +ansible >= 4.10.0 +antsibull-changelog >= 0.12.0 \ No newline at end of file From 9928d60160d02b8ee9799e954a455cd859fe6561 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:13:54 +0100 Subject: [PATCH 26/71] Reduce testing scope for now. --- .github/workflows/ansible-test.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index 5a7d9dc0f..70c242bb5 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -30,8 +30,8 @@ jobs: # It's important that Sanity is tested against all stable-X.Y branches # Testing against `devel` may fail as new tests are added. # - stable-2.9 # Only if your collection supports Ansible 2.9 - - stable-2.10 - - stable-2.11 + # - stable-2.10 + # - stable-2.11 - stable-2.12 - devel runs-on: ubuntu-latest @@ -146,16 +146,16 @@ jobs: matrix: ansible: # - stable-2.9 # Only if your collection supports Ansible 2.9 - - stable-2.10 - - stable-2.11 + # - stable-2.10 + # - stable-2.11 - stable-2.12 - devel python: - - '2.6' - - '2.7' - - '3.5' - - '3.6' - - '3.7' + # - '2.6' + # - '2.7' + # - '3.5' + # - '3.6' + # - '3.7' - '3.8' - '3.9' - '3.10' From f0d7ec45a37e544142b9a9319a2d9e589510e919 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:14:08 +0100 Subject: [PATCH 27/71] Fix missing boilerplate. --- plugins/doc_fragments/common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/doc_fragments/common.py b/plugins/doc_fragments/common.py index d98c19f99..bdef1c4ad 100644 --- a/plugins/doc_fragments/common.py +++ b/plugins/doc_fragments/common.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + class ModuleDocFragment(object): DOCUMENTATION = r''' options: From 42e51318965561f278aac69cd39ae37370a42cb1 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:28:21 +0100 Subject: [PATCH 28/71] Make integration test more resilient. --- tests/integration/targets/full/tasks/prep.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/integration/targets/full/tasks/prep.yml b/tests/integration/targets/full/tasks/prep.yml index 802f81517..3029a5d2a 100644 --- a/tests/integration/targets/full/tasks/prep.yml +++ b/tests/integration/targets/full/tasks/prep.yml @@ -13,8 +13,6 @@ ansible.builtin.command: "omd create --no-tmpfs --admin-password {{ automation_secret }} {{ site }}" args: creates: "/omd/sites/{{ site }}" - register: omd_state_create - name: "Start site." - ansible.builtin.command: "omd start {{ site }}" - register: omd_state_start + ansible.builtin.shell: "omd status -b {{ site }} || omd start {{ site }}" From 70dbd076d94b6cfe735501250ccba618d3a6c687 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:59:36 +0100 Subject: [PATCH 29/71] Update meta data. --- galaxy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/galaxy.yml b/galaxy.yml index 8bfbeb4ed..f5909fbdf 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -9,7 +9,7 @@ namespace: tribe29 name: checkmk # The version of the collection. Must be compatible with semantic versioning -version: 0.0.1 +version: 0.0.2 # The path to the Markdown (.md) readme file. This path is relative to the root of the collection readme: README.md @@ -49,7 +49,7 @@ tags: [tribe29, checkmk, monitoring] repository: https://github.com/tribe29/ansible-collection-tribe29.checkmk # The URL to any online docs -documentation: https://github.com/tribe29/ansible-collection-tribe29.checkmk/tree/main/docs +documentation: https://github.com/tribe29/ansible-collection-tribe29.checkmk/blob/main/README.md # The URL to the homepage of the collection/project homepage: https://github.com/tribe29/ansible-collection-tribe29.checkmk From febe1a815bc768895184ed890b445e539402cec0 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:59:57 +0100 Subject: [PATCH 30/71] Broaden sanity tests scope. --- .github/workflows/ansible-test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index 70c242bb5..f9814339e 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -30,8 +30,8 @@ jobs: # It's important that Sanity is tested against all stable-X.Y branches # Testing against `devel` may fail as new tests are added. # - stable-2.9 # Only if your collection supports Ansible 2.9 - # - stable-2.10 - # - stable-2.11 + - stable-2.10 + - stable-2.11 - stable-2.12 - devel runs-on: ubuntu-latest From 15d2048610188dd86e78b4bfb9c6d25a521c0e1c Mon Sep 17 00:00:00 2001 From: Lars Getwan Date: Thu, 17 Mar 2022 14:26:39 +0100 Subject: [PATCH 31/71] Fixed the module import for pathlib/pathlib2. --- plugins/modules/folder.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/modules/folder.py b/plugins/modules/folder.py index f6207173d..9c819a80f 100644 --- a/plugins/modules/folder.py +++ b/plugins/modules/folder.py @@ -88,7 +88,9 @@ if sys.version[0] == '3': from pathlib import Path + PYTHON_VERSION = 3 else: + PYTHON_VERSION = 2 try: from pathlib2 import Path except ImportError: @@ -232,7 +234,7 @@ def run_module(): # Handle library import error according to the following link: # https://docs.ansible.com/ansible/latest/dev_guide/testing/sanity/import.html - if not HAS_PATHLIB2_LIBRARY: + if PYTHON_VERSION == 2 and not HAS_PATHLIB2_LIBRARY: # Needs: from ansible.module_utils.basic import missing_required_lib module.fail_json( msg=missing_required_lib('pathlib2'), From 788e6836688b67d6a4bcb9cfc7b0727ef488c227 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 14:36:14 +0100 Subject: [PATCH 32/71] Add .github config and security policy. --- .github/ISSUE_TEMPLATE/config.yaml | 13 +++++++++++++ SECURITY.md | 15 +++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yaml create mode 100644 SECURITY.md diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 000000000..24de52c5a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1,13 @@ +--- +# https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser +blank_issues_enabled: true +contact_links: +# - name: Security bug report +# url: +# about: +- name: Ansible Code of Conduct + url: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_template_chooser_ansible_collections + about: Be nice to other members of the community. +- name: Talk to our Checkmk community. + url: https://forum.checkmk.com/ + about: Join our Checkmk community to aks and answer questions diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..abc869ba9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +Please always use the latest available version! +Versions in here are used solely for stability, so your Ansible configuration +does not break. +However, we cannot and will not support older versions, especially security-wise. +If you find a vulnerability, please report it as stated below, +and we will do our best to fix it in the next release. + +## Reporting a Vulnerability + +For now, please open a normal [issue](https://github.com/tribe29/ansible-collection-tribe29.checkmk/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) in the repository but try to disclose the +vulnerability as reasonable as possible. \ No newline at end of file From a5a38a718d95921220dc8c0918b3d3e0dd24d629 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 14:39:51 +0100 Subject: [PATCH 33/71] Add community link. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 20485adf2..09bbecd00 100644 --- a/README.md +++ b/README.md @@ -113,8 +113,9 @@ if you list the `tribe29.checkmk` collection in the playbook's [`collections`](h ``` ### See Also: -* [Checkmk Documentation](https://docs.checkmk.com/) * [Checkmk Website](https://checkmk.com) +* [Checkmk Documentation](https://docs.checkmk.com/) +* [Checkmk Community](https://forum.checkmk.com/) * [tribe29 - the checkmk company](https://tribe29.com) ## Contributing to this collection From 953f90704a8d9b3856bbe0cf90d35c34375f5d30 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 15:01:23 +0100 Subject: [PATCH 34/71] Lower minimum required Ansible version. --- meta/runtime.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta/runtime.yml b/meta/runtime.yml index 37621e652..83ba62e83 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,4 +1,4 @@ -requires_ansible: '>=2.12.1' +requires_ansible: '>=2.10.1' action_groups: checkmk: From 5ef759f41f30859023bb3c21631d2890f2b9af10 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:05:16 +0100 Subject: [PATCH 35/71] Add ansible-test workflow. Taken from the ansible-collections project itself: https://github.com/ansible-collections/collection_template/blob/main/.github/workflows/ansible-test.yml --- .github/workflows/ansible-test.yaml | 210 ++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 .github/workflows/ansible-test.yaml diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml new file mode 100644 index 000000000..3551364e5 --- /dev/null +++ b/.github/workflows/ansible-test.yaml @@ -0,0 +1,210 @@ +# https://github.com/ansible-collections/collection_template/blob/main/.github/workflows/ansible-test.yml +# README FIRST +# 1. replace "NAMESPACE" and "COLLECTION_NAME" with the correct name in the env section (e.g. with 'community' and 'mycollection') +# 2. If you don't have unit tests remove that section +# 3. If your collection depends on other collections ensure they are installed, see "Install collection dependencies" +# If you need help please ask in #ansible-community on the Libera.chat IRC network + +name: CI +on: + # Run CI against all pushes (direct commits, also merged PRs), Pull Requests + push: + branches: + - main + - devel +env: + NAMESPACE: tribe29 + COLLECTION_NAME: checkmk + +jobs: + +### +# Sanity tests (REQUIRED) +# +# https://docs.ansible.com/ansible/latest/dev_guide/testing_sanity.html + + sanity: + name: Sanity (Ⓐ${{ matrix.ansible }}) + strategy: + matrix: + ansible: + # It's important that Sanity is tested against all stable-X.Y branches + # Testing against `devel` may fail as new tests are added. + # - stable-2.9 # Only if your collection supports Ansible 2.9 + - stable-2.10 + - stable-2.11 + - stable-2.12 + - devel + runs-on: ubuntu-latest + steps: + + # ansible-test requires the collection to be in a directory in the form + # .../ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}}/ + + - name: Check out code + uses: actions/checkout@v2 + with: + path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + - name: Set up Python + uses: actions/setup-python@v2 + with: + # it is just required to run that once as "ansible-test sanity" in the docker image + # will run on all python versions it supports. + python-version: 3.8 + + # Install the head of the given branch (devel, stable-2.10) + - name: Install ansible-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + # run ansible-test sanity inside of Docker. + # The docker container has all the pinned dependencies that are required + # and all python versions ansible supports. + - name: Run sanity tests + run: ansible-test sanity --docker -v --color --coverage + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # ansible-test support producing code coverage date + - name: Generate coverage report + run: ansible-test coverage xml -v --requirements --group-by command --group-by version + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + - uses: codecov/codecov-action@v2 + with: + fail_ci_if_error: false + +### +# Unit tests (OPTIONAL) +# +# https://docs.ansible.com/ansible/latest/dev_guide/testing_units.html + + # units: + # runs-on: ubuntu-latest + # name: Units (Ⓐ${{ matrix.ansible }}) + # strategy: + # # As soon as the first unit test fails, cancel the others to free up the CI queue + # fail-fast: true + # matrix: + # ansible: + # # - stable-2.9 # Only if your collection supports Ansible 2.9 + # - stable-2.10 + # - stable-2.11 + # - stable-2.12 + # - devel + + # steps: + # - name: Check out code + # uses: actions/checkout@v2 + # with: + # path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # - name: Set up Python + # uses: actions/setup-python@v2 + # with: + # # it is just required to run that once as "ansible-test units" in the docker image + # # will run on all python versions it supports. + # python-version: 3.8 + + # - name: Install ansible-base (${{ matrix.ansible }}) + # run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + # # OPTIONAL If your unit test requires Python libraries from other collections + # # Install them like this + # - name: Install collection dependencies + # run: ansible-galaxy collection install ansible.netcommon ansible.utils -p . + + # # Run the unit tests + # - name: Run unit test + # run: ansible-test units -v --color --docker --coverage + # working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # # ansible-test support producing code coverage date + # - name: Generate coverage report + # run: ansible-test coverage xml -v --requirements --group-by command --group-by version + # working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + # - uses: codecov/codecov-action@v2 + # with: + # fail_ci_if_error: false + +### +# Integration tests (RECOMMENDED) +# +# https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html + + +# If the application you are testing is available as a docker container and you want to test +# multiple versions see the following for an example: +# https://github.com/ansible-collections/community.zabbix/tree/master/.github/workflows + + integration: + runs-on: ubuntu-latest + name: I (Ⓐ${{ matrix.ansible }}+py${{ matrix.python }}) + strategy: + fail-fast: false + matrix: + ansible: + # - stable-2.9 # Only if your collection supports Ansible 2.9 + - stable-2.10 + - stable-2.11 + - stable-2.12 + - devel + python: + - '2.6' + - '2.7' + - '3.5' + - '3.6' + - '3.7' + - '3.8' + - '3.9' + - '3.10' + exclude: + # Because ansible-test doesn't support Python 3.9 for Ansible 2.9 + # and Python 3.10 is supported in 2.12 or later. + - ansible: stable-2.9 + python: '3.9' + - ansible: stable-2.9 + python: '3.10' + - ansible: stable-2.10 + python: '3.10' + - ansible: stable-2.11 + python: '3.10' + + + steps: + - name: Check out code + uses: actions/checkout@v2 + with: + path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + - name: Set up Python + uses: actions/setup-python@v2 + with: + # it is just required to run that once as "ansible-test integration" in the docker image + # will run on all python versions it supports. + python-version: 3.8 + + - name: Install ansible-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + # OPTIONAL If your integration test requires Python libraries or modules from other collections + # Install them like this + # - name: Install collection dependencies + # run: ansible-galaxy collection install ansible.netcommon -p . + + # Run the integration tests + - name: Run integration test + run: ansible-test integration -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker --coverage + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # ansible-test support producing code coverage date + - name: Generate coverage report + run: ansible-test coverage xml -v --requirements --group-by command --group-by version + working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + + # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + - uses: codecov/codecov-action@v2 + with: + fail_ci_if_error: false From 78ddaefe5ce11d2b90316525026e2a43a70df382 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:05:31 +0100 Subject: [PATCH 36/71] Make CodeQL run only once a day. --- .github/workflows/codeql-analysis.yaml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 38656d947..ae999a8e1 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -12,13 +12,8 @@ name: "CodeQL" on: - push: - branches: [ main, devel ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ main ] schedule: - - cron: '23 4 * * 3' + - cron: '0 0 * * *' jobs: analyze: From c1a1d39bb3fa2bcbd6cddc4ebbfeb6b29c9bf566 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:25:08 +0100 Subject: [PATCH 37/71] Update release.yml. Add automatic changelog building. --- .github/workflows/release.yaml | 36 +++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b86db93a7..be7700550 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,7 +4,7 @@ on: # yamllint disable-line rule:truthy workflow_dispatch: jobs: - releaseanddeploy: + release: runs-on: ubuntu-latest strategy: matrix: @@ -28,20 +28,42 @@ jobs: python -m pip install --upgrade pip if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Compile Collection Changelog + run: antsibull-changelog release + + - name: Commit build changelogs + run: | + cd build/src + git config --local user.email "actions@github.com" + git config --local user.name "GitHub Actions" + git checkout -B changelogs-compile + git add -f changelogs/ CHANGELOG.rst + git commit -m "Add built front-end assets" + git push -f origin changelogs-compile + + - name: Merge changelogs back into main + uses: devmasx/merge-branch@master + with: + type: now + target_branch: main + github_token: ${{ github.token }} + + - name: Delete changelogs-compile branch + uses: dawidd6/action-delete-branch@v3 + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + branches: changelogs-compile + - name: Copy Files and Directories to Source run: | mkdir -p build/src cp $files build/src cp -rf $directories build/src env: - # files: "README.md LICENSE ansible.cfg galaxy.yml" - files: "README.md ansible.cfg galaxy.yml" + files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" # directories: "playbooks plugins roles vars" directories: "changelogs docs meta playbooks plugins" - - name: Compile Collection Changelog - run: cd build/src && antsibull-changelog release - - name: Build Ansible Collection run: ansible-galaxy collection build build/src --force @@ -83,5 +105,5 @@ jobs: asset_name: tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz asset_content_type: application/tar+gzip - # - name: Deploy Ansible collection to Galaxy + # - name: changelogs-compile Ansible collection to Galaxy # run: ansible-galaxy collection publish tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz --api-key ${{ secrets.GALAXY_API_KEY }} From 9bd57838df72e35627ce7760d17c9608c0d579c3 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:25:24 +0100 Subject: [PATCH 38/71] Align tests ENV with release.yml. --- .github/workflows/integration.yaml | 2 +- .github/workflows/sanity.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 1fdecbdd4..d9156f05e 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -34,7 +34,7 @@ jobs: cp -rf $directories ansible_collections/$namespace/$collection/ env: # Keep in sync with release.yaml! - files: "README.md ansible.cfg galaxy.yml" + files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" directories: "changelogs docs meta playbooks plugins" namespace: "tribe29" collection: "checkmk" diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml index 39fd0c71a..741c4e6c5 100644 --- a/.github/workflows/sanity.yaml +++ b/.github/workflows/sanity.yaml @@ -35,7 +35,7 @@ jobs: cp -rf $directories ansible_collections/$namespace/$collection/ env: # Keep in sync with release.yaml! - files: "README.md ansible.cfg galaxy.yml" + files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" directories: "changelogs docs meta playbooks plugins" namespace: "tribe29" collection: "checkmk" From e135cb94080576cc457990c1a310adb1abcfa276 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:25:52 +0100 Subject: [PATCH 39/71] Add CHANGELOG.rst. --- CHANGELOG.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 CHANGELOG.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 000000000..5444ce479 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,24 @@ +============================= +tribe29.checkmk Release Notes +============================= + +.. contents:: Topics + + +v0.0.1 +====== + +Major Changes +------------- + +- Add activation module. +- Add discovery module. +- Add folder module. +- Add host module. +- Initial creation of collection structure and layout. + +Known Issues +------------ + +- Discovery is not feature complete yet. +- This release is still in development and a heavy work in progress. From f786d10e5a88bb6b5a7bf24782309ee029e323f5 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 12:59:17 +0100 Subject: [PATCH 40/71] Remove single tests in favor of ansible-test.yml --- .github/workflows/ansible-test.yaml | 35 ++++++++++----------- .github/workflows/integration.yaml | 46 --------------------------- .github/workflows/sanity.yaml | 49 ----------------------------- 3 files changed, 17 insertions(+), 113 deletions(-) delete mode 100644 .github/workflows/integration.yaml delete mode 100644 .github/workflows/sanity.yaml diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index 3551364e5..5a7d9dc0f 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -5,9 +5,8 @@ # 3. If your collection depends on other collections ensure they are installed, see "Install collection dependencies" # If you need help please ask in #ansible-community on the Libera.chat IRC network -name: CI +name: Ansible Tests on: - # Run CI against all pushes (direct commits, also merged PRs), Pull Requests push: branches: - main @@ -64,15 +63,15 @@ jobs: run: ansible-test sanity --docker -v --color --coverage working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - # ansible-test support producing code coverage date - - name: Generate coverage report - run: ansible-test coverage xml -v --requirements --group-by command --group-by version - working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + # # ansible-test support producing code coverage date + # - name: Generate coverage report + # run: ansible-test coverage xml -v --requirements --group-by command --group-by version + # working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME - - uses: codecov/codecov-action@v2 - with: - fail_ci_if_error: false + # # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + # - uses: codecov/codecov-action@v2 + # with: + # fail_ci_if_error: false ### # Unit tests (OPTIONAL) @@ -199,12 +198,12 @@ jobs: run: ansible-test integration -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker --coverage working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - # ansible-test support producing code coverage date - - name: Generate coverage report - run: ansible-test coverage xml -v --requirements --group-by command --group-by version - working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} + # # ansible-test support producing code coverage date + # - name: Generate coverage report + # run: ansible-test coverage xml -v --requirements --group-by command --group-by version + # working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}} - # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME - - uses: codecov/codecov-action@v2 - with: - fail_ci_if_error: false + # # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME + # - uses: codecov/codecov-action@v2 + # with: + # fail_ci_if_error: false diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml deleted file mode 100644 index d9156f05e..000000000 --- a/.github/workflows/integration.yaml +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Ansible Collection Full Integration Test -on: # yamllint disable-line rule:truthy - push: - branches: - - 'main' - - 'devel' - -jobs: - ansible-test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['2.6', '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10'] - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install Dependencies. - run: | - python -m pip install --upgrade pip - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - - name: Copy Files and Directories to Source - run: | - mkdir -p ansible_collections/$namespace/$collection/ - cp $files ansible_collections/$namespace/$collection/ - cp -rf $directories ansible_collections/$namespace/$collection/ - env: - # Keep in sync with release.yaml! - files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" - directories: "changelogs docs meta playbooks plugins" - namespace: "tribe29" - collection: "checkmk" - - - name: Run Integration Checks. - run: cd ansible_collections/$namespace/$collection/ && ansible-test integration full - env: - namespace: "tribe29" - collection: "checkmk" diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml deleted file mode 100644 index 741c4e6c5..000000000 --- a/.github/workflows/sanity.yaml +++ /dev/null @@ -1,49 +0,0 @@ ---- -# Interesting? https://github.com/marketplace/actions/ansible-test -name: Ansible Collection Sanity Tests -on: # yamllint disable-line rule:truthy - push: - branches: - - 'main' - - 'devel' - -jobs: - ansible-test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.9] - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install Dependencies. - run: | - python -m pip install --upgrade pip - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - - name: Copy Files and Directories to Source - run: | - mkdir -p ansible_collections/$namespace/$collection/ - cp $files ansible_collections/$namespace/$collection/ - cp -rf $directories ansible_collections/$namespace/$collection/ - env: - # Keep in sync with release.yaml! - files: "CHANGELOG.rst LICENSE README.md ansible.cfg galaxy.yml" - directories: "changelogs docs meta playbooks plugins" - namespace: "tribe29" - collection: "checkmk" - - - name: Run Sanity Checks. - run: cd ansible_collections/$namespace/$collection/ && ansible-test sanity - env: - namespace: "tribe29" - collection: "checkmk" - -# ToDo: Automatically raise issue? From b859fee8948053609fbeca7c08ae5506b6306c12 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:02:02 +0100 Subject: [PATCH 41/71] Update requirements.txt. --- requirements.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 73ae5ae19..7cc4172cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -ansible >= 5.1.0 -antsibull-changelog >= 0.12.0 -pathlib2 \ No newline at end of file +ansible >= 4.10.0 +antsibull-changelog >= 0.12.0 \ No newline at end of file From 274fa8d2a62f0e53938a61b2cec313ef8ec695da Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:13:54 +0100 Subject: [PATCH 42/71] Reduce testing scope for now. --- .github/workflows/ansible-test.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index 5a7d9dc0f..70c242bb5 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -30,8 +30,8 @@ jobs: # It's important that Sanity is tested against all stable-X.Y branches # Testing against `devel` may fail as new tests are added. # - stable-2.9 # Only if your collection supports Ansible 2.9 - - stable-2.10 - - stable-2.11 + # - stable-2.10 + # - stable-2.11 - stable-2.12 - devel runs-on: ubuntu-latest @@ -146,16 +146,16 @@ jobs: matrix: ansible: # - stable-2.9 # Only if your collection supports Ansible 2.9 - - stable-2.10 - - stable-2.11 + # - stable-2.10 + # - stable-2.11 - stable-2.12 - devel python: - - '2.6' - - '2.7' - - '3.5' - - '3.6' - - '3.7' + # - '2.6' + # - '2.7' + # - '3.5' + # - '3.6' + # - '3.7' - '3.8' - '3.9' - '3.10' From a38d809b494ff3bb1059b1479c45e07376a3eea0 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:14:08 +0100 Subject: [PATCH 43/71] Fix missing boilerplate. --- plugins/doc_fragments/common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/doc_fragments/common.py b/plugins/doc_fragments/common.py index d98c19f99..bdef1c4ad 100644 --- a/plugins/doc_fragments/common.py +++ b/plugins/doc_fragments/common.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + class ModuleDocFragment(object): DOCUMENTATION = r''' options: From 5bf4ae8df713d32c4f5eda87174deefea6c56886 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:28:21 +0100 Subject: [PATCH 44/71] Make integration test more resilient. --- tests/integration/targets/full/tasks/prep.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/integration/targets/full/tasks/prep.yml b/tests/integration/targets/full/tasks/prep.yml index 802f81517..3029a5d2a 100644 --- a/tests/integration/targets/full/tasks/prep.yml +++ b/tests/integration/targets/full/tasks/prep.yml @@ -13,8 +13,6 @@ ansible.builtin.command: "omd create --no-tmpfs --admin-password {{ automation_secret }} {{ site }}" args: creates: "/omd/sites/{{ site }}" - register: omd_state_create - name: "Start site." - ansible.builtin.command: "omd start {{ site }}" - register: omd_state_start + ansible.builtin.shell: "omd status -b {{ site }} || omd start {{ site }}" From c1c88ded1d984e8f45e1e4b7df677bf1668d407b Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:59:36 +0100 Subject: [PATCH 45/71] Update meta data. --- galaxy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/galaxy.yml b/galaxy.yml index 8bfbeb4ed..f5909fbdf 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -9,7 +9,7 @@ namespace: tribe29 name: checkmk # The version of the collection. Must be compatible with semantic versioning -version: 0.0.1 +version: 0.0.2 # The path to the Markdown (.md) readme file. This path is relative to the root of the collection readme: README.md @@ -49,7 +49,7 @@ tags: [tribe29, checkmk, monitoring] repository: https://github.com/tribe29/ansible-collection-tribe29.checkmk # The URL to any online docs -documentation: https://github.com/tribe29/ansible-collection-tribe29.checkmk/tree/main/docs +documentation: https://github.com/tribe29/ansible-collection-tribe29.checkmk/blob/main/README.md # The URL to the homepage of the collection/project homepage: https://github.com/tribe29/ansible-collection-tribe29.checkmk From 3c15a04e8ae452262bf23686f5de4be94b545099 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 13:59:57 +0100 Subject: [PATCH 46/71] Broaden sanity tests scope. --- .github/workflows/ansible-test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index 70c242bb5..f9814339e 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -30,8 +30,8 @@ jobs: # It's important that Sanity is tested against all stable-X.Y branches # Testing against `devel` may fail as new tests are added. # - stable-2.9 # Only if your collection supports Ansible 2.9 - # - stable-2.10 - # - stable-2.11 + - stable-2.10 + - stable-2.11 - stable-2.12 - devel runs-on: ubuntu-latest From dd8fe45f95306bfa01ae63116373fef78f77dd8f Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:32:07 +0100 Subject: [PATCH 47/71] Bugfix and reorder release logic. --- .github/workflows/release.yaml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index be7700550..e835233f1 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -33,7 +33,6 @@ jobs: - name: Commit build changelogs run: | - cd build/src git config --local user.email "actions@github.com" git config --local user.name "GitHub Actions" git checkout -B changelogs-compile @@ -47,12 +46,6 @@ jobs: type: now target_branch: main github_token: ${{ github.token }} - - - name: Delete changelogs-compile branch - uses: dawidd6/action-delete-branch@v3 - with: - github_token: "${{ secrets.GITHUB_TOKEN }}" - branches: changelogs-compile - name: Copy Files and Directories to Source run: | @@ -107,3 +100,9 @@ jobs: # - name: changelogs-compile Ansible collection to Galaxy # run: ansible-galaxy collection publish tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz --api-key ${{ secrets.GALAXY_API_KEY }} + + - name: Delete changelogs-compile branch + uses: dawidd6/action-delete-branch@v3 + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + branches: changelogs-compile From c9f068550729b987b6c192cf3991da3fa3d55440 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:49:33 +0100 Subject: [PATCH 48/71] Change destination branch. --- .github/workflows/release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e835233f1..590b777df 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,11 +40,11 @@ jobs: git commit -m "Add built front-end assets" git push -f origin changelogs-compile - - name: Merge changelogs back into main + - name: Merge changelogs back into devel uses: devmasx/merge-branch@master with: type: now - target_branch: main + target_branch: devel github_token: ${{ github.token }} - name: Copy Files and Directories to Source From 1faaf8cc9e8661548cbf9ae2a9dc225a62765491 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 11:51:07 +0100 Subject: [PATCH 49/71] Rename release action. --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 590b777df..e7e377845 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,5 +1,5 @@ --- -name: Release new Collection Version +name: Release Collection on: # yamllint disable-line rule:truthy workflow_dispatch: From 89d782cb4ab17d8ea28ae4f8b2a6c8e511b285cf Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 12:07:32 +0100 Subject: [PATCH 50/71] Change logic to a pull request. Automatic merging or force pushing does not work on protected branches. --- .github/workflows/release.yaml | 43 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e7e377845..3a40b5f18 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -30,22 +30,29 @@ jobs: - name: Compile Collection Changelog run: antsibull-changelog release - - - name: Commit build changelogs - run: | - git config --local user.email "actions@github.com" - git config --local user.name "GitHub Actions" - git checkout -B changelogs-compile - git add -f changelogs/ CHANGELOG.rst - git commit -m "Add built front-end assets" - git push -f origin changelogs-compile - - - name: Merge changelogs back into devel - uses: devmasx/merge-branch@master + + - name: Create Pull Request for docs + uses: peter-evans/create-pull-request@v3 with: - type: now - target_branch: devel - github_token: ${{ github.token }} + commit-message: Update Changelogs + committer: GitHub + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + signoff: false + branch: changelogs-compile + delete-branch: true + title: '[Auto] Update changelogs' + body: | + Update changelogs report + - Changelogs updated during *today's* release + - Auto-generated by [create-pull-request][1] + + [1]: https://github.com/peter-evans/create-pull-request + assignees: robin-tribe29 + reviewers: robin-tribe29 + team-reviewers: | + owners + maintainers + draft: false - name: Copy Files and Directories to Source run: | @@ -100,9 +107,3 @@ jobs: # - name: changelogs-compile Ansible collection to Galaxy # run: ansible-galaxy collection publish tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz --api-key ${{ secrets.GALAXY_API_KEY }} - - - name: Delete changelogs-compile branch - uses: dawidd6/action-delete-branch@v3 - with: - github_token: "${{ secrets.GITHUB_TOKEN }}" - branches: changelogs-compile From 26356b3379e43f7fed9abc41081d2a448dd4c0e2 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 12:14:56 +0100 Subject: [PATCH 51/71] Clean up action. --- .github/workflows/release.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3a40b5f18..5d89c1280 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,9 +6,6 @@ on: # yamllint disable-line rule:truthy jobs: release: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.9] steps: - name: Checkout code @@ -18,10 +15,10 @@ jobs: id: current_version run: echo "::set-output name=version::$(grep 'version:' galaxy.yml | cut -d ' ' -f 2)" - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python. uses: actions/setup-python@v2 with: - python-version: ${{ matrix.python-version }} + python-version: 3.9 - name: Install Dependencies run: | From 2d9f61c3b0ebc229171d11e0ffcde0c8b253d130 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 12:41:06 +0100 Subject: [PATCH 52/71] Cleanup changelogs and release action. --- .github/workflows/release.yaml | 14 +++------ changelogs/changelog.yaml | 30 +++++++++++++++++++ .../fragments/{initial.yml => v0.0.2.yml} | 7 ++--- 3 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 changelogs/changelog.yaml rename changelogs/fragments/{initial.yml => v0.0.2.yml} (93%) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5d89c1280..aff6747de 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -27,7 +27,8 @@ jobs: - name: Compile Collection Changelog run: antsibull-changelog release - + + # https://github.com/marketplace/actions/create-pull-request - name: Create Pull Request for docs uses: peter-evans/create-pull-request@v3 with: @@ -35,20 +36,13 @@ jobs: committer: GitHub author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> signoff: false - branch: changelogs-compile + branch: changelogs-update delete-branch: true title: '[Auto] Update changelogs' body: | - Update changelogs report - - Changelogs updated during *today's* release - - Auto-generated by [create-pull-request][1] - - [1]: https://github.com/peter-evans/create-pull-request + Changelogs updated during *${{ steps.current_version.outputs.version }}* release. assignees: robin-tribe29 reviewers: robin-tribe29 - team-reviewers: | - owners - maintainers draft: false - name: Copy Files and Directories to Source diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml new file mode 100644 index 000000000..484c7e49d --- /dev/null +++ b/changelogs/changelog.yaml @@ -0,0 +1,30 @@ +ancestor: null +releases: + 0.0.1: + changes: + known_issues: + - Activation is not site aware yet. All sites will be activated. + - Discovery is not feature complete yet. + - This release is still in development and a heavy work in progress. + major_changes: + - Add activation module. + - Add discovery module. + - Add folder module. + - Add host module. + - Initial creation of collection structure and layout. + fragments: + - initial.yml + modules: + - description: Activate changes in Checkmk. + name: activation + namespace: '' + - description: discovery services in Checkmk. + name: discovery + namespace: '' + - description: Manage folders in Checkmk. + name: folder + namespace: '' + - description: Manage hosts in Checkmk. + name: host + namespace: '' + release_date: '2022-02-14' diff --git a/changelogs/fragments/initial.yml b/changelogs/fragments/v0.0.2.yml similarity index 93% rename from changelogs/fragments/initial.yml rename to changelogs/fragments/v0.0.2.yml index 6bc7a9a96..b4e3d5f74 100644 --- a/changelogs/fragments/initial.yml +++ b/changelogs/fragments/v0.0.2.yml @@ -1,10 +1,7 @@ # https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to major_changes: - - Initial creation of collection structure and layout. - - Add activation module. - - Add discovery module. - - Add folder module. - - Add host module. + - Major overhaul of folder module. + - Major overhaul of host module. known_issues: - This release is still in development and a heavy work in progress. From 2f81442c1f9887b9e53bd1ee4ea9a6cd2ba2466d Mon Sep 17 00:00:00 2001 From: robin-tribe29 Date: Thu, 17 Mar 2022 11:43:00 +0000 Subject: [PATCH 53/71] Update Changelogs --- CHANGELOG.rst | 25 +++++++++++++ changelogs/.plugin-cache.yaml | 37 +++++++++++++++++++ .../{fragments => archive/0.0.2}/v0.0.2.yml | 0 changelogs/changelog.yaml | 12 ++++++ 4 files changed, 74 insertions(+) create mode 100644 changelogs/.plugin-cache.yaml rename changelogs/{fragments => archive/0.0.2}/v0.0.2.yml (100%) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5444ce479..b8b386a3e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,22 @@ tribe29.checkmk Release Notes .. contents:: Topics +v0.0.2 +====== + +Major Changes +------------- + +- Major overhaul of folder module. +- Major overhaul of host module. + +Known Issues +------------ + +- Activation is not site aware yet. All sites will be activated. +- Discovery is not feature complete yet. +- This release is still in development and a heavy work in progress. + v0.0.1 ====== @@ -20,5 +36,14 @@ Major Changes Known Issues ------------ +- Activation is not site aware yet. All sites will be activated. - Discovery is not feature complete yet. - This release is still in development and a heavy work in progress. + +New Modules +----------- + +- tribe29.checkmk.activation - Activate changes in Checkmk. +- tribe29.checkmk.discovery - discovery services in Checkmk. +- tribe29.checkmk.folder - Manage folders in Checkmk. +- tribe29.checkmk.host - Manage hosts in Checkmk. diff --git a/changelogs/.plugin-cache.yaml b/changelogs/.plugin-cache.yaml new file mode 100644 index 000000000..2e416df3c --- /dev/null +++ b/changelogs/.plugin-cache.yaml @@ -0,0 +1,37 @@ +objects: + role: {} +plugins: + become: {} + cache: {} + callback: {} + cliconf: {} + connection: {} + httpapi: {} + inventory: {} + lookup: {} + module: + activation: + description: Activate changes in Checkmk. + name: activation + namespace: '' + version_added: 0.0.1 + discovery: + description: Discover services in Checkmk. + name: discovery + namespace: '' + version_added: 0.0.1 + folder: + description: Manage folders in Checkmk. + name: folder + namespace: '' + version_added: 0.0.1 + host: + description: Manage hosts in Checkmk. + name: host + namespace: '' + version_added: 0.0.1 + netconf: {} + shell: {} + strategy: {} + vars: {} +version: 0.0.2 diff --git a/changelogs/fragments/v0.0.2.yml b/changelogs/archive/0.0.2/v0.0.2.yml similarity index 100% rename from changelogs/fragments/v0.0.2.yml rename to changelogs/archive/0.0.2/v0.0.2.yml diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 484c7e49d..35144dc8a 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -28,3 +28,15 @@ releases: name: host namespace: '' release_date: '2022-02-14' + 0.0.2: + changes: + known_issues: + - Activation is not site aware yet. All sites will be activated. + - Discovery is not feature complete yet. + - This release is still in development and a heavy work in progress. + major_changes: + - Major overhaul of folder module. + - Major overhaul of host module. + fragments: + - v0.0.2.yml + release_date: '2022-03-17' From 03efcac55a89acdff8f2fccb56859e24d7fddc1a Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 15:06:37 +0100 Subject: [PATCH 54/71] Try to improve full integration test. --- tests/integration/targets/full/vars/main.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration/targets/full/vars/main.yml b/tests/integration/targets/full/vars/main.yml index be2ed01c6..52d5a9a2d 100644 --- a/tests/integration/targets/full/vars/main.yml +++ b/tests/integration/targets/full/vars/main.yml @@ -1,6 +1,7 @@ --- -download_url: "https://download.checkmk.com/checkmk/2.0.0p20/check-mk-raw-2.0.0p20_0.bionic_amd64.deb" -site: "test" +checkmk_version: 2.0.0p21 +download_url: "https://download.checkmk.com/checkmk/{{ checkmk_version }}/check-mk-raw-{{ checkmk_version }}_0.{{ ansible_distribution_release }}_amd64.deb" +site: "integration" server_url: "http://127.0.0.1/" automation_user: "cmkadmin" automation_secret: "d7586df1-01db-3eda-9858-dbcf18d0c361" From ea45c2741bd2a31560cd6ab7d7ab84b3177c21ed Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 15:07:03 +0100 Subject: [PATCH 55/71] Update ansible-test scope. --- .github/workflows/ansible-test.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index f9814339e..287101123 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -146,10 +146,10 @@ jobs: matrix: ansible: # - stable-2.9 # Only if your collection supports Ansible 2.9 - # - stable-2.10 - # - stable-2.11 + - stable-2.10 + - stable-2.11 - stable-2.12 - - devel + # - devel python: # - '2.6' # - '2.7' @@ -171,7 +171,6 @@ jobs: - ansible: stable-2.11 python: '3.10' - steps: - name: Check out code uses: actions/checkout@v2 From 3a886475fea1d1e4f42332ed4d0b754359b0b808 Mon Sep 17 00:00:00 2001 From: Lars Getwan Date: Thu, 17 Mar 2022 15:06:08 +0100 Subject: [PATCH 56/71] Using the sites parameter is now possible. --- playbooks/test-full.yml | 19 ++++--- playbooks/test-user.yml | 103 ++++++++++++++++++++++++++++++++++ playbooks/vars/users.yml | 29 ++++++++++ plugins/modules/activation.py | 9 ++- 4 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 playbooks/test-user.yml create mode 100644 playbooks/vars/users.yml diff --git a/playbooks/test-full.yml b/playbooks/test-full.yml index bf12099ac..0e38dd002 100644 --- a/playbooks/test-full.yml +++ b/playbooks/test-full.yml @@ -11,7 +11,8 @@ automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" force_foreign_changes: 'true' - sites: "test" + sites: + - "{{ site }}" delegate_to: localhost run_once: 'true' @@ -24,7 +25,7 @@ path: "{{ item.path }}" title: "{{ item.title }}" state: "present" - delegate_to: localhost + delegate_to: localhost run_once: 'yes' loop: "{{ checkmk_folders }}" @@ -61,7 +62,8 @@ automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" force_foreign_changes: 'true' - sites: "test" + sites: + - "{{ site }}" delegate_to: localhost run_once: 'true' @@ -87,7 +89,8 @@ automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" force_foreign_changes: 'true' - sites: "test" + sites: + - "{{ site }}" delegate_to: localhost run_once: 'true' @@ -109,7 +112,8 @@ automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" force_foreign_changes: 'true' - sites: "test" + sites: + - "{{ site }}" delegate_to: localhost run_once: 'true' @@ -133,7 +137,7 @@ path: "{{ item.path }}" title: "{{ item.title }}" state: "absent" - delegate_to: localhost + delegate_to: localhost run_once: 'yes' loop: "{{ checkmk_folders }}" @@ -144,6 +148,7 @@ automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" force_foreign_changes: 'true' - sites: "test" + sites: + - "{{ site }}" delegate_to: localhost run_once: 'true' diff --git a/playbooks/test-user.yml b/playbooks/test-user.yml new file mode 100644 index 000000000..fd364b8c3 --- /dev/null +++ b/playbooks/test-user.yml @@ -0,0 +1,103 @@ +- name: "Test user module." + hosts: all + gather_facts: 'no' + vars_files: + - ./vars/config.yml + - ./vars/users.yml + tasks: + - name: "Run activation module - 1." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: + - "{{ site }}" + delegate_to: localhost + run_once: 'true' + + - name: "Create users." + user: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + username: "{{ item.username }}" + fullname: "{{ item.fullname }}" + auth_option: "{{ item.auth_option }}" + roles: + - "admin" + authorized_sites: + - "{{ site }}" + state: "present" + register: testout + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_users }}" + + - name: "Run activation module - 2." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: + - "{{ site }}" + delegate_to: localhost + run_once: 'true' + + - name: "Edit users." + user: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + username: "{{ item.username }}" + contact_options: "{{ item.contact_options }}" + contactgroups: "{{ item.contactgroups }}" + #authorized_sites: + # - "{{ site }}" + state: "present" + register: testout + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_users }}" + + - name: "Run activation module - 3." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: + - "{{ site }}" + delegate_to: localhost + run_once: 'true' + + - name: "Delete users." + user: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + username: "{{ item.username }}" + state: "absent" + register: testout + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_users }}" + + - name: "Run activation module - 4." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: + - "{{ site }}" + delegate_to: localhost + run_once: 'true' diff --git a/playbooks/vars/users.yml b/playbooks/vars/users.yml new file mode 100644 index 000000000..268797635 --- /dev/null +++ b/playbooks/vars/users.yml @@ -0,0 +1,29 @@ +--- +checkmk_users: + - username: user1 + fullname: User Eins + auth_option: + password: "123" + auth_type: password + contact_options: + email: 123@company.com + contactgroups: + - team1 + - username: user2 + fullname: User Zwei + auth_option: + password: "234" + auth_type: password + contact_options: + email: 234@company.com + contactgroups: + - team2 + - username: user3 + fullname: User Drei + auth_option: + password: "345" + auth_type: password + contact_options: + email: 345@company.com + contactgroups: + - team3 diff --git a/plugins/modules/activation.py b/plugins/modules/activation.py index 0f3d6abf6..bdc9d30a2 100644 --- a/plugins/modules/activation.py +++ b/plugins/modules/activation.py @@ -72,7 +72,7 @@ def run_module(): site=dict(type='str', required=True), automation_user=dict(type='str', required=True), automation_secret=dict(type='str', required=True, no_log=True), - sites=dict(type='str', required=True), + sites=dict(type='raw', default=[]), force_foreign_changes=dict(type='bool', default=False), ) @@ -93,6 +93,8 @@ def run_module(): automation_user = module.params['automation_user'] automation_secret = module.params['automation_secret'] sites = module.params['sites'] + if sites == {}: + sites = [] force_foreign_changes = module.params['force_foreign_changes'] http_code_mapping = { @@ -123,10 +125,7 @@ def run_module(): params = { 'force_foreign_changes': force_foreign_changes, 'redirect': True, # ToDo: Do we need this? Does it need to be configurable? - # ToDo: make sites list iterable - # 'sites': { - # 'sitename' - # } + 'sites': sites } api_endpoint = '/domain-types/activation_run/actions/activate-changes/invoke' From cb6036ca60572d3fc7478ea46b49796eb581fb73 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 16:13:58 +0100 Subject: [PATCH 57/71] Update Ansible support again. --- .github/workflows/ansible-test.yaml | 14 +++++++------- meta/runtime.yml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index e92549228..7f3d84b5d 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -146,16 +146,16 @@ jobs: matrix: ansible: # - stable-2.9 # Only if your collection supports Ansible 2.9 - - stable-2.10 - - stable-2.11 + # - stable-2.10 + # - stable-2.11 - stable-2.12 # - devel python: - # - '2.6' - # - '2.7' - # - '3.5' - # - '3.6' - # - '3.7' + - '2.6' + - '2.7' + - '3.5' + - '3.6' + - '3.7' - '3.8' - '3.9' - '3.10' diff --git a/meta/runtime.yml b/meta/runtime.yml index 83ba62e83..37621e652 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,4 +1,4 @@ -requires_ansible: '>=2.10.1' +requires_ansible: '>=2.12.1' action_groups: checkmk: From 5f25ed0e633f8d9f39b83344484ab563703bf210 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 16:30:23 +0100 Subject: [PATCH 58/71] Update docs and remove relict. --- plugins/modules/activation.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/plugins/modules/activation.py b/plugins/modules/activation.py index bdc9d30a2..a81091b7b 100644 --- a/plugins/modules/activation.py +++ b/plugins/modules/activation.py @@ -24,9 +24,9 @@ options: sites: - description: The sites that should be activated. - required: true - type: str + description: The sites that should be activated. Omitting this option activates all sites. + default: [] + type: raw force_foreign_changes: description: Wheather to active foreign changes. default: false @@ -37,15 +37,29 @@ ''' EXAMPLES = r''' -# Pass in a message -- name: "Activate changes." +- name: "Activate changes on all sites." + tribe29.checkmk.activation: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + +- name: "Activate changes on a specific site." + tribe29.checkmk.activation: + server_url: "http://localhost/" + site: "my_site" + automation_user: "automation" + automation_secret: "$SECRET" + sites: + - "my_site" + +- name: "Activate changes including foreign changes." tribe29.checkmk.activation: server_url: "http://localhost/" site: "my_site" automation_user: "automation" automation_secret: "$SECRET" force_foreign_changes: 'true' - sites: "my_site" ''' RETURN = r''' @@ -81,10 +95,6 @@ def run_module(): module = AnsibleModule(argument_spec=module_args, supports_check_mode=False) - if module.params['sites'] is None: - module.params['sites'] = { - } # ToDo: How to pass empty array of strings? - changed = False failed = False http_code = '' From bfc786cc7e3f48e9537a9382038c6c4434b52fe5 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 16:42:01 +0100 Subject: [PATCH 59/71] Improve integration test suite. --- playbooks/test-full.yml | 5 + .../targets/full/tasks/activation.yml | 151 ++++++++++++++++++ .../full/tasks/{test.yml => discovery.yml} | 64 ++++---- .../integration/targets/full/tasks/folder.yml | 49 ++++++ tests/integration/targets/full/tasks/host.yml | 128 +++++++++++++++ tests/integration/targets/full/tasks/main.yml | 13 +- 6 files changed, 379 insertions(+), 31 deletions(-) create mode 100644 tests/integration/targets/full/tasks/activation.yml rename tests/integration/targets/full/tasks/{test.yml => discovery.yml} (85%) create mode 100644 tests/integration/targets/full/tasks/folder.yml create mode 100644 tests/integration/targets/full/tasks/host.yml diff --git a/playbooks/test-full.yml b/playbooks/test-full.yml index 0e38dd002..c79bcc219 100644 --- a/playbooks/test-full.yml +++ b/playbooks/test-full.yml @@ -1,3 +1,8 @@ +--- + +# Make sure to keep the tests in sync! +# Rather use the tests than this playbook directly. + - name: "Test all modules." hosts: all gather_facts: 'no' diff --git a/tests/integration/targets/full/tasks/activation.yml b/tests/integration/targets/full/tasks/activation.yml new file mode 100644 index 000000000..df63f1c60 --- /dev/null +++ b/tests/integration/targets/full/tasks/activation.yml @@ -0,0 +1,151 @@ +--- +# Take this from playbooks/test-full.yml to ensure full coverage! +# Be sure to remove header! + +- name: "Run activation without 'site:'." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' + +- name: "Create folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + +- name: "Create hosts." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + attributes: + site: "{{ site }}" + ipaddress: 127.0.0.1 + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + +- name: "Run activation without 'site:'." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' + +- name: "Delete folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "absent" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + +- name: "Run activation without 'site:'." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' + +- name: "Create folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + +- name: "Create hosts." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + attributes: + site: "{{ site }}" + ipaddress: 127.0.0.1 + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + +- name: "Run activation with single 'site:'." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: + - "{{ site }}" + delegate_to: localhost + run_once: 'true' + +- name: "Run activation with single 'site:'." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + sites: + - "{{ site }}" + delegate_to: localhost + run_once: 'true' + +- name: "Delete folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "absent" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + +- name: "Run activation without 'site:'." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' \ No newline at end of file diff --git a/tests/integration/targets/full/tasks/test.yml b/tests/integration/targets/full/tasks/discovery.yml similarity index 85% rename from tests/integration/targets/full/tasks/test.yml rename to tests/integration/targets/full/tasks/discovery.yml index 10c113b41..b31d38744 100644 --- a/tests/integration/targets/full/tasks/test.yml +++ b/tests/integration/targets/full/tasks/discovery.yml @@ -2,17 +2,6 @@ # Take this from playbooks/test-full.yml to ensure full coverage! # Be sure to remove header! -- name: "Run activation module - 1." - activation: - server_url: "{{ server_url }}" - site: "{{ site }}" - automation_user: "{{ automation_user }}" - automation_secret: "{{ automation_secret }}" - force_foreign_changes: 'true' - sites: "test" - delegate_to: localhost - run_once: 'true' - - name: "Create folders." folder: server_url: "{{ server_url }}" @@ -42,30 +31,29 @@ run_once: 'yes' loop: "{{ checkmk_hosts }}" -- name: "Discover hosts." +- name: "Discover hosts 'refresh'." discovery: server_url: "{{ server_url }}" site: "{{ site }}" automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" host_name: "{{ item.name }}" - state: "fix_all" + state: "refresh" delegate_to: localhost run_once: 'yes' loop: "{{ checkmk_hosts }}" -- name: "Run activation module - 2." +- name: "Run activation." activation: server_url: "{{ server_url }}" site: "{{ site }}" automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" force_foreign_changes: 'true' - sites: "test" delegate_to: localhost run_once: 'true' -- name: "Change host attributes." +- name: "Delete hosts." host: server_url: "{{ server_url }}" site: "{{ site }}" @@ -73,47 +61,56 @@ automation_secret: "{{ automation_secret }}" host_name: "{{ item.name }}" folder: "{{ item.folder }}" - attributes: - site: "{{ site }}" - alias: "Important Server" - ipaddress: 127.0.0.2 - state: "present" + state: "absent" delegate_to: localhost run_once: 'yes' loop: "{{ checkmk_hosts }}" -- name: "Run activation module - 3." +- name: "Run activation." activation: server_url: "{{ server_url }}" site: "{{ site }}" automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" force_foreign_changes: 'true' - sites: "test" delegate_to: localhost run_once: 'true' -- name: "Move host to another folder." +- name: "Create hosts." host: server_url: "{{ server_url }}" site: "{{ site }}" automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" host_name: "{{ item.name }}" - folder: "/bar" + folder: "{{ item.folder }}" + attributes: + site: "{{ site }}" + ipaddress: 127.0.0.1 state: "present" delegate_to: localhost run_once: 'yes' loop: "{{ checkmk_hosts }}" -- name: "Run activation module - 4." +- name: "Discover hosts 'fix_all'." + discovery: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + state: "fix_all" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + +- name: "Run activation." activation: server_url: "{{ server_url }}" site: "{{ site }}" automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" force_foreign_changes: 'true' - sites: "test" delegate_to: localhost run_once: 'true' @@ -130,6 +127,16 @@ run_once: 'yes' loop: "{{ checkmk_hosts }}" +- name: "Run activation." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' + - name: "Delete folders." folder: server_url: "{{ server_url }}" @@ -143,13 +150,12 @@ run_once: 'yes' loop: "{{ checkmk_folders }}" -- name: "Run activation module - 5." +- name: "Run activation." activation: server_url: "{{ server_url }}" site: "{{ site }}" automation_user: "{{ automation_user }}" automation_secret: "{{ automation_secret }}" force_foreign_changes: 'true' - sites: "test" delegate_to: localhost run_once: 'true' diff --git a/tests/integration/targets/full/tasks/folder.yml b/tests/integration/targets/full/tasks/folder.yml new file mode 100644 index 000000000..f767a3f3a --- /dev/null +++ b/tests/integration/targets/full/tasks/folder.yml @@ -0,0 +1,49 @@ +--- +# Take this from playbooks/test-full.yml to ensure full coverage! +# Be sure to remove header! + +- name: "Create folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + +- name: "Run activation." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' + +- name: "Delete folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "absent" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + +- name: "Run activation." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' diff --git a/tests/integration/targets/full/tasks/host.yml b/tests/integration/targets/full/tasks/host.yml new file mode 100644 index 000000000..147cc73d6 --- /dev/null +++ b/tests/integration/targets/full/tasks/host.yml @@ -0,0 +1,128 @@ +--- +# Take this from playbooks/test-full.yml to ensure full coverage! +# Be sure to remove header! + +- name: "Create folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + +- name: "Create hosts." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + attributes: + site: "{{ site }}" + ipaddress: 127.0.0.1 + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + +- name: "Run activation." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' + +- name: "Change host attributes." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + attributes: + site: "{{ site }}" + alias: "Important Server" + ipaddress: 127.0.0.2 + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + +- name: "Run activation." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' + +- name: "Move host to another folder." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "/bar" + state: "present" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + +- name: "Run activation." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' + +- name: "Delete hosts." + host: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + host_name: "{{ item.name }}" + folder: "{{ item.folder }}" + state: "absent" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_hosts }}" + +- name: "Delete folders." + folder: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + path: "{{ item.path }}" + title: "{{ item.title }}" + state: "absent" + delegate_to: localhost + run_once: 'yes' + loop: "{{ checkmk_folders }}" + +- name: "Run activation." + activation: + server_url: "{{ server_url }}" + site: "{{ site }}" + automation_user: "{{ automation_user }}" + automation_secret: "{{ automation_secret }}" + force_foreign_changes: 'true' + delegate_to: localhost + run_once: 'true' diff --git a/tests/integration/targets/full/tasks/main.yml b/tests/integration/targets/full/tasks/main.yml index 22c526e43..31f267adb 100644 --- a/tests/integration/targets/full/tasks/main.yml +++ b/tests/integration/targets/full/tasks/main.yml @@ -6,5 +6,14 @@ ansible.builtin.pause: seconds: 5 -- name: "Run tests." - ansible.builtin.import_tasks: test.yml +- name: "Run activation tests." + ansible.builtin.import_tasks: hosts.yml + +- name: "Run discover tests." + ansible.builtin.import_tasks: hosts.yml + +- name: "Run host tests." + ansible.builtin.import_tasks: hosts.yml + +- name: "Run folder tests." + ansible.builtin.import_tasks: hosts.yml From b5e35f55d19d8aa944d6662a3f31b669afb02a76 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 16:45:31 +0100 Subject: [PATCH 60/71] Add ansible tests badge to README. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 09bbecd00..f9a06d58f 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ with automating Checkmk through Ansible. --- ## Here be dragons! +[![Ansible Tests](https://github.com/tribe29/ansible-collection-tribe29.checkmk/actions/workflows/ansible-test.yaml/badge.svg)](https://github.com/tribe29/ansible-collection-tribe29.checkmk/actions/workflows/ansible-test.yaml) + **This is a work in progress! Do not use unless you know what you are doing!** Everything within this repository is subject to possibly heavy change and we cannot guarantee any stability at this point. You have been warned! From 7202858a01060e171838f19f03e73a0063557d44 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 16:54:38 +0100 Subject: [PATCH 61/71] Bugfix integration tests. Add path-ignore to ansible-test action. --- .github/workflows/ansible-test.yaml | 6 ++++++ tests/integration/targets/full/tasks/main.yml | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index 7f3d84b5d..21a947c24 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -11,6 +11,12 @@ on: branches: - main - devel + paths-ignore: + - '.github/**' + - 'changelogs/**' + - 'docs/**' + - '**.md' + env: NAMESPACE: tribe29 COLLECTION_NAME: checkmk diff --git a/tests/integration/targets/full/tasks/main.yml b/tests/integration/targets/full/tasks/main.yml index 31f267adb..32b00861e 100644 --- a/tests/integration/targets/full/tasks/main.yml +++ b/tests/integration/targets/full/tasks/main.yml @@ -7,13 +7,13 @@ seconds: 5 - name: "Run activation tests." - ansible.builtin.import_tasks: hosts.yml + ansible.builtin.import_tasks: activation.yml -- name: "Run discover tests." - ansible.builtin.import_tasks: hosts.yml +- name: "Run discovery tests." + ansible.builtin.import_tasks: discovery.yml - name: "Run host tests." - ansible.builtin.import_tasks: hosts.yml + ansible.builtin.import_tasks: host.yml - name: "Run folder tests." - ansible.builtin.import_tasks: hosts.yml + ansible.builtin.import_tasks: folder.yml From 25593352999b13b97f237cd45eaec65745642f9a Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 16:58:47 +0100 Subject: [PATCH 62/71] Add option to manually run actions. --- .github/workflows/ansible-test.yaml | 1 + .github/workflows/codeql-analysis.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index 21a947c24..fcfa10937 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -7,6 +7,7 @@ name: Ansible Tests on: + workflow_dispatch: push: branches: - main diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index ae999a8e1..afeac108b 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -12,6 +12,7 @@ name: "CodeQL" on: + workflow_dispatch: schedule: - cron: '0 0 * * *' From 748dd79dd2b4ba85432bdcbc4c1308441299c916 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 17:15:22 +0100 Subject: [PATCH 63/71] Trying to get the tests green. --- meta/runtime.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta/runtime.yml b/meta/runtime.yml index 37621e652..53c9c7873 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,4 +1,4 @@ -requires_ansible: '>=2.12.1' +requires_ansible: '>=2.11.1' action_groups: checkmk: From 51a40d2c1f03ed56c5684ba72fbbd85a0611e2fa Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Thu, 17 Mar 2022 17:21:15 +0100 Subject: [PATCH 64/71] Still trying to get the tests green. --- .github/workflows/ansible-test.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index fcfa10937..f02960feb 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -36,8 +36,8 @@ jobs: ansible: # It's important that Sanity is tested against all stable-X.Y branches # Testing against `devel` may fail as new tests are added. - # - stable-2.9 # Only if your collection supports Ansible 2.9 - - stable-2.10 + # - stable-2.9 # Only if your collection supports Ansible 2.9 + # - stable-2.10 - stable-2.11 - stable-2.12 - devel @@ -158,7 +158,7 @@ jobs: - stable-2.12 # - devel python: - - '2.6' + # - '2.6' - '2.7' - '3.5' - '3.6' From afb8fe5f77837d937dfccbcdff3bc3de7b64ebf2 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Fri, 18 Mar 2022 08:38:24 +0100 Subject: [PATCH 65/71] Enable publishing release to the Galaxy. --- .github/workflows/release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index aff6747de..90ef82101 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -96,5 +96,5 @@ jobs: asset_name: tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz asset_content_type: application/tar+gzip - # - name: changelogs-compile Ansible collection to Galaxy - # run: ansible-galaxy collection publish tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz --api-key ${{ secrets.GALAXY_API_KEY }} + - name: Publish Ansible Collection to the Galaxy + run: ansible-galaxy collection publish tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz --api-key ${{ secrets.GALAXY_API_KEY }} From badf5ad47d6d7b809f87c65c491cdcca80814d07 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Fri, 18 Mar 2022 08:39:23 +0100 Subject: [PATCH 66/71] Fix action trigger. --- .github/workflows/ansible-test.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ansible-test.yaml b/.github/workflows/ansible-test.yaml index f02960feb..6aa5a0128 100644 --- a/.github/workflows/ansible-test.yaml +++ b/.github/workflows/ansible-test.yaml @@ -13,7 +13,6 @@ on: - main - devel paths-ignore: - - '.github/**' - 'changelogs/**' - 'docs/**' - '**.md' From a6b4447a6d5be70b56e91511de2a7e17125b5614 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Fri, 18 Mar 2022 08:51:48 +0100 Subject: [PATCH 67/71] Cleanup and update changelogs. --- changelogs/archive/0.0.1/initial.yml | 51 +++++++++++++++++++++++++++ changelogs/fragments/.gitkeep | 0 changelogs/fragments/v0.0.3.yml | 46 ++++++++++++++++++++++++ tribe29-checkmk-0.0.1.tar.gz | Bin 61089 -> 0 bytes 4 files changed, 97 insertions(+) create mode 100644 changelogs/archive/0.0.1/initial.yml create mode 100644 changelogs/fragments/.gitkeep create mode 100644 changelogs/fragments/v0.0.3.yml delete mode 100644 tribe29-checkmk-0.0.1.tar.gz diff --git a/changelogs/archive/0.0.1/initial.yml b/changelogs/archive/0.0.1/initial.yml new file mode 100644 index 000000000..13d679403 --- /dev/null +++ b/changelogs/archive/0.0.1/initial.yml @@ -0,0 +1,51 @@ +# https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to +major_changes: + - Add activation module. + - Add discovery module. + - Add folder module. + - Add host module. + - Initial creation of collection structure and layout. + +known_issues: + - Activation is not site aware yet. All sites will be activated. + - Discovery is not feature complete yet. + - This release is still in development and a heavy work in progress. + +## Line Format +# When writing a changelog entry, use the following format: + +# - scope - description starting with a lowercase letter and ending with a period at the very end. Multiple sentences are allowed (https://github.com/reference/to/an/issue or, if there is no issue, reference to a pull request itself). + +# The scope is usually a module or plugin name or group of modules or plugins, for example, lookup plugins. While module names can (and should) be mentioned directly (foo_module), plugin names should always be followed by the type (foo inventory plugin). + +# For changes that are not really scoped (for example, which affect a whole collection), use the following format: + +# - Description starting with an uppercase letter and ending with a dot at the very end. Multiple sentences are allowed (https://github.com/reference/to/an/issue or, if there is no issue, reference to a pull request itself). + + +## Possible keys: + +# breaking_changes + +# Changes that break existing playbooks or roles. This includes any change to existing behavior that forces users to update tasks. Displayed in both the changelogs and the Porting Guides. +# major_changes + +# Major changes to Ansible itself. Generally does not include module or plugin changes. Displayed in both the changelogs and the Porting Guides. +# minor_changes + +# Minor changes to Ansible, modules, or plugins. This includes new features, new parameters added to modules, or behavior changes to existing parameters. +# deprecated_features + +# Features that have been deprecated and are scheduled for removal in a future release. Displayed in both the changelogs and the Porting Guides. +# removed_features + +# Features that were previously deprecated and are now removed. Displayed in both the changelogs and the Porting Guides. +# security_fixes + +# Fixes that address CVEs or resolve security concerns. Include links to CVE information. +# bugfixes + +# Fixes that resolve issues. +# known_issues + +# Known issues that are currently not fixed or will not be fixed. diff --git a/changelogs/fragments/.gitkeep b/changelogs/fragments/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/changelogs/fragments/v0.0.3.yml b/changelogs/fragments/v0.0.3.yml new file mode 100644 index 000000000..702cb29de --- /dev/null +++ b/changelogs/fragments/v0.0.3.yml @@ -0,0 +1,46 @@ +# https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to +minor_changes: + - Activation is now site aware. + +known_issues: + - This release is still in development and a heavy work in progress. + - Discovery is not feature complete yet. + +## Line Format +# When writing a changelog entry, use the following format: + +# - scope - description starting with a lowercase letter and ending with a period at the very end. Multiple sentences are allowed (https://github.com/reference/to/an/issue or, if there is no issue, reference to a pull request itself). + +# The scope is usually a module or plugin name or group of modules or plugins, for example, lookup plugins. While module names can (and should) be mentioned directly (foo_module), plugin names should always be followed by the type (foo inventory plugin). + +# For changes that are not really scoped (for example, which affect a whole collection), use the following format: + +# - Description starting with an uppercase letter and ending with a dot at the very end. Multiple sentences are allowed (https://github.com/reference/to/an/issue or, if there is no issue, reference to a pull request itself). + + +## Possible keys: + +# breaking_changes + +# Changes that break existing playbooks or roles. This includes any change to existing behavior that forces users to update tasks. Displayed in both the changelogs and the Porting Guides. +# major_changes + +# Major changes to Ansible itself. Generally does not include module or plugin changes. Displayed in both the changelogs and the Porting Guides. +# minor_changes + +# Minor changes to Ansible, modules, or plugins. This includes new features, new parameters added to modules, or behavior changes to existing parameters. +# deprecated_features + +# Features that have been deprecated and are scheduled for removal in a future release. Displayed in both the changelogs and the Porting Guides. +# removed_features + +# Features that were previously deprecated and are now removed. Displayed in both the changelogs and the Porting Guides. +# security_fixes + +# Fixes that address CVEs or resolve security concerns. Include links to CVE information. +# bugfixes + +# Fixes that resolve issues. +# known_issues + +# Known issues that are currently not fixed or will not be fixed. diff --git a/tribe29-checkmk-0.0.1.tar.gz b/tribe29-checkmk-0.0.1.tar.gz deleted file mode 100644 index 2bd4a8b6c8f33918eded2883bd39fd107b44dbbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61089 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 z 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

v8?7B7QHXs<`~C?`q+}IhSL)ZcUvk zW=)d#A~sBwfIfTNXVm_;v!iqr;qU+>-SjuGQJY+5*rzI4!A@qFcr2`?q2;(rtq18H zoTShungg(Wgy{7F(NDPnEFX+{~vsg##>kaVozQ{3b#`@69z2|yOz;1Mh&F4lMhFwqbX1j15T z9bdQz;j=84!iWAFl=hP(IHT&lhEqi50ds6x*6R7s_(j*BPYVYvv9a3zE|)s!U@n@o zZeD#oUBklfU^#_ Date: Fri, 18 Mar 2022 09:14:35 +0100 Subject: [PATCH 68/71] Finalize workflows for release. --- .../{codeql-analysis.yaml => codeql.yaml} | 0 .github/workflows/release.yaml | 22 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) rename .github/workflows/{codeql-analysis.yaml => codeql.yaml} (100%) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql.yaml similarity index 100% rename from .github/workflows/codeql-analysis.yaml rename to .github/workflows/codeql.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 90ef82101..acf90d116 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -69,19 +69,37 @@ jobs: body: | # Ansible Collection: tribe29.checkmk + ## Installing locally + You can install this collection locally as follows, if you download the tarball from this release: ansible-galaxy collection install /path/to/tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz - You can also include it in a `requirements.yml` file using the format: + You can also include it in a `requirements.yml` file and install it with + `ansible-galaxy collection install -r requirements.yml`, using the format: ```yaml --- collections: - source: /path/to/tribe29-checkmk-${{ steps.current_version.outputs.version }}.tar.gz type: file ``` - and install it with `ansible-galaxy collection install -r requirements.yml`. + + ### Installing from the Galaxy + + You can install the Checkmk collection with the Ansible Galaxy CLI: + + ansible-galaxy collection install tribe29.checkmk + + You can also include it in a `requirements.yml` file and install it with + `ansible-galaxy collection install -r requirements.yml`, using the format: + + ```yaml + --- + collections: + - name: tribe29.checkmk + version: ${{ steps.current_version.outputs.version }} + ``` draft: false prerelease: false From e866750bda1d71468209dbe0bc346f1a0afee00a Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Fri, 18 Mar 2022 09:14:55 +0100 Subject: [PATCH 69/71] Clean up metadata. --- changelogs/fragments/{v0.0.3.yml => v0.1.0.yml} | 3 +++ galaxy.yml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) rename changelogs/fragments/{v0.0.3.yml => v0.1.0.yml} (97%) diff --git a/changelogs/fragments/v0.0.3.yml b/changelogs/fragments/v0.1.0.yml similarity index 97% rename from changelogs/fragments/v0.0.3.yml rename to changelogs/fragments/v0.1.0.yml index 702cb29de..3eb5daddc 100644 --- a/changelogs/fragments/v0.0.3.yml +++ b/changelogs/fragments/v0.1.0.yml @@ -1,4 +1,7 @@ # https://docs.ansible.com/ansible/latest/community/development_process.html#changelogs-how-to +major_changes: + - First release to Ansible Galaxy. + minor_changes: - Activation is now site aware. diff --git a/galaxy.yml b/galaxy.yml index f5909fbdf..48b5bed31 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -9,7 +9,7 @@ namespace: tribe29 name: checkmk # The version of the collection. Must be compatible with semantic versioning -version: 0.0.2 +version: 0.1.0 # The path to the Markdown (.md) readme file. This path is relative to the root of the collection readme: README.md From 6cc911b91bb0c0554744984a5d5ef5219908577a Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Fri, 18 Mar 2022 09:19:48 +0100 Subject: [PATCH 70/71] Update documentation. --- CONTRIBUTING.md | 8 ++++++-- README.md | 18 ++++++++++++------ docs/TRIBE_DOCS.md | 29 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 docs/TRIBE_DOCS.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2bc3bf5b6..42f3981ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,8 +24,12 @@ Dedicated requirements will be added here as suitable. ## Pull Requests -Please open a [pull request](https://github.com/tribe29/ansible-collection-tribe29.checkmk/pulls?q=is%3Apr+is%3Aopen) if you have something to contribute. -Dedicated requirements will be added here as suitable. +Please open a [pull request](https://github.com/tribe29/ansible-collection-tribe29.checkmk/pulls?q=is%3Apr+is%3Aopen) +if you have something to contribute. +On pull request creation, checks will run and tell you, +if your changes work with the collection. If errors are detected, please try to +fix them and update your pull request accordingly. +If you need help, feel free to ask for it. ## Code of Conduct diff --git a/README.md b/README.md index f9a06d58f..ba88fa0fe 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ with automating Checkmk through Ansible. ## Here be dragons! [![Ansible Tests](https://github.com/tribe29/ansible-collection-tribe29.checkmk/actions/workflows/ansible-test.yaml/badge.svg)](https://github.com/tribe29/ansible-collection-tribe29.checkmk/actions/workflows/ansible-test.yaml) -**This is a work in progress! Do not use unless you know what you are doing!** +**This is a work in progress!** Everything within this repository is subject to possibly heavy change and we cannot guarantee any stability at this point. You have been warned! @@ -67,14 +67,14 @@ offline installation as follows: ansible-galaxy collection install /path/to/tribe29-checkmk-X.Y.Z.tar.gz -You can also include it in a `requirements.yml` file using the format: +You can also include it in a `requirements.yml` file and install it with +`ansible-galaxy collection install -r requirements.yml`, using the format: ```yaml --- collections: - source: /path/to/tribe29-checkmk-X.Y.Z.tar.gz type: file ``` -and install it with `ansible-galaxy collection install -r requirements.yml`. ### From the Galaxy @@ -89,6 +89,7 @@ You can also include it in a `requirements.yml` file and install it with --- collections: - name: tribe29.checkmk + version: X.Y.Z ``` ## Using this collection @@ -112,8 +113,10 @@ if you list the `tribe29.checkmk` collection in the playbook's [`collections`](h automation_user: "automation" automation_secret: "$SECRET" force_foreign_changes: 'true' + sites: + - "my_site" ``` -### See Also: +### More information about Checkmk * [Checkmk Website](https://checkmk.com) * [Checkmk Documentation](https://docs.checkmk.com/) @@ -136,7 +139,7 @@ See [CHANGELOG.rst](CHANGELOG.rst). ## Roadmap This is merely a collection of possible additions to the role. -Please do **not** consider a concrete planning document for the near future! +Please do **not** consider a concrete planning document! - Modules - Monitoring @@ -154,8 +157,11 @@ Please do **not** consider a concrete planning document for the near future! - Users - Lookup Plugins - Version +- Roles + - Checkmk server installation + - Checkmk agent installation -## More information +## More information about Ansible - [Ansible Collection overview](https://github.com/ansible-collections/overview) - [Ansible User guide](https://docs.ansible.com/ansible/latest/user_guide/index.html) diff --git a/docs/TRIBE_DOCS.md b/docs/TRIBE_DOCS.md new file mode 100644 index 000000000..53760447f --- /dev/null +++ b/docs/TRIBE_DOCS.md @@ -0,0 +1,29 @@ +# tribe29 documentation + +## Development process + +When making changes to the collection please keep in mind to add a new file to +the `changelogs/fragments` folder in which you document, what you are changing. +The changelog will be built automatically on github. +If uncertain, ask @robin-tribe29 about how to handle the changelogs. + +## Tests + +Always run tests! The following are currently implemented: + +- `ansible-test sanity --docker default` +- `ansible-test integration --docker default` + +Changes should only be pushed, when all tests succeed locally. +Additionally those tests are run using Github Actions. + +## Release + +The release process is really straight forward: Update `galaxy.yml` with the +new version number, check the changelogs and tests and then run +the Github Action `Release Collection`. That takes care of creating a release +on Github and publishing the artifact to the Ansible Galaxy. + +## Ressources +- https://docs.ansible.com/ansible/latest/dev_guide/developing_collections_structure.html +- https://docs.ansible.com/ansible/latest/user_guide/collections_using.html From 2c4db627b958fde10b2261816feaabe2ac0ea790 Mon Sep 17 00:00:00 2001 From: Robin Gierse Date: Fri, 18 Mar 2022 09:41:01 +0100 Subject: [PATCH 71/71] Update README.md. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ba88fa0fe..a7ff71597 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,10 @@ Also, keep an eye on [this Checkmk forum post](https://forum.checkmk.com/t/check ## Getting help Please be aware, that although the content in this repository is maintained and -curated by tribe29, this is fully open source and there is no commercial support -to this whatsoever! We are happy to welcome you in our [Checkmk Community](https://forum.checkmk.com/) -or to look at [issues](https://github.com/tribe29/ansible-collection-tribe29.checkmk/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) -you create, but this is still a side project and we can only work on this on a **best effort basis**. +curated by tribe29, this is fully open source and there is no commercial support to this whatsoever! +Of course you can reach out in the [Checkmk Community (using the 'ansible' tag)](https://forum.checkmk.com/tag/ansible) +or create [issues](https://github.com/tribe29/ansible-collection-tribe29.checkmk/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc), +but this is still a side project and we can only work on this on a **best effort basis**. ## Repository Structure