diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a77efc3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,168 @@ +# AGENTS.MD - kubevirt.core +## Strict rules +- Always make sure Apache 2.0 headers are present. +- Always run `make format`, `make test-unit`, `make test-sanity`, `make test-integration` before pushing. + +## Project Overview +`kubevirt.core` is an Ansible collection that provides modules and plugins for managing KubeVirt virtual machines on Kubernetes clusters. It includes modules to create, delete, and query VirtualMachines and VirtualMachineInstances (`kubevirt_vm`, `kubevirt_vm_info`, `kubevirt_vmi_info`), as well as a dynamic inventory plugin (`kubevirt`) that discovers running VMs for use as Ansible targets. The collection depends on `kubernetes.core` and the Python `kubernetes` client library to interact with the Kubernetes API. + +## Architecture +Ansible collection project with three main plugins: + +* `kubevirt`: Inventory source for KubeVirt VirtualMachines +* `kubevirt_vm`: Create or delete KubeVirt VirtualMachines +* `kubevirt_vm_info`: Describe KubeVirt VirtualMachines +* `kubevirt_vmi_info`: Describe KubeVirt VirtualMachineInstances + +The key directories are: + +* `plugins/` — Contains all Ansible plugin code. + * `plugins/modules/` — Ansible modules (`kubevirt_vm`, `kubevirt_vm_info`, `kubevirt_vmi_info`) that implement the CRUD operations against the KubeVirt API. + * `plugins/inventory/` — Dynamic inventory plugin (`kubevirt.py`) that discovers KubeVirt VMs and exposes them as Ansible hosts. + * `plugins/module_utils/` — Shared Python helpers (`diff.py`, `info.py`) used by the modules to handle resource diffing and info retrieval. + * `plugins/doc_fragments/` — Reusable documentation fragments (e.g., `kubevirt_auth_options.py`) included in module docs. +* `tests/` — All test suites for the collection. + * `tests/unit/` — Unit tests for plugin logic. + * `tests/integration/` — Integration tests that run against a live KubeVirt cluster. + * `tests/sanity/` — Ansible sanity test ignore files per Ansible version. +* `meta/` — Collection metadata (`runtime.yml`) defining the minimum supported Ansible version (`requires_ansible`). +* `docs/` — Project documentation sources (`developing.md` for code conventions, `testing.md` for test setup, `releasing.md` for versioning strategy). +* `examples/` — Sample playbooks and inventory files demonstrating collection usage. +* `changelogs/` — Changelog configuration and release fragments used by `antsibull-changelog`. +* `hack/` — Helper scripts for CI/dev environments (e.g., `e2e-setup.sh` for end-to-end cluster setup). +* `bin/` — Vendored tool wrappers (`kind`, `kubectl`) used by CI and local development. + +## Build and Development +### Prerequisites +- Python (check [README.md](README.md) Section [Ansible and Python version compatibility](README.md#ansible-and-python-version-compatibility) for required version). +- Ansible (check [README.md](README.md) Section [Ansible and Python version compatibility](README.md#ansible-and-python-version-compatibility) for required version). +- `kubernetes.core` Ansible collection (see `requirements.yml` for version constraints). Install with `ansible-galaxy collection install -r requirements.yml`. +- Python libraries: `jsonpatch`, `kubernetes`, `PyYAML` (see `requirements.txt` for version constraints). Install with `pip install -r requirements.txt`. +- Test dependencies: `pytest`, `pytest-ansible`, `pytest-mock`, `pytest-xdist`, `ansible-compat`, `typing-extensions` (see `test-requirements.txt`). Install with `pip install -r test-requirements.txt`. +- Formatting: `black` (installed automatically by `tox` when running `make format`). +- `tox` and `tox-ansible` for running tests and managing virtualenvs. + +### Key make targets +- `make build-venv` — Create a development virtualenv (via tox) and symlink it to `.venv`. +- `make format` — Run `black` code formatter. +- `make test-unit` — Run unit tests across supported Ansible versions. +- `make test-sanity` — Run Ansible sanity tests across supported Ansible versions. + +### Cluster development +- `make test-integration` — Run integration tests (requires a running KubeVirt cluster). +- `make cluster-up` — Spin up a local Kind cluster with KubeVirt installed. +- `make cluster-down` — Tear down the local Kind cluster. + +### Building collection +In order to build the collection and install it locally, run: + +```bash +ansible-galaxy collection build . +ansible-galaxy collection install kubevirt-kubevirt.core-*.tar.gz +``` +### Contributor's guidelines +Check [developing.md](docs/developing.md) to get code and naming style conventions. + +## Testing +- **Unit tests:** pytest is used. Run with `make test-unit`. +- **Integration tests:** Tests ran in a live Kubernetes cluster. These tests are collections of ansible playbooks crafted to deploy, fetch and assert certain actions. +- **Sanity tests:** Built-in `ansible-test sanity` checks (PEP 8, import validation, documentation, etc.). Run with `make test-sanity`. Per-version ignore files live in `tests/sanity/`, in this directory, a per-version ignore file need to be added everytime there is a new version of ansible-core.. +- **Module unit tests:** Located in `tests/unit/plugins/modules/`. These tests invoke each module's `main()` entry point with patched `AnsibleModule` args and mocked API clients, verifying argument validation, expected API calls, and exit/fail outcomes without hitting a real cluster. +- **Blackbox unit tests:** Located in `tests/unit/plugins/inventory/blackbox/`. These tests call `_populate_inventory` with crafted VM, VMI, and service data, then assert the resulting inventory state (host vars, groups, connections). They verify user-facing inventory behaviour end-to-end without hitting a real cluster. + +Follow instructions in [testing.md](docs/testing.md) to set up development environments. Use the `make` targets for running the tests. +Before running a test that makes use of `ansible-test` command such as `make test-integration`, it is required to spam a Kubernetes cluster using `make cluster-up` and to bind mount the repository as described in [testing.md](docs/testing.md) section "Running tests with ansible-test". +Moreover, the Kubernetes cluster can be used to do quick tests such as running a test Ansible inventory, Ansible playbook, etc. In directory, `bin` the tool `kubectl` can be found to work with the cluster directly. Nevertheless, in this project, ansible commands are preferred. + +### Test patterns + +#### Naming conventions +- Test files are named `test_.py`, one per function under test. +- Test functions are named `test__` (e.g., `test_fetch_objects_early_return`). + +#### Unit test conventions +- Unit tests must assert and test each and every code branch. +- Unit tests use `mocker.patch` / `mocker.patch.object` (from `pytest-mock`) for patching, and `monkeypatch` when simpler attribute replacement suffices. +- Unit test must use `@pytest.mark.parametrize` as much as possible. +- When parametrize arguments become too complex or definitions could be reused across tests, define them as module-level dict constants and combine variants using the `|` (dict union) operator. +- Use `pytest.raises(Exception, match=pattern)` for exception message validation. + +#### Shared fixtures and utilities +- `tests/unit/plugins/inventory/conftest.py` provides shared fixtures (`inventory`, `groups`, `hosts`, `client`) all scoped to `function` for test isolation. +- Test files may define local fixtures when scenarios require specific setup (e.g., `test_kubevirt_format_dynamic_api_exc.py`). +- `tests/unit/utils/ansible_module_mock.py` provides the `patch_module_args()` context manager and `AnsibleExitJson`/`AnsibleFailJson` exception classes used across module tests. +- `tests/unit/plugins/inventory/constants.py` provides shared constants (`DEFAULT_NAMESPACE`, `DEFAULT_BASE_DOMAIN`) and a `merge_dicts()` helper for deep dictionary merging. +- `request.getfixturevalue()` is used for dynamic fixture lookup when tests need to select fixtures at runtime. + +#### Integration test structure +- Integration tests are located inside each `target` directory depending on which plugin is being tested (`tests/integration/targets/`). +- Each target contains: `runme.sh` (entry point), `generate.yml` (generates test files from Jinja2 templates), and `*.yml.j2` templates. +- Tests follow a Generate -> Run -> Verify -> Cleanup lifecycle with bash trap handlers for cleanup on failure. +- Namespace isolation is achieved via random suffixes to prevent conflicts between test runs. + +## Plugin Design + +### Inventory plugin — `plugins/inventory/kubevirt.py` +Dynamic inventory plugin that extends Ansible's `BaseInventoryPlugin`, `Constructable`, and `Cacheable`. Config files must end in `*.kubevirt.yml` or `*.kubevirt.yaml`. + +The plugin follows a fetch-then-populate pipeline: +1. Parse the user config and build an options dataclass. +2. Check the inventory cache or fetch VMs, VMIs, and Services from the Kubernetes API via the `kubernetes.core` dynamic client. +3. Populate the inventory by creating groups (by cluster name and namespace) and adding hosts with variables derived from VM/VMI metadata and status. + +Key design details: +- Host naming is configurable via `host_format` (default: `{namespace}-{name}`). +- Host vars are prefixed with `vm_` or `vmi_` depending on the source object. Status fields are converted from camelCase to snake_case. +- Connection address resolution follows a priority order: `kube_secondary_dns` derived hostname, LoadBalancer/NodePort service host+port, or the VMI's primary interface IP. +- Windows VMs are auto-detected (via `guestOSInfo`, preference annotations, or `vm.kubevirt.io/os` annotation) and set `ansible_connection=winrm` with WinRM service port lookup. +- Supports Ansible `constructed` features (`compose`, `groups`, `keyed_groups`). +- Includes backwards-compatible handling for a deprecated `connections` parameter (removed in 3.0.0). + +### Module — `plugins/modules/kubevirt_vm.py` +Creates, patches, or deletes KubeVirt VirtualMachines. The module: +1. Defines its argument spec by merging module-specific args with auth and common specs from `kubernetes.core`. +2. Constructs a VM resource definition from module params, translating Ansible-style snake_case params into the KubeVirt API spec (camelCase). +3. Sets a wait condition based on `running`/`run_strategy` params to determine expected VM state. +4. Delegates execution to `kubernetes.core`'s runner. + +Supports `name` or `generate_name` (mutually exclusive), `running` or `run_strategy` (mutually exclusive), instancetype/preference matchers, data volume templates, and `hidden_fields` for filtering noisy metadata from results. + +### Module — `plugins/modules/kubevirt_vm_info.py` +Read-only module to describe KubeVirt VirtualMachines. Builds its arg spec from a shared info spec (defined in `module_utils/info.py`) plus auth args, adds a `running` parameter to control the wait condition, and delegates to a shared info execution function. + +### Module — `plugins/modules/kubevirt_vmi_info.py` +Read-only module to describe KubeVirt VirtualMachineInstances. Structurally identical to `kubevirt_vm_info` but queries `VirtualMachineInstance` resources and has no `running` parameter (always waits for Ready=True). + +### Shared utilities — `plugins/module_utils/` +- `info.py` — Shared argument spec and execution logic for the info modules. Instantiates a `K8sService`, performs the resource lookup, and exits with results. +- `diff.py` — Monkey-patches `kubernetes.core`'s diff logic to ignore metadata-only changes (`generation`, `resourceVersion`), fixing false-positive "changed" results. + + +## Conventions + +### Code +- All source files must carry Apache 2.0 license headers. +- Code style and naming conventions are documented in [developing.md](docs/developing.md). +- Always use strong typing in the function headers. This applies to parameters and return values. + +### Commits +- Commit messages follow conventional commits with scope, e.g. `feat(vm_info): ...`, `fix(tests): ...`, `chore(deps): ...`. +- Commits must be signed off (`git commit -s`). +- If an AI agent assisted the developer, the commit must add `assisted-by` and the AI agent signature. + +### Pull requests and issues +- PRs must follow `.github/PULL_REQUEST_TEMPLATE.md`. Key sections: what the PR does, which issue(s) it fixes, reviewer notes, and a release note block. +- Issues must follow the templates in `.github/ISSUE_TEMPLATE/` (`bug_report.md`, `feature_request.md`). +- Always read the relevant template before creating a PR or issue and fill in all sections. +- PRs must pass CI (linter, sanity, unit, and integration tests) before merge. + +### Versioning and releases +- The project follows [Semantic Versioning 2.0.0](https://semver.org/) as documented in [releasing.md](docs/releasing.md). +- Releases are triggered by annotated git tags. The CI workflow (`.github/workflows/release.yml`) builds the collection, publishes to Ansible Galaxy, and creates a GitHub release. +- Changelog entries are managed by `antsibull-changelog` using fragments in `changelogs/fragments/`. + +### CI +- Main CI workflow (`.github/workflows/ci.yml`) runs: tree-clean check, ansible-lint, sanity tests, and unit tests across a matrix of Ansible and Python versions. +- Integration tests run in a separate workflow (`.github/workflows/integration.yml`) against a Kind cluster with KubeVirt. +- Docs linting validates Ansible documentation format via `antsibull-docs` (`.github/workflows/extra-docs-linting.yml`). +- Dependency updates are automated via Renovate (`renovate.json`), which manages e2e-setup tool versions, GitHub Actions, and collection dependencies. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..81813b4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# CLAUDE.md + +See [AGENTS.md](AGENTS.md) for project documentation. \ No newline at end of file