From e1076702949f990d13525c80130310b6d9592ffc Mon Sep 17 00:00:00 2001 From: jeanluc Date: Mon, 12 Aug 2024 19:33:21 +0200 Subject: [PATCH 1/2] Update to Copier template 0.3.7 --- .copier-answers.yml | 12 +- .github/workflows/pr.yml | 14 +- .github/workflows/tag.yml | 29 +- .pre-commit-config.yaml | 28 +- .pre-commit-hooks/make-autodocs.py | 1 - CODE-OF-CONDUCT.md | 127 ++++++ CONTRIBUTING.md | 14 + NOTICE | 10 + README.md | 11 +- docs/_ext/saltdomain.py | 18 + docs/conf.py | 11 +- docs/topics/installation.md | 8 - noxfile.py | 10 +- pyproject.toml | 89 ++-- src/saltext/haproxy/beacons/__init__.py | 517 ------------------------ src/saltext/haproxy/modules/__init__.py | 3 - tests/conftest.py | 2 +- tests/unit/conftest.py | 5 + 18 files changed, 294 insertions(+), 615 deletions(-) create mode 100644 CODE-OF-CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 NOTICE create mode 100644 docs/_ext/saltdomain.py diff --git a/.copier-answers.yml b/.copier-answers.yml index 0852d71..29c79d3 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,15 +1,19 @@ # Autogenerated. Do not edit this by hand, use `copier update`. --- -_commit: 0.2.6 +_commit: 0.3.7 _src_path: https://github.com/lkubb/salt-extension-copier author: EITR Technologies, LLC author_email: devops@eitr.tech -docs_url: '' +coc_contact: devops@eitr.tech +copyright_begin: 2024 +deploy_docs: rolling +docs_url: https://salt-extensions.github.io/saltext-haproxy/ +integration_name: HAProxy license: apache loaders: - beacon - module -max_salt_version: 3006 +max_salt_version: 3007 no_saltext_namespace: false package_name: haproxy project_name: haproxy @@ -18,5 +22,7 @@ salt_version: '3005' source_url: https://github.com/salt-extensions/saltext-haproxy ssh_fixtures: false summary: Salt Extension for interacting with HAProxy +test_containers: false tracker_url: https://github.com/salt-extensions/saltext-haproxy/issues url: https://github.com/salt-extensions/saltext-haproxy +workflows: org diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index c612d7a..47c40ed 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1,11 +1,21 @@ name: Pull Request or Push -on: [push, pull_request] +on: + push: + branches: + - 'main' # Run on pushes to main + tags-ignore: + - '*' # Ignore pushes to tags + pull_request: jobs: - ci: + call_central_workflow: name: CI uses: salt-extensions/central-artifacts/.github/workflows/ci.yml@main + with: + deploy-docs: true permissions: contents: write + id-token: write + pages: write pull-requests: read diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 9f86b4e..dab0796 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -3,24 +3,31 @@ name: Tagged Releases on: push: tags: - - "v*" + - "v*" # Only tags starting with "v" for "v1.0.0", etc. jobs: - ci: + get_tag_version: runs-on: ubuntu-latest + outputs: + version: ${{ steps.get_version.outputs.version }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Extract tag name id: get_version - run: echo "VERSION=$(echo ${GITHUB_REF#refs/tags/v})" >> $GITHUB_ENV + run: echo "version=$(echo ${GITHUB_REF#refs/tags/v})" >> $GITHUB_OUTPUT - - name: CI - uses: salt-extensions/central-artifacts/.github/workflows/ci.yml@main - with: - release: true - version: ${{ env.VERSION }} - permissions: - contents: write - pull-requests: read + call_central_workflow: + needs: get_tag_version + uses: salt-extensions/central-artifacts/.github/workflows/ci.yml@main + with: + deploy-docs: true + release: true + version: ${{ needs.get_tag_version.outputs.version }} + permissions: + contents: write + id-token: write + pages: write + pull-requests: read + secrets: inherit diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f5a7029..bcfaf1c 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ minimum_pre_commit_version: 2.4.0 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: check-merge-conflict # Check for files that contain merge conflict strings. - id: trailing-whitespace # Trims trailing whitespace. @@ -56,7 +56,7 @@ repos: args: [--silent, -E, fix_docstrings] - repo: https://github.com/asottile/pyupgrade - rev: v2.37.2 + rev: v3.15.2 hooks: - id: pyupgrade name: Rewrite Code to be Py3.8+ @@ -65,35 +65,35 @@ repos: ] exclude: src/saltext/haproxy/version.py - - repo: https://github.com/asottile/reorder_python_imports - rev: v3.10.0 + - repo: https://github.com/PyCQA/isort + rev: 5.13.2 hooks: - - id: reorder-python-imports + - id: isort args: [ - --py38-plus, + --py 38, ] - exclude: src/saltext/haproxy/version.py + exclude: src/saltext/haproxy/(__init__|version).py - repo: https://github.com/psf/black - rev: 22.6.0 + rev: 24.2.0 hooks: - id: black args: [-l 100] exclude: src/saltext/haproxy/version.py - repo: https://github.com/adamchainz/blacken-docs - rev: v1.12.1 + rev: 1.16.0 hooks: - id: blacken-docs args: [--skip-errors] files: ^(docs/.*\.rst|src/saltext/haproxy/.*\.py)$ additional_dependencies: - - black==22.6.0 + - black==24.2.0 # <---- Formatting ----------------------------------------------------------------------------- # ----- Security ------------------------------------------------------------------------------> - repo: https://github.com/PyCQA/bandit - rev: "1.7.4" + rev: 1.7.8 hooks: - id: bandit alias: bandit-salt @@ -101,7 +101,7 @@ repos: args: [--silent, -lll, --skip, B701] exclude: src/saltext/haproxy/version.py - repo: https://github.com/PyCQA/bandit - rev: "1.7.4" + rev: 1.7.8 hooks: - id: bandit alias: bandit-tests @@ -112,7 +112,7 @@ repos: # ----- Code Analysis -------------------------------------------------------------------------> - repo: https://github.com/saltstack/mirrors-nox - rev: v2021.6.12 + rev: v2022.11.21 hooks: - id: nox alias: lint-src @@ -125,7 +125,7 @@ repos: - -- - repo: https://github.com/saltstack/mirrors-nox - rev: v2021.6.12 + rev: v2022.11.21 hooks: - id: nox alias: lint-tests diff --git a/.pre-commit-hooks/make-autodocs.py b/.pre-commit-hooks/make-autodocs.py index b8eb966..0324f8b 100644 --- a/.pre-commit-hooks/make-autodocs.py +++ b/.pre-commit-hooks/make-autodocs.py @@ -3,7 +3,6 @@ import subprocess from pathlib import Path - repo_path = Path(subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode().strip()) src_dir = repo_path / "src" / "saltext" / "haproxy" doc_dir = repo_path / "docs" diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md new file mode 100644 index 0000000..da3a34a --- /dev/null +++ b/CODE-OF-CONDUCT.md @@ -0,0 +1,127 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in Salt +Extension Modules for HAProxy project and our community a +harassment-free experience for everyone, regardless of age, body size, visible +or invisible disability, ethnicity, sex characteristics, gender identity and +expression, level of experience, education, socio-economic status, nationality, +personal appearance, race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at devops@eitr.tech. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0f05c50 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,14 @@ +Thanks for your interest in contributing to the Salt Extension Modules for +HAProxy! We welcome any contribution, large or small - from +adding a new feature to fixing a single letter typo. + +This is a companion to the Salt Project and the [Salt Contributing +Guide][salt-contributing] should be considered the default for this project. +Where this project disagrees with the Salt Project, the guidelines here take +precedence. Where this project is silent, the Salt guidelines should be used. + +See the **Contributing** section in the [README][README.md] for a quickstart. + + +[README.md]: README.md +[salt-contributing]: https://docs.saltproject.io/en/master/topics/development/contributing.html diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..5637fe3 --- /dev/null +++ b/NOTICE @@ -0,0 +1,10 @@ +Salt Extension Modules for HAProxy +Copyright 2024 EITR Technologies, LLC + +This product is licensed to you under the Apache 2.0 license (the "License"). +You may not use this product except in compliance with the Apache 2.0 License. + +This product may include a number of subcomponents with separate copyright +notices and license terms. Your use of these subcomponents is subject to the +terms and conditions of the subcomponent's license, as noted in the LICENSE +file. diff --git a/README.md b/README.md index e792f2b..2cedd2e 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -# Salt Extension for Haproxy +# Salt Extension for HAProxy Salt Extension for interacting with HAProxy ## Security -If you think you've found a security vulnerability, see +If you think you have found a security vulnerability, see [Salt's security guide][security]. ## User Documentation This README is for people aiming to contribute to the project. If you just want to get started with the extension, check out the -module docstrings (for now, documentation is coming!). +[User Documentation][docs]. ## Contributing @@ -64,11 +64,11 @@ these areas: You could also contribute in other ways: * Writing blog posts -* Posting on social media about how you used Salt+Haproxy to solve your +* Posting on social media about how you used Salt + HAProxy to solve your problems, including videos * Giving talks at conferences * Publishing videos -* Asking/answering questions in IRC, Slack, or email groups +* Asking/answering questions in IRC, Discord or email groups Any of these things are super valuable to our community, and we sincerely appreciate every contribution! @@ -84,3 +84,4 @@ that's where you'll find the rest of the documentation. [PRs]: https://github.com/salt-extensions/saltext-haproxy/pulls [discussions]: https://github.com/salt-extensions/saltext-haproxy/discussions [comments]: https://conventionalcomments.org/ +[docs]: https://salt-extensions.github.io/saltext-haproxy/ diff --git a/docs/_ext/saltdomain.py b/docs/_ext/saltdomain.py new file mode 100644 index 0000000..7a85489 --- /dev/null +++ b/docs/_ext/saltdomain.py @@ -0,0 +1,18 @@ +""" +Copied/distilled from Salt doc/_ext/saltdomain.py in order to be able +to use Salt's custom doc refs. +""" + + +def setup(app): + app.add_crossref_type( + directivename="conf_master", + rolename="conf_master", + indextemplate="pair: %s; conf/master", + ) + app.add_crossref_type( + directivename="conf_minion", + rolename="conf_minion", + indextemplate="pair: %s; conf/minion", + ) + return {"parallel_read_safe": True, "parallel_write_safe": True} diff --git a/docs/conf.py b/docs/conf.py index 13718f1..0e6842e 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,12 +42,12 @@ # -- Project information ----------------------------------------------------- this_year = datetime.datetime.today().year -if this_year == 2021: - copyright_year = 2021 +if this_year == 2024: + copyright_year = "2024" else: - copyright_year = f"2021 - {this_year}" + copyright_year = f"2024 - {this_year}" project = dist.metadata["Summary"] -author = dist.metadata["Author"] +author = dist.metadata.get("Author") if author is None: # Core metadata is serialized differently with pyproject.toml: @@ -79,6 +79,8 @@ # -- General configuration --------------------------------------------------- +linkcheck_ignore = [r"http://localhost:\d+"] + # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. @@ -92,6 +94,7 @@ "sphinx.ext.coverage", "sphinx_copybutton", "sphinxcontrib.spelling", + "saltdomain", "sphinxcontrib.towncrier.ext", "myst_parser", "sphinx_inline_tabs", diff --git a/docs/topics/installation.md b/docs/topics/installation.md index b8585ac..8e931f8 100644 --- a/docs/topics/installation.md +++ b/docs/topics/installation.md @@ -22,15 +22,7 @@ pip install saltext-haproxy ``` ::: -:::{important} -Currently, there is [an issue][issue-second-saltext] where the installation of a Saltext fails silently -if the environment already has another one installed. You can workaround this by -removing all Saltexts and reinstalling them in one transaction. -::: - :::{hint} Saltexts are not distributed automatically via the fileserver like custom modules, they need to be installed on each node you want them to be available on. ::: - -[issue-second-saltext]: https://github.com/saltstack/salt/issues/65433 diff --git a/noxfile.py b/noxfile.py index 64a1e3e..705dd80 100755 --- a/noxfile.py +++ b/noxfile.py @@ -19,16 +19,16 @@ nox.options.error_on_missing_interpreters = False # Python versions to test against -PYTHON_VERSIONS = ("3", "3.8", "3.9", "3.10") +PYTHON_VERSIONS = ("3", "3.8", "3.9", "3.10", "3.11", "3.12") # Be verbose when running under a CI context CI_RUN = ( os.environ.get("JENKINS_URL") or os.environ.get("CI") or os.environ.get("DRONE") is not None ) PIP_INSTALL_SILENT = CI_RUN is False -SKIP_REQUIREMENTS_INSTALL = "SKIP_REQUIREMENTS_INSTALL" in os.environ +SKIP_REQUIREMENTS_INSTALL = os.environ.get("SKIP_REQUIREMENTS_INSTALL", "0") == "1" EXTRA_REQUIREMENTS_INSTALL = os.environ.get("EXTRA_REQUIREMENTS_INSTALL") -COVERAGE_VERSION_REQUIREMENT = "coverage==5.2" +COVERAGE_REQUIREMENT = os.environ.get("COVERAGE_REQUIREMENT") or "coverage==7.5.1" SALT_REQUIREMENT = os.environ.get("SALT_REQUIREMENT") or "salt>=3005" if SALT_REQUIREMENT == "salt==master": SALT_REQUIREMENT = "git+https://github.com/saltstack/salt.git@master" @@ -89,9 +89,7 @@ def _install_requirements( # Always have the wheel package installed session.install("--progress-bar=off", "wheel", silent=PIP_INSTALL_SILENT) if install_coverage_requirements: - session.install( - "--progress-bar=off", COVERAGE_VERSION_REQUIREMENT, silent=PIP_INSTALL_SILENT - ) + session.install("--progress-bar=off", COVERAGE_REQUIREMENT, silent=PIP_INSTALL_SILENT) if install_salt: session.install("--progress-bar=off", SALT_REQUIREMENT, silent=PIP_INSTALL_SILENT) diff --git a/pyproject.toml b/pyproject.toml index 6db7663..209af8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,8 @@ classifiers = [ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", @@ -42,6 +44,7 @@ content-type = "text/markdown" [project.urls] Homepage = "https://github.com/salt-extensions/saltext-haproxy" +Documentation = "https://salt-extensions.github.io/saltext-haproxy/" Source = "https://github.com/salt-extensions/saltext-haproxy" Tracker = "https://github.com/salt-extensions/saltext-haproxy/issues" @@ -70,8 +73,8 @@ lint = [ "saltpylint", ] tests = [ - "pytest>=6.1.0", - "pytest-salt-factories>=1.0.0rc19", + "pytest>=7.2.0", + "pytest-salt-factories>=1.0.0", ] [project.entry-points."salt.loader"] @@ -101,42 +104,48 @@ build_dir = "build/sphinx" [tool.black] line-length = 100 +[tool.isort] +force_single_line = true +skip = ["src/saltext/haproxy/__init__.py"] +profile = "black" +line_length = 100 + [tool.towncrier] - package = "saltext.haproxy" - filename = "CHANGELOG.md" - template = "changelog/.template.jinja" - directory = "changelog/" - start_string = "# Changelog\n" - underlines = ["", "", ""] - title_format = "## {version} ({project_date})" - issue_format = "[#{issue}](https://github.com/salt-extensions/saltext-haproxy/issues/{issue})" - - [[tool.towncrier.type]] - directory = "removed" - name = "Removed" - showcontent = true - - [[tool.towncrier.type]] - directory = "deprecated" - name = "Deprecated" - showcontent = true - - [[tool.towncrier.type]] - directory = "changed" - name = "Changed" - showcontent = true - - [[tool.towncrier.type]] - directory = "fixed" - name = "Fixed" - showcontent = true - - [[tool.towncrier.type]] - directory = "added" - name = "Added" - showcontent = true - - [[tool.towncrier.type]] - directory = "security" - name = "Security" - showcontent = true +package = "saltext.haproxy" +filename = "CHANGELOG.md" +template = "changelog/.template.jinja" +directory = "changelog/" +start_string = "# Changelog\n" +underlines = ["", "", ""] +title_format = "## {version} ({project_date})" +issue_format = "[#{issue}](https://github.com/salt-extensions/saltext-haproxy/issues/{issue})" + +[[tool.towncrier.type]] +directory = "removed" +name = "Removed" +showcontent = true + +[[tool.towncrier.type]] +directory = "deprecated" +name = "Deprecated" +showcontent = true + +[[tool.towncrier.type]] +directory = "changed" +name = "Changed" +showcontent = true + +[[tool.towncrier.type]] +directory = "fixed" +name = "Fixed" +showcontent = true + +[[tool.towncrier.type]] +directory = "added" +name = "Added" +showcontent = true + +[[tool.towncrier.type]] +directory = "security" +name = "Security" +showcontent = true diff --git a/src/saltext/haproxy/beacons/__init__.py b/src/saltext/haproxy/beacons/__init__.py index 40f8d15..e69de29 100644 --- a/src/saltext/haproxy/beacons/__init__.py +++ b/src/saltext/haproxy/beacons/__init__.py @@ -1,517 +0,0 @@ -""" -This package contains the loader modules for the salt streams system -""" -import copy -import logging -import re -import sys - -import salt.loader -import salt.utils.event -import salt.utils.minion - -log = logging.getLogger(__name__) - - -class Beacon: - """ - This class is used to evaluate and execute on the beacon system - """ - - def __init__(self, opts, functions): - self.opts = opts - self.functions = functions - self.beacons = salt.loader.beacons(opts, functions) - self.interval_map = dict() - - def process(self, config, grains): - """ - Process the configured beacons - - The config must be a list and looks like this in yaml - - .. code_block:: yaml - beacons: - inotify: - - files: - - /etc/fstab: {} - - /var/cache/foo: {} - """ - ret = [] - b_config = copy.deepcopy(config) - if "enabled" in b_config and not b_config["enabled"]: - return - for mod in config: - if mod == "enabled": - continue - - # Convert beacons that are lists to a dict to make processing easier - current_beacon_config = None - if isinstance(config[mod], list): - current_beacon_config = {} - list(map(current_beacon_config.update, config[mod])) - elif isinstance(config[mod], dict): - current_beacon_config = config[mod] - - if "enabled" in current_beacon_config: - if not current_beacon_config["enabled"]: - log.trace("Beacon %s disabled", mod) - continue - else: - # remove 'enabled' item before processing the beacon - if isinstance(config[mod], dict): - del config[mod]["enabled"] - else: - self._remove_list_item(config[mod], "enabled") - - log.trace("Beacon processing: %s", mod) - beacon_name = None - if self._determine_beacon_config(current_beacon_config, "beacon_module"): - beacon_name = current_beacon_config["beacon_module"] - else: - beacon_name = mod - - # Run the validate function if it's available, - # otherwise there is a warning about it being missing - validate_str = f"{beacon_name}.validate" - if validate_str in self.beacons: - valid, vcomment = self.beacons[validate_str](b_config[mod]) - - if not valid: - log.error( - "Beacon %s configuration invalid, not running.\n%s", - mod, - vcomment, - ) - continue - else: - log.warning( - "No validate function found for %s, running basic beacon validation.", - mod, - ) - if not isinstance(b_config[mod], list): - log.error("Configuration for beacon must be a list.") - continue - - b_config[mod].append({"_beacon_name": mod}) - fun_str = f"{beacon_name}.beacon" - if fun_str in self.beacons: - runonce = self._determine_beacon_config(current_beacon_config, "run_once") - interval = self._determine_beacon_config(current_beacon_config, "interval") - if interval: - b_config = self._trim_config(b_config, mod, "interval") - if not self._process_interval(mod, interval): - log.trace("Skipping beacon %s. Interval not reached.", mod) - continue - if self._determine_beacon_config(current_beacon_config, "disable_during_state_run"): - log.trace( - "Evaluting if beacon %s should be skipped due to a state run.", - mod, - ) - b_config = self._trim_config(b_config, mod, "disable_during_state_run") - is_running = False - running_jobs = salt.utils.minion.running(self.opts) - for job in running_jobs: - if re.match("state.*", job["fun"]): - is_running = True - if is_running: - close_str = f"{beacon_name}.close" - if close_str in self.beacons: - log.info("Closing beacon %s. State run in progress.", mod) - self.beacons[close_str](b_config[mod]) - else: - log.info("Skipping beacon %s. State run in progress.", mod) - continue - # Update __grains__ on the beacon - self.beacons[fun_str].__globals__["__grains__"] = grains - - # Fire the beacon! - error = None - try: - raw = self.beacons[fun_str](b_config[mod]) - except: # pylint: disable=bare-except - error = f"{sys.exc_info()[1]}" - log.error("Unable to start %s beacon, %s", mod, error) - # send beacon error event - tag = "salt/beacon/{}/{}/".format( # pylint: disable=consider-using-f-string - self.opts["id"], mod - ) - ret.append( - { - "tag": tag, - "error": error, - "data": {}, - "beacon_name": beacon_name, - } - ) - if not error: - for data in raw: - tag = ( - "salt/beacon/{}/{}/".format( # pylint: disable=consider-using-f-string - self.opts["id"], mod - ) - ) - if "tag" in data: - tag += data.pop("tag") - if "id" not in data: - data["id"] = self.opts["id"] - ret.append({"tag": tag, "data": data, "beacon_name": beacon_name}) - if runonce: - self.disable_beacon(mod) - else: - log.warning("Unable to process beacon %s", mod) - return ret - - def _trim_config(self, b_config, mod, key): - """ - Take a beacon configuration and strip out the interval bits - """ - if isinstance(b_config[mod], list): - self._remove_list_item(b_config[mod], key) - elif isinstance(b_config[mod], dict): - b_config[mod].pop(key) - return b_config - - def _determine_beacon_config(self, current_beacon_config, key): - """ - Process a beacon configuration to determine its interval - """ - - interval = False - if isinstance(current_beacon_config, dict): - interval = current_beacon_config.get(key, False) - - return interval - - def _process_interval(self, mod, interval): - """ - Process beacons with intervals - Return True if a beacon should be run on this loop - """ - log.trace("Processing interval %s for beacon mod %s", interval, mod) - loop_interval = self.opts["loop_interval"] - if mod in self.interval_map: - log.trace("Processing interval in map") - counter = self.interval_map[mod] - log.trace("Interval counter: %s", counter) - if counter * loop_interval >= interval: - self.interval_map[mod] = 1 - return True - else: - self.interval_map[mod] += 1 - else: - log.trace("Interval process inserting mod: %s", mod) - self.interval_map[mod] = 1 - return False - - def _get_index(self, beacon_config, label): - """ - Return the index of a labeled config item in the beacon config, -1 if the index is not found - """ - - indexes = [index for index, item in enumerate(beacon_config) if label in item] - if not indexes: - return -1 - else: - return indexes[0] - - def _remove_list_item(self, beacon_config, label): - """ - Remove an item from a beacon config list - """ - - index = self._get_index(beacon_config, label) - del beacon_config[index] - - def _update_enabled(self, name, enabled_value): - """ - Update whether an individual beacon is enabled - """ - - if isinstance(self.opts["beacons"][name], dict): - # Backwards compatibility - self.opts["beacons"][name]["enabled"] = enabled_value - else: - enabled_index = self._get_index(self.opts["beacons"][name], "enabled") - if enabled_index >= 0: - self.opts["beacons"][name][enabled_index]["enabled"] = enabled_value - else: - self.opts["beacons"][name].append({"enabled": enabled_value}) - - def _get_beacons(self, include_opts=True, include_pillar=True): - """ - Return the beacons data structure - """ - beacons = {} - if include_pillar: - pillar_beacons = self.opts.get("pillar", {}).get("beacons", {}) - if not isinstance(pillar_beacons, dict): - raise ValueError("Beacons must be of type dict.") - beacons.update(pillar_beacons) - if include_opts: - opts_beacons = self.opts.get("beacons", {}) - if not isinstance(opts_beacons, dict): - raise ValueError("Beacons must be of type dict.") - beacons.update(opts_beacons) - return beacons - - def list_beacons(self, include_pillar=True, include_opts=True): - """ - List the beacon items - - include_pillar: Whether to include beacons that are - configured in pillar, default is True. - - include_opts: Whether to include beacons that are - configured in opts, default is True. - """ - beacons = self._get_beacons(include_pillar=include_pillar, include_opts=include_opts) - - # Fire the complete event back along with the list of beacons - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - {"complete": True, "beacons": beacons}, - tag="/salt/minion/minion_beacons_list_complete", - ) - - return True - - def list_available_beacons(self): - """ - List the available beacons - """ - _beacons = [ - "{}".format(_beacon.replace(".beacon", "")) # pylint: disable=consider-using-f-string - for _beacon in self.beacons - if ".beacon" in _beacon - ] - - # Fire the complete event back along with the list of beacons - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - {"complete": True, "beacons": _beacons}, - tag="/salt/minion/minion_beacons_list_available_complete", - ) - - return True - - def validate_beacon(self, name, beacon_data): - """ - Return available beacon functions - """ - beacon_name = next(item.get("beacon_module", name) for item in beacon_data) - - validate_str = f"{beacon_name}.validate" - # Run the validate function if it's available, - # otherwise there is a warning about it being missing - if validate_str in self.beacons: - if "enabled" in beacon_data: - del beacon_data["enabled"] - valid, vcomment = self.beacons[validate_str](beacon_data) - else: - vcomment = ( - "Beacon {} does not have a validate" # pylint: disable=consider-using-f-string - " function, skipping validation.".format(beacon_name) - ) - valid = True - - # Fire the complete event back along with the list of beacons - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - {"complete": True, "vcomment": vcomment, "valid": valid}, - tag="/salt/minion/minion_beacon_validation_complete", - ) - - return True - - def add_beacon(self, name, beacon_data): - """ - Add a beacon item - """ - - data = {} - data[name] = beacon_data - - if name in self._get_beacons(include_opts=False): - comment = ( - "Cannot update beacon item {}, " # pylint: disable=consider-using-f-string - "because it is configured in pillar.".format(name) - ) - complete = False - else: - if name in self.opts["beacons"]: - comment = f"Updating settings for beacon item: {name}" - else: - comment = f"Added new beacon item: {name}" - complete = True - self.opts["beacons"].update(data) - - # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: - evt.fire_event( - { - "complete": complete, - "comment": comment, - "beacons": self.opts["beacons"], - }, - tag="/salt/minion/minion_beacon_add_complete", - ) - - return True - - def modify_beacon(self, name, beacon_data): - """ - Modify a beacon item - """ - - data = {} - data[name] = beacon_data - - if name in self._get_beacons(include_opts=False): - comment = f"Cannot modify beacon item {name}, it is configured in pillar." - complete = False - else: - comment = f"Updating settings for beacon item: {name}" - complete = True - self.opts["beacons"].update(data) - - # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - { - "complete": complete, - "comment": comment, - "beacons": self.opts["beacons"], - }, - tag="/salt/minion/minion_beacon_modify_complete", - ) - return True - - def delete_beacon(self, name): - """ - Delete a beacon item - """ - - if name in self._get_beacons(include_opts=False): - comment = f"Cannot delete beacon item {name}, it is configured in pillar." - complete = False - else: - if name in self.opts["beacons"]: - del self.opts["beacons"][name] - comment = f"Deleting beacon item: {name}" - else: - comment = f"Beacon item {name} not found." - complete = True - - # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - { - "complete": complete, - "comment": comment, - "beacons": self.opts["beacons"], - }, - tag="/salt/minion/minion_beacon_delete_complete", - ) - - return True - - def enable_beacons(self): - """ - Enable beacons - """ - - self.opts["beacons"]["enabled"] = True - - # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - {"complete": True, "beacons": self.opts["beacons"]}, - tag="/salt/minion/minion_beacons_enabled_complete", - ) - - return True - - def disable_beacons(self): - """ - Enable beacons - """ - - self.opts["beacons"]["enabled"] = False - - # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - {"complete": True, "beacons": self.opts["beacons"]}, - tag="/salt/minion/minion_beacons_disabled_complete", - ) - - return True - - def enable_beacon(self, name): - """ - Enable a beacon - """ - - if name in self._get_beacons(include_opts=False): - comment = f"Cannot enable beacon item {name}, it is configured in pillar." - complete = False - else: - self._update_enabled(name, True) - comment = f"Enabling beacon item {name}" - complete = True - - # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - { - "complete": complete, - "comment": comment, - "beacons": self.opts["beacons"], - }, - tag="/salt/minion/minion_beacon_enabled_complete", - ) - - return True - - def disable_beacon(self, name): - """ - Disable a beacon - """ - - if name in self._get_beacons(include_opts=False): - comment = f"Cannot disable beacon item {name}, it is configured in pillar." - complete = False - else: - self._update_enabled(name, False) - comment = f"Disabling beacon item {name}" - complete = True - - # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - { - "complete": complete, - "comment": comment, - "beacons": self.opts["beacons"], - }, - tag="/salt/minion/minion_beacon_disabled_complete", - ) - - return True - - def reset(self): - """ - Reset the beacons to defaults - """ - self.opts["beacons"] = {} - with salt.utils.event.get_event("minion", opts=self.opts) as evt: - evt.fire_event( - { - "complete": True, - "comment": "Beacons have been reset", - "beacons": self.opts["beacons"], - }, - tag="/salt/minion/minion_beacon_reset_complete", - ) - return True diff --git a/src/saltext/haproxy/modules/__init__.py b/src/saltext/haproxy/modules/__init__.py index 95433ea..e69de29 100644 --- a/src/saltext/haproxy/modules/__init__.py +++ b/src/saltext/haproxy/modules/__init__.py @@ -1,3 +0,0 @@ -""" -Execution Module Directory -""" diff --git a/tests/conftest.py b/tests/conftest.py index 7f9c1f1..0f7c984 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,9 +2,9 @@ import os import pytest -from saltext.haproxy import PACKAGE_ROOT from saltfactories.utils import random_string +from saltext.haproxy import PACKAGE_ROOT # Reset the root logger to its default level(because salt changed it) logging.root.setLevel(logging.WARNING) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 3e3fa4a..b3b6ced 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -1,3 +1,5 @@ +import os + import pytest import salt.config @@ -17,6 +19,7 @@ def minion_opts(tmp_path): dirpath.mkdir(parents=True) opts[name] = str(dirpath) opts["log_file"] = "logs/minion.log" + opts["conf_file"] = os.path.join(opts["conf_dir"], "minion") return opts @@ -35,6 +38,7 @@ def master_opts(tmp_path): dirpath.mkdir(parents=True) opts[name] = str(dirpath) opts["log_file"] = "logs/master.log" + opts["conf_file"] = os.path.join(opts["conf_dir"], "master") return opts @@ -54,4 +58,5 @@ def syndic_opts(tmp_path): dirpath.mkdir(parents=True) opts[name] = str(dirpath) opts["log_file"] = "logs/syndic.log" + opts["conf_file"] = os.path.join(opts["conf_dir"], "syndic") return opts From c08a668f473ba7896aad36bae741d665d9da72eb Mon Sep 17 00:00:00 2001 From: jeanluc Date: Mon, 12 Aug 2024 19:34:08 +0200 Subject: [PATCH 2/2] Run pre-commit after update --- src/saltext/haproxy/beacons/haproxy.py | 1 + src/saltext/haproxy/modules/haproxyconn.py | 2 ++ tests/unit/beacons/test_haproxy.py | 1 + tests/unit/modules/test_haproxyconn.py | 1 + 4 files changed, 5 insertions(+) diff --git a/src/saltext/haproxy/beacons/haproxy.py b/src/saltext/haproxy/beacons/haproxy.py index e19ec34..a3aa13d 100644 --- a/src/saltext/haproxy/beacons/haproxy.py +++ b/src/saltext/haproxy/beacons/haproxy.py @@ -4,6 +4,7 @@ .. versionadded:: 2016.11.0 """ + import logging import salt.utils.beacons diff --git a/src/saltext/haproxy/modules/haproxyconn.py b/src/saltext/haproxy/modules/haproxyconn.py index 586bdc9..f7a8cf3 100644 --- a/src/saltext/haproxy/modules/haproxyconn.py +++ b/src/saltext/haproxy/modules/haproxyconn.py @@ -3,6 +3,7 @@ .. versionadded:: 2014.7.0 """ + import logging import os import stat @@ -307,6 +308,7 @@ def set_state(name, backend, state, socket=DEFAULT_SOCKET_URL): salt '*' haproxy.set_state my_proxy_server my_backend ready """ + # Pulling this in from the latest 0.5 release which is not yet in PyPi. # https://github.com/neurogeek/haproxyctl class setServerState(haproxy.cmds.Cmd): diff --git a/tests/unit/beacons/test_haproxy.py b/tests/unit/beacons/test_haproxy.py index 97bb307..febe39a 100644 --- a/tests/unit/beacons/test_haproxy.py +++ b/tests/unit/beacons/test_haproxy.py @@ -4,6 +4,7 @@ HAProxy beacon test cases """ + from unittest.mock import MagicMock from unittest.mock import patch diff --git a/tests/unit/modules/test_haproxyconn.py b/tests/unit/modules/test_haproxyconn.py index a032dfb..f2a0241 100644 --- a/tests/unit/modules/test_haproxyconn.py +++ b/tests/unit/modules/test_haproxyconn.py @@ -3,6 +3,7 @@ Test cases for salt.modules.haproxyconn """ + import pytest import salt.modules.haproxyconn as haproxyconn