python-rundeck 1.1.0__tar.gz → 2.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. python_rundeck-2.0.1/API_COVERAGE.md +88 -0
  2. python_rundeck-2.0.1/CHANGELOG.md +100 -0
  3. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/PKG-INFO +58 -29
  4. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/README.md +53 -24
  5. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/pyproject.toml +12 -8
  6. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/client.py +6 -2
  7. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/const.py +4 -3
  8. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/exceptions.py +1 -1
  9. python_rundeck-2.0.1/src/rundeck/v1/objects/acl.py +37 -0
  10. python_rundeck-2.0.1/src/rundeck/v1/objects/authorizations.py +45 -0
  11. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/jobs.py +26 -13
  12. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/metrics.py +4 -0
  13. python_rundeck-2.0.1/src/rundeck/v1/objects/project_plugins.py +29 -0
  14. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/projects.py +28 -4
  15. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/scheduler.py +16 -7
  16. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/system.py +5 -2
  17. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/tokens.py +4 -0
  18. python_rundeck-1.1.0/src/rundeck/v1/objects/acl.py +0 -19
  19. python_rundeck-1.1.0/src/rundeck/v1/objects/config_management.py +0 -64
  20. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/LICENSE +0 -0
  21. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/__init__.py +0 -0
  22. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/base.py +0 -0
  23. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/config.py +0 -0
  24. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/py.typed +0 -0
  25. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/utils.py +0 -0
  26. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/__init__.py +0 -0
  27. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/__init__.py +0 -0
  28. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/adhoc.py +0 -0
  29. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/executions.py +0 -0
  30. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/features.py +0 -0
  31. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/key_storage.py +0 -0
  32. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/plugins.py +0 -0
  33. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/scm.py +0 -0
  34. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/users.py +0 -0
  35. {python_rundeck-1.1.0 → python_rundeck-2.0.1}/src/rundeck/v1/objects/webhooks.py +0 -0
@@ -0,0 +1,88 @@
1
+ # Rundeck Community API coverage
2
+
3
+ Audited against the Community source tag [v6.3.0](https://github.com/rundeck/rundeck/tree/v6.3.0)
4
+ and the [6.2.1–6.3.0 changes](https://github.com/rundeck/rundeck/compare/v6.2.1...v6.3.0),
5
+ the [API reference](https://docs.rundeck.com/docs/api/), the
6
+ [OpenAPI contract](https://docs.rundeck.com/docs/files/rundeck-api.yml), and the
7
+ [API version history](https://docs.rundeck.com/docs/api/rundeck-api-versions.html).
8
+ The package defaults to API v59 and retains explicit API v14+ selection; each row
9
+ describes the minimum version of the operation rather than the package's oldest
10
+ supported API version. API v60 can be selected explicitly on 6.3.0.
11
+ `Community` means the operation is part of Rundeck Community.
12
+
13
+ ## Community 6.3.0 compatibility (2.0.1)
14
+
15
+ No new Community routes were introduced in the compared route mappings. API v60
16
+ promotes `/enterprise/cluster/info` and `/enterprise/cluster/member/{memberId}`;
17
+ both are excluded. The Community scheduler remains available.
18
+
19
+ | HTTP | Route | Minimum API | Edition | Python interface / change | Official source | Tests |
20
+ |---|---|---:|---|---|---|---|
21
+ | POST, PUT | `/system/acl/{name}`, `/project/{project}/acl/{name}` | 17 system / 13 project | Community | JSON `contents` writes; unchanged YAML reads | API ACL reference; RUN-4790 | `test_community_630.py`, `test_system_acl.py`, `test_project_acl.py` (unit); `test_acl_json_lifecycle`, `test_yaml_request_json_response_regression` (integration) |
22
+ | GET; GET, POST | `/job/{id}/meta`, `/project/{project}/jobs/browse` | 46 | Community | Preserve `executionAclValid` metadata, including false values; added in server 6.3.0 | `JobExecutionAclMetadataComponent` at v6.3.0 | `test_job_metadata_is_raw_for_get_and_browse` (unit); `test_job_acl_metadata` (integration) |
23
+ | GET | `/plugin/list`, `/plugin/detail/{service}/{provider}` | 14 / 49 | Community | Preserve `outputs` and property `outputMetadata` | `PluginApiService` at v6.3.0 | `test_plugin_fields_are_preserved` (unit); existing plugin integrations |
24
+ | PUT | `/project/{project}/import` | 14 | Community | Additional configure permission for configuration or node-source import in server 6.3.0 | `ProjectController.apiProjectImport`, PR #10650 | `test_archive_permission_error_keeps_false_options_and_details` (unit); `test_archive_import_requires_configure` (integration) |
25
+ | POST | `/webhook/{token}` | 30 | Community | Opaque public secret unchanged by server-side token hashing | `WebhookService` and `WebhookTokenSecureMigrationService` at v6.3.0 | `test_webhook_event_send_supports_json_and_data` (unit); `test_project_webhook_lifecycle_and_send` (integration) |
26
+ | GET | `/metrics/*`, `/monitoring/*` | legacy / unversioned | Community | Docker legacy metrics variable honored in server 6.3.0 | RUN-4859 | `test_legacy_metrics_enabled_by_docker_variable`, existing monitoring integrations |
27
+
28
+ Server-version features above are not gated on the selected API version. The
29
+ `executionAclValid` warning does not enforce execution permissions. The source
30
+ tag restores the `${job.serverUrl}` trailing slash (RUN-5051); no URL rewrite is
31
+ performed by this client. Session cookies follow requests' standard behavior.
32
+
33
+ ## Added or corrected in 2.0.0
34
+
35
+ | HTTP | Route | Minimum API | Edition | Python interface | Official reference | Test |
36
+ |---|---|---:|---|---|---|---|
37
+ | GET | `/api/{v}/` | 14 | Community | `Rundeck.api_info()` | API root / API reference | `tests/unit/test_community_v59.py::test_api_info_and_default_api_version` |
38
+ | GET | `/api/{v}/home/summary` | 14 | Community | `rd.system.summary()` | System endpoints | `tests/unit/test_community_v59.py::test_system_summary_scheduler_and_expired_tokens_routes` |
39
+ | GET | `/api/{v}/scheduler/jobs` | 14 | Community | `rd.scheduler.jobs()` | Scheduler endpoints; v6.2.1 route mapping | `tests/unit/test_community_v59.py::test_system_summary_scheduler_and_expired_tokens_routes` |
40
+ | GET | `/api/{v}/scheduler/server/{uuid}/jobs` | 14 | Community | `rd.scheduler.jobs(server_uuid=...)` | Scheduler endpoints; v6.2.1 route mapping | `tests/unit/test_community_v59.py::test_system_summary_scheduler_and_expired_tokens_routes` |
41
+ | PUT | `/api/{v}/scheduler/takeover` | 14 | Community | `rd.scheduler.takeover(job_id=... or job_ids=[...])` | Scheduler endpoints; v6.2.1 route mapping | `tests/unit/test_scheduler.py` |
42
+ | POST | `/api/{v}/tokens/{user}/removeExpired` | 14 | Community | `rd.tokens.remove_expired(user)` | Tokens endpoints | `tests/unit/test_community_v59.py::test_system_summary_scheduler_and_expired_tokens_routes` |
43
+ | GET | `/monitoring/health/readiness` | n/a | Community | `rd.monitoring.readiness()` | Monitoring endpoints | `tests/unit/test_community_v59.py::test_async_export_post_uses_form_and_monitoring_readiness_is_unversioned` |
44
+ | POST | `/api/{v}/project/{project}/jobs/validateOption` | 47 | Community | `rd.jobs.validate_option(...)` / `project.jobs.validate_option(...)` | Jobs endpoints | `tests/unit/test_community_v59.py::test_option_validation_preserves_false_and_project_context` |
45
+ | POST | `/api/{v}/project/{project}/plugins/save` | 55 | Community | `project.plugins.save(...)` | Project plugin configuration | `tests/unit/test_community_v59.py::test_project_plugins_and_browse_post_contracts` |
46
+ | POST | `/api/{v}/project/{project}/jobs/browse` | 46 | Community | `rd.jobs.browse(..., use_post=True)` | Jobs browse endpoint | `tests/unit/test_community_v59.py::test_project_plugins_and_browse_post_contracts` |
47
+ | POST | `/api/{v}/project/{project}/export/async` | 14 | Community | `project.archive.export_async(..., use_post=True)` | Project archive endpoints | `tests/unit/test_community_v59.py::test_async_export_post_uses_form_and_monitoring_readiness_is_unversioned` |
48
+ | GET | `/api/{v}/authorizations/application/{kind}` | 14 | Community, experimental | `rd.authorizations.application_kind(...)` | Authorization evaluation endpoints | `tests/unit/test_community_v59.py::test_authorization_routes_repeat_actions_as_values` |
49
+ | GET | `/api/{v}/authorizations/application/{type}/{specifier}` | 14 | Community, experimental | `rd.authorizations.application_resource(...)` | Authorization evaluation endpoints | `tests/unit/test_community_v59.py::test_authorization_routes_repeat_actions_as_values` |
50
+ | GET | `/api/{v}/authorizations/project/{project}/{kind}` | 14 | Community, experimental | `rd.authorizations.project_kind(...)` | Authorization evaluation endpoints | `tests/unit/test_community_v59.py::test_authorization_routes_repeat_actions_as_values` |
51
+ | GET | `/api/{v}/authorizations/project/{project}/{type}/{specifier}` | 14 | Community, experimental | `rd.authorizations.project_resource(...)` | Authorization evaluation endpoints | `tests/unit/test_community_v59.py::test_authorization_routes_repeat_actions_as_values` |
52
+ | GET | `/api/{v}/authorizations/project/{project}/job/{id}` | 14 | Community, experimental | `rd.authorizations.project_job(...)` | Authorization evaluation endpoints | `tests/unit/test_community_v59.py::test_authorization_routes_repeat_actions_as_values` |
53
+ | GET | `/api/{v}/system/acl/{name}` | 14 | Community | `rd.system.acl.get(name)` (`Accept: application/yaml`) | System ACL endpoints | `tests/unit/test_system_acl.py` |
54
+ | GET | `/api/{v}/project/{project}/acl/{name}` | 14 | Community | `project.acl.get(name)` (`Accept: application/yaml`) | Project ACL endpoints | `tests/unit/test_project_acl.py` |
55
+
56
+ The authorization evaluation API is documented as experimental. `actions` is
57
+ sent as a repeated query parameter. Job browse POST keeps metadata and path
58
+ parameters in the query and sends search filters as JSON. Async archive POST
59
+ uses form encoding for execution IDs. `Execution.get_output()` returns the
60
+ server payload unchanged in both historical API v58 and compacted API v59 modes.
61
+
62
+ ## Existing Community operations
63
+
64
+ The following endpoint families are also exposed by this package and are checked
65
+ by their focused unit tests. Paths below use `{v}` for the selected `/api/{v}`
66
+ prefix. See each test module for the supported verbs, request fields, response
67
+ handling, and pagination behavior.
68
+
69
+ | HTTP | Route(s) | Minimum API | Edition | Python interface | Official reference | Test |
70
+ |---|---|---:|---|---|---|---|
71
+ | GET, POST, PUT, DELETE | `/project`, `/project/{project}`, `/project/{project}/resources`, `/project/{project}/config`, `/project/{project}/readme`, `/project/{project}/motd`, `/project/{project}/history`, `/project/{project}/acl/{name}` | 14 | Community | `rd.projects` and nested project managers | Projects endpoints | `tests/unit/test_project_*.py`, `tests/unit/test_project_acl.py` |
72
+ | GET, POST, PUT, DELETE | `/project/{project}/jobs`, `/job/{id}`, `/job/{id}/run`, `/job/{id}/executions`, `/project/{project}/jobs/bulk`, `/project/{project}/jobs/delete`, `/project/{project}/jobs/browse` | 14 (browse 46) | Community | `rd.jobs`, `project.jobs`, `Job` | Jobs endpoints | `tests/unit/test_jobs.py`, `tests/unit/test_community_v59.py` |
73
+ | GET, POST, PUT, DELETE | `/execution/{id}`, `/execution/{id}/output`, `/execution/{id}/state`, `/execution/{id}/abort`, `/execution/{id}/retry`, `/project/{project}/executions` | 14 | Community | `rd.executions`, `Execution` | Executions endpoints | `tests/unit/test_executions.py` |
74
+ | GET, POST, PUT, DELETE | `/system/acl`, `/system/executions/{enable,disable,status}`, `/system/logstorage`, `/system/keys/{path}` | 14 | Community | `rd.system`, `rd.keys` | System, key storage endpoints | `tests/unit/test_system_*.py`, `tests/unit/test_key_storage.py` |
75
+ | GET, POST, DELETE | `/tokens`, `/tokens/{id}`, `/tokens/{user}/removeExpired`, `/user`, `/user/{login}` | 14 | Community | `rd.tokens`, `rd.users` | Tokens and user endpoints | `tests/unit/test_tokens.py`, `tests/unit/test_users.py` |
76
+ | GET, POST, PUT, DELETE | `/project/{project}/scm/{plugin}/{action}`, `/plugin/{service}/{name}`, `/project/{project}/webhooks` | 14 | Community | SCM, plugin, and webhook managers | SCM, plugins, webhooks endpoints | `tests/unit/test_project_scm.py`, `tests/unit/test_plugins.py`, `tests/unit/test_webhooks.py` |
77
+ | GET | `/metrics[/metrics|/healthcheck|/ping|/threads]`, `/monitoring[/metrics|/health|/info|/prometheus|/threaddump]` | 14 / unversioned | Community | `rd.metrics`, `rd.monitoring` | Metrics and monitoring endpoints | `tests/unit/test_metrics.py` |
78
+ | POST | `/run/command`, `/project/{project}/run/command` | 14 | Community | `rd.run_command`, `project.run_command` | Command execution endpoint | `tests/unit/test_adhoc.py` |
79
+
80
+ ## Excluded commercial surfaces
81
+
82
+ | Removed interface / route | Reason | Verification |
83
+ |---|---|---|
84
+ | `rd.config_management` (`/config/...`) | Commercial Config Management product surface | `tests/unit/test_community_v59.py::test_api_info_and_default_api_version` |
85
+ | `Job.tags()` (`/job/{id}/tags`) | Commercial endpoint absent from the Community v6.2.1 contract | `tests/unit/test_jobs.py` |
86
+
87
+ Commercial-only calendars, runners, licenses, and other product-specific routes
88
+ are outside this client’s Community API coverage.
@@ -0,0 +1,100 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [2.0.1] - 2026-11-08
9
+ ### Fixed
10
+ - Refresh vulnerable locked dependencies to their latest compatible stable releases;
11
+ require Requests 2.33+, Black 26.3.1+ and pytest 9.0.3+.
12
+ - Refresh the virtual environment's pip before auditing cached CI environments.
13
+ - ACL writes use the official JSON `contents` envelope and request JSON responses,
14
+ avoiding raw YAML response errors while retaining YAML reads and public return types.
15
+ - ACL reads unwrap older controllers' JSON envelopes even when YAML was requested.
16
+ - Integration webhook discovery now fails if the created resource disappears.
17
+
18
+ ### Added
19
+ - Rundeck Community 6.3.0 integration coverage in API v59 and v60, alongside
20
+ 6.2.1/v59, 6.0.1/v58 and 5.18.0/v56 compatibility targets.
21
+ - Regression coverage for job execution ACL metadata, plugin output metadata,
22
+ archive import permissions, forwarded session cookies and legacy Docker metrics.
23
+
24
+ ### Changed
25
+ - Current known API is v60; the default remains v59. Enterprise v60 endpoints remain excluded.
26
+ - Local integration image defaults to `rundeck/rundeck:6.3.0-20261022`.
27
+ - Integration containers are isolated by Compose project and cleaned up on failures.
28
+ - Gitflow feature branches run the integration and Python compatibility matrices.
29
+
30
+ ## [2.0.0] - 2026-10-10
31
+ ### Added
32
+ - Community API v59 support, including API discovery, system summary, scheduler job
33
+ listing, expired-token cleanup, monitoring readiness, option validation, project
34
+ plugin configuration, POST job browsing, and asynchronous archive export.
35
+ - Experimental authorization-evaluation endpoints and a Community API coverage matrix.
36
+ - Community integration defaults for Rundeck 6.2.1/API v59, with documented 6.0.1/v58
37
+ and 5.18.0/v56 compatibility runs.
38
+
39
+ ### Changed
40
+ - Default API version is now v59; explicitly selected older versions remain supported.
41
+ - System and project ACL reads explicitly request YAML. HTTP 422 responses map to
42
+ `RundeckValidationError` while retaining the response body.
43
+ - Scheduler cluster takeover accepts one or multiple job IDs. In Community without
44
+ cluster mode, takeover has no scheduling state to transfer.
45
+
46
+ ### Removed
47
+ - Removed commercial Config Management interfaces and `Job.tags()`.
48
+
49
+ ## [1.1.0] - 2026-07-25
50
+ ### Added
51
+ - Support for `user_agent` and `config_section` parameters in the client configuration.
52
+ - API v57/v58 support: execution metrics, job browse, `metaExclude`, modern
53
+ monitoring endpoints, and v56/v57 response metadata coverage.
54
+ - Community endpoints for project history/meta/node tags, execution input and
55
+ output state, job executions, project SCM toggle, and current-user update.
56
+
57
+ ### Changed
58
+ - `__version__` now resolves from the installed package metadata, with a local
59
+ fallback to `pyproject.toml` when the package is not installed.
60
+ - README configuration section updated to include `user_agent` and `config_section`.
61
+ - Default Rundeck API version updated to v58; the client retains v14
62
+ compatibility and documents the current server minimum v17 and v34 deprecation.
63
+ - Corrected Community routes and verbs for job forecasts, resource model
64
+ sources, bulk job actions, bulk job deletion, and job pagination.
65
+ - `Job.tags` and Config Management were incorrectly grouped with scheduler
66
+ cluster operations in this entry. The first two are commercial surfaces;
67
+ scheduler listing and takeover are Community endpoints and remain supported.
68
+
69
+ ## [1.0.1] - 2026-01-21
70
+ ### Changed
71
+ - CI: skip lint/tests on `main` and build only on tags.
72
+ - Classifier updated to `Development Status :: 5 - Production/Stable`.
73
+
74
+ ## [1.0.0] - 2026-01-21
75
+ ### Changed
76
+ - `Job.run` and `Job.retry` now return raw responses by default and support `as_execution=True` for `Execution`.
77
+ - `RundeckObject.refresh` and `Execution.refresh` now return `None` (in-place update).
78
+ - `User.update` now returns the raw response and updates the object in place.
79
+ - Key storage API now uses `StorageKey` objects for `create`/`update`, moves raw content access to `StorageKey.content()`, renames `get_metadata` to `get`, and supports updating via `StorageKey.update()`.
80
+ - System ACL policies are treated as resources and `SystemACL.update()` now updates the object in place.
81
+ - Project webhooks are treated as resources (`project.webhooks`), while event sending remains a utility manager.
82
+
83
+ ## [0.3.0] - 2026-01-14
84
+ ### Added
85
+ - README badges for PyPI version, supported Python versions, license, and downloads.
86
+ - Contributor dev setup note in README.
87
+
88
+ ### Changed
89
+ - README translated to English.
90
+ - Installation section now targets PyPI installs.
91
+ - GitLab CI PyPI deployment uses verbose `twine upload`.
92
+
93
+ ## [0.2.0] - 2026-01-12
94
+ ### Added
95
+ - Project metadata URLs in `pyproject.toml` (Homepage, Repository, Documentation, Issues, Changelog).
96
+ - Changelog file to track releases.
97
+
98
+ ## [0.1.0] - 2026-01-11
99
+ ### Added
100
+ - Initial release of the python-rundeck client library.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-rundeck
3
- Version: 1.1.0
4
- Summary: Python client for the Rundeck API (v14-v58).
3
+ Version: 2.0.1
4
+ Summary: Python client for the Rundeck Community API (v14-v60, default v59).
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
7
7
  Keywords: rundeck,api,client
@@ -21,12 +21,12 @@ Classifier: Topic :: System :: Systems Administration
21
21
  Classifier: Typing :: Typed
22
22
  Provides-Extra: dev
23
23
  Requires-Dist: bandit (>=1.7.9,<2.0.0) ; extra == "dev"
24
- Requires-Dist: black (>=25.11.0,<26.0.0) ; extra == "dev"
24
+ Requires-Dist: black (>=26.3.1,<27.0.0) ; extra == "dev"
25
25
  Requires-Dist: flake8 (>=7.0.0,<8.0.0) ; extra == "dev"
26
26
  Requires-Dist: isort (>=7.0.0,<8.0.0) ; extra == "dev"
27
- Requires-Dist: pytest (>=9.0.2,<10.0.0) ; extra == "dev"
27
+ Requires-Dist: pytest (>=9.0.3,<10.0.0) ; extra == "dev"
28
28
  Requires-Dist: pytest-cov (>=5.0.0,<6.0.0) ; extra == "dev"
29
- Requires-Dist: requests (>=2.32.5,<3.0.0)
29
+ Requires-Dist: requests (>=2.33.0,<3.0.0)
30
30
  Requires-Dist: ruff (>=0.7.0,<1.0.0) ; extra == "dev"
31
31
  Requires-Dist: safety (>=3.2.0,<4.0.0) ; extra == "dev"
32
32
  Project-URL: Changelog, https://gitlab.com/pascal.seckinger/python-rundeck/-/blob/main/CHANGELOG.md
@@ -43,7 +43,9 @@ python-rundeck
43
43
  ![License](https://img.shields.io/pypi/l/python-rundeck)
44
44
  ![Downloads](https://static.pepy.tech/badge/python-rundeck)
45
45
 
46
- Python client for the Rundeck API (v14-v58), inspired by the python-gitlab architecture. The default API version is v58; current Rundeck servers require v17 or later and versions below v34 are deprecated. Provides typed managers for Community resources (projects, jobs, executions, tokens, users, system) and SCM (import/export).
46
+ Python client for the Rundeck Community API (v14-v60), inspired by the python-gitlab architecture. Rundeck Community 6.3.0 is the integration reference. The default API version remains v59; use `api_version=60` explicitly with 6.3.0. Current Rundeck servers require v17 or later and versions below v34 are deprecated. Provides typed managers for Community resources (projects, jobs, executions, tokens, users, system) and SCM (import/export).
47
+
48
+ Version 2.0 removes commercial-only Config Management and job-tag endpoints. Cluster schedule listing and takeover are Community operations and remain available. The authorization-evaluation endpoints are experimental in Rundeck and are exposed as such by this client.
47
49
 
48
50
  Contents
49
51
  --------
@@ -69,9 +71,9 @@ Quick start
69
71
  ```python
70
72
  from rundeck.client import Rundeck
71
73
 
72
- rd = Rundeck(url="https://rundeck.example.com", token="MY_TOKEN", api_version=58)
74
+ rd = Rundeck(url="https://rundeck.example.com", token="MY_TOKEN")
73
75
  # Password auth (session cookie) if no token
74
- rd = Rundeck(url="https://rundeck.example.com", username="admin", password="admin", api_version=58)
76
+ rd = Rundeck(url="https://rundeck.example.com", username="admin", password="admin")
75
77
 
76
78
  # List projects
77
79
  projects = rd.projects.list()
@@ -94,7 +96,7 @@ Main parameters:
94
96
  - `url`: Rundeck URL (e.g. `https://rundeck.example.com`)
95
97
  - `token`: API token (header `X-Rundeck-Auth-Token`)
96
98
  - `username` / `password`: Session authentication (j_security_check) if no token is provided.
97
- - `api_version`: API version (default: `58`)
99
+ - `api_version`: API version (default: `59`; older versions can be selected explicitly)
98
100
  - `timeout`: Request timeout (float, seconds)
99
101
  - `ssl_verify`: TLS verification (bool or CA path)
100
102
  - `user_agent`: Custom HTTP User-Agent (string)
@@ -105,7 +107,7 @@ Useful env vars (client config):
105
107
  - `RUNDECK_URL`: Base URL
106
108
  - `RUNDECK_TOKEN`: API token (header `X-Rundeck-Auth-Token`)
107
109
  - `RUNDECK_USERNAME` / `RUNDECK_PASSWORD`: Session auth (if no token)
108
- - `RUNDECK_API_VERSION`: API version (e.g. 58)
110
+ - `RUNDECK_API_VERSION`: API version (default `59`; older server versions can be selected explicitly)
109
111
  - `RUNDECK_TIMEOUT`: Request timeout (seconds)
110
112
  - `RUNDECK_SSL_VERIFY`: TLS verification (bool or CA path)
111
113
  - `RUNDECK_USER_AGENT`: HTTP User-Agent
@@ -120,14 +122,14 @@ Available resources
120
122
  - `tokens` (`TokenManager`): list, get, create, delete.
121
123
  - `users` (`UserManager`): user operations (per current implementation).
122
124
  - `metrics` (`MetricsManager`): legacy `/api/V/metrics` endpoints (list/data/healthcheck/ping/threads).
123
- - `monitoring` (`MonitoringManager`): modern non-versioned `/monitoring/*` endpoints (Prometheus, metrics, health, info, thread dump).
125
+ - `monitoring` (`MonitoringManager`): modern non-versioned `/monitoring/*` endpoints (Prometheus, metrics, health, readiness, info, thread dump).
126
+ - `authorizations` (`AuthorizationsManager`): experimental authorization evaluation endpoints.
124
127
  - `plugins` (`PluginManager`): list installed plugins (`/plugin/list`).
125
128
  - `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`): project webhook management and sending via token.
126
129
  - `key_storage` (`StorageKeyManager`): key storage management `/storage/keys`.
127
130
  - `adhoc` (`AdhocManager`, via `project.adhoc`): run AdHoc commands/scripts.
128
- - `system` (`SystemManager`): system info, executions enable/disable, logstorage, ACL.
129
- - `config_management` (`ConfigManagementManager`): global configuration `/config` (commercial, deprecated and scheduled for removal in 2.0).
130
- - `scheduler` (`SchedulerManager`): cluster scheduler takeover (commercial, deprecated and scheduled for removal in 2.0).
131
+ - `system` (`SystemManager`): system info and summary, executions enable/disable, logstorage, ACL.
132
+ - `scheduler` (`SchedulerManager`): scheduled job listing and cluster schedule takeover (Community).
131
133
  - `scm` (via `project.scm` and `job.scm`): import/export plugins, setup, enable/disable, status, actions (commit/import/export...).
132
134
 
133
135
  Return types policy
@@ -148,7 +150,7 @@ Factory managers:
148
150
  Returns `Execution` objects (execution factory).
149
151
 
150
152
  Utility managers:
151
- - `system`, `config_management`, `metrics`, `scheduler`, `webhooks` (event send).
153
+ - `system`, `metrics`, `scheduler`, `webhooks` (event send), `authorizations` (experimental).
152
154
  - `scm` (via `project.scm` and `job.scm`), and project sub-managers like
153
155
  `project.config`, `project.resources`, `project.sources`, `project.readme`,
154
156
  `project.archive`, `project.acl`.
@@ -156,6 +158,23 @@ Utility managers:
156
158
  Note: `project.webhooks.create(...)` returns the raw API response (e.g., `{"msg": "Saved webhook"}`)
157
159
  because the Rundeck API does not return the created webhook object.
158
160
 
161
+ Compatibility with Community 6.3.0 (2.0.1)
162
+ ---------------------------------------
163
+ - API v60 is recognized; the default stays v59 to retain compatibility with Community 6.2.1. The new v60 cluster endpoints are Enterprise-only and excluded.
164
+ - ACL creation and updates send the YAML text inside a JSON `contents` envelope and request JSON responses. Reads still request YAML and unwrap legacy JSON responses from older project ACL controllers. Python signatures and resource return conventions are unchanged.
165
+ - `job.meta(meta="executionAclValid")` and `project.jobs.browse(meta="executionAclValid")` expose the server's saved-user authorization warning. It does not block execution. Plugin `outputs` and property `outputMetadata` are preserved when returned by the server.
166
+ - Archive imports with `importConfig=True` or `importNodesSources=True` also require project `configure` authorization on 6.3.0. Use explicit `False` values for jobs-only imports. Authorization failures retain the server response in `RundeckAuthenticationError.response_body`.
167
+ - Webhook tokens remain opaque public secrets. The server handles hashing and migration; callers must continue sending the original secret.
168
+ - Session authentication uses standard cookie handling; 6.3.0 fixes direct HTTP authentication when the public URL is HTTPS behind forwarded headers.
169
+ - The stable 6.3.0 sources restore the trailing slash on `${job.serverUrl}` (RUN-5051), despite the earlier release-note wording. The client does not rewrite job context URLs.
170
+
171
+ Migration from 1.1.0
172
+ -------------------
173
+ - Set `api_version=59` explicitly when upgrading a server/client pair gradually; the default is now 59.
174
+ - Remove calls to `rd.config_management` and `job.tags()`: their endpoints are only available in commercial Rundeck products.
175
+ - `rd.scheduler.takeover()` and scheduler job listing remain supported by Community. On a non-cluster server, takeover returns a successful response indicating that no action was performed.
176
+ - `rd.authorizations` wraps experimental endpoints and can change with Rundeck releases.
177
+
159
178
  Example:
160
179
  ```python
161
180
  job = rd.jobs.get("job-id") # -> Job (object)
@@ -468,17 +487,6 @@ if features:
468
487
  print(first.name, status.enabled)
469
488
  ```
470
489
 
471
- Global configuration `/config`
472
- ```python
473
- # Commercial compatibility surface; deprecated and removed in 2.0.
474
- cfg = rd.config_management
475
- all_configs = cfg.list()
476
- cfg.save([{"key": "ui.banner", "value": "Hello"}])
477
- cfg.delete("ui.banner", strata="default")
478
- cfg.refresh()
479
- cfg.restart()
480
- ```
481
-
482
490
  Monitoring
483
491
  ----------
484
492
  ```python
@@ -490,6 +498,24 @@ thread_dump = rd.monitoring.thread_dump()
490
498
 
491
499
  # Legacy metrics require rundeck.metrics.legacy.enabled=true on Rundeck 6.0.
492
500
  legacy_threads = rd.metrics.threads()
501
+ ready = rd.monitoring.readiness()
502
+ ```
503
+
504
+ Community additions
505
+ -------------------
506
+ ```python
507
+ api_info = rd.api_info()
508
+ summary = rd.system.summary()
509
+ scheduled_jobs = rd.scheduler.jobs()
510
+ rd.tokens.remove_expired("admin") # use "*" with suitable administrator rights
511
+
512
+ project = rd.projects.get("demo")
513
+ project.plugins.save("ResourceModelSource", "resources.source", plugins=[])
514
+ project.archive.export_async(execution_ids=["1", "2"], use_post=True)
515
+ project.jobs.validate_option(option_data, job_was_scheduled=False)
516
+
517
+ # Experimental authorization API; actions are repeated query parameters.
518
+ permissions = rd.authorizations.application_kind("job", ["read", "run"])
493
519
  ```
494
520
 
495
521
  Pagination
@@ -510,13 +536,16 @@ Development and testing
510
536
  - Formatting/Lint: `black src/rundeck tests/`, `ruff check src/rundeck tests/`
511
537
  - Typing: `mypy src/rundeck/`
512
538
  - Tests: `pytest`
513
- - Local integration tests: `scripts/run-integration.sh` starts Rundeck Community 6.0.1 by default with API v58. Use `RUNDECK_IMAGE=rundeck/rundeck:5.18.0 RUNDECK_API_VERSION=56 scripts/run-integration.sh` for the v56 compatibility run. Add `KEEP_STACK=1` to keep the instance running after tests. Dedicated teardown script: `scripts/stop-integration.sh`.
539
+ - Local integration tests: `scripts/run-integration.sh` starts `rundeck/rundeck:6.3.0-20261022` with API v59. Each run has an isolated Compose project and cleans up even when tests fail. With `KEEP_STACK=1`, the script prints the project name needed by `scripts/stop-integration.sh`.
540
+ - GitLab runs five integration targets: 6.3.0/API 59, 6.3.0/API 60, 6.2.1/API 59, 6.0.1/API 58 and 5.18.0/API 56. Set `RUNDECK_IMAGE` and `RUNDECK_API_VERSION` to run each target locally. Python compatibility tests retain 3.11–3.15 (3.15-rc Docker image).
541
+ - To exercise the 6.3.0 session regression and legacy metrics: `RUNDECK_PUBLIC_URL=https://rundeck.example.invalid RUNDECK_METRICS_LEGACY_ENABLED=true scripts/run-integration.sh`. The client still connects to `RUNDECK_URL` (default `http://localhost:4440`). The public URL defaults to the client URL, ensuring session redirects also work on older servers. Optional `RUNDECK_HTTP_PORT` changes the published port; set `RUNDECK_URL` accordingly.
514
542
  - The docker compose stack mounts `tokens.properties` at the repo root to seed a local admin token for integration tests. This file is only intended for local testing.
543
+ - Run local compatibility targets sequentially, stopping each stack before starting the next. This keeps at most one Rundeck JVM and one PostgreSQL container active.
515
544
 
516
545
  Code structure
517
546
  --------------
518
547
  - `src/rundeck/base.py`: generic objects/managers, `_build_path` helpers, pagination, CRUD.
519
548
  - `src/rundeck/client.py`: HTTP client, `http_get/post/put/delete/list` methods.
520
- - `src/rundeck/v1/objects/`: domain managers and objects (projects, jobs, executions, system, tokens, users, config_management).
521
- - Managers/objects schema: we apply the manager -> object pattern wherever the API exposes an identifiable resource (jobs, projects, users, tokens, executions, etc.). For purely global or utility endpoints without a resource (e.g., `/system` and its subdomains, `/config`, `/metrics`), we use dedicated sub-managers rather than forcing an artificial object.
549
+ - `src/rundeck/v1/objects/`: Community domain managers and objects (projects, jobs, executions, system, tokens, users, authorizations).
550
+ - Managers/objects schema: we apply the manager -> object pattern wherever the API exposes an identifiable resource (jobs, projects, users, tokens, executions, etc.). For global or utility endpoints without a resource (e.g., `/system`, `/scheduler`, `/metrics`), we use dedicated sub-managers rather than forcing an artificial object.
522
551
 
@@ -5,7 +5,9 @@ python-rundeck
5
5
  ![License](https://img.shields.io/pypi/l/python-rundeck)
6
6
  ![Downloads](https://static.pepy.tech/badge/python-rundeck)
7
7
 
8
- Python client for the Rundeck API (v14-v58), inspired by the python-gitlab architecture. The default API version is v58; current Rundeck servers require v17 or later and versions below v34 are deprecated. Provides typed managers for Community resources (projects, jobs, executions, tokens, users, system) and SCM (import/export).
8
+ Python client for the Rundeck Community API (v14-v60), inspired by the python-gitlab architecture. Rundeck Community 6.3.0 is the integration reference. The default API version remains v59; use `api_version=60` explicitly with 6.3.0. Current Rundeck servers require v17 or later and versions below v34 are deprecated. Provides typed managers for Community resources (projects, jobs, executions, tokens, users, system) and SCM (import/export).
9
+
10
+ Version 2.0 removes commercial-only Config Management and job-tag endpoints. Cluster schedule listing and takeover are Community operations and remain available. The authorization-evaluation endpoints are experimental in Rundeck and are exposed as such by this client.
9
11
 
10
12
  Contents
11
13
  --------
@@ -31,9 +33,9 @@ Quick start
31
33
  ```python
32
34
  from rundeck.client import Rundeck
33
35
 
34
- rd = Rundeck(url="https://rundeck.example.com", token="MY_TOKEN", api_version=58)
36
+ rd = Rundeck(url="https://rundeck.example.com", token="MY_TOKEN")
35
37
  # Password auth (session cookie) if no token
36
- rd = Rundeck(url="https://rundeck.example.com", username="admin", password="admin", api_version=58)
38
+ rd = Rundeck(url="https://rundeck.example.com", username="admin", password="admin")
37
39
 
38
40
  # List projects
39
41
  projects = rd.projects.list()
@@ -56,7 +58,7 @@ Main parameters:
56
58
  - `url`: Rundeck URL (e.g. `https://rundeck.example.com`)
57
59
  - `token`: API token (header `X-Rundeck-Auth-Token`)
58
60
  - `username` / `password`: Session authentication (j_security_check) if no token is provided.
59
- - `api_version`: API version (default: `58`)
61
+ - `api_version`: API version (default: `59`; older versions can be selected explicitly)
60
62
  - `timeout`: Request timeout (float, seconds)
61
63
  - `ssl_verify`: TLS verification (bool or CA path)
62
64
  - `user_agent`: Custom HTTP User-Agent (string)
@@ -67,7 +69,7 @@ Useful env vars (client config):
67
69
  - `RUNDECK_URL`: Base URL
68
70
  - `RUNDECK_TOKEN`: API token (header `X-Rundeck-Auth-Token`)
69
71
  - `RUNDECK_USERNAME` / `RUNDECK_PASSWORD`: Session auth (if no token)
70
- - `RUNDECK_API_VERSION`: API version (e.g. 58)
72
+ - `RUNDECK_API_VERSION`: API version (default `59`; older server versions can be selected explicitly)
71
73
  - `RUNDECK_TIMEOUT`: Request timeout (seconds)
72
74
  - `RUNDECK_SSL_VERIFY`: TLS verification (bool or CA path)
73
75
  - `RUNDECK_USER_AGENT`: HTTP User-Agent
@@ -82,14 +84,14 @@ Available resources
82
84
  - `tokens` (`TokenManager`): list, get, create, delete.
83
85
  - `users` (`UserManager`): user operations (per current implementation).
84
86
  - `metrics` (`MetricsManager`): legacy `/api/V/metrics` endpoints (list/data/healthcheck/ping/threads).
85
- - `monitoring` (`MonitoringManager`): modern non-versioned `/monitoring/*` endpoints (Prometheus, metrics, health, info, thread dump).
87
+ - `monitoring` (`MonitoringManager`): modern non-versioned `/monitoring/*` endpoints (Prometheus, metrics, health, readiness, info, thread dump).
88
+ - `authorizations` (`AuthorizationsManager`): experimental authorization evaluation endpoints.
86
89
  - `plugins` (`PluginManager`): list installed plugins (`/plugin/list`).
87
90
  - `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`): project webhook management and sending via token.
88
91
  - `key_storage` (`StorageKeyManager`): key storage management `/storage/keys`.
89
92
  - `adhoc` (`AdhocManager`, via `project.adhoc`): run AdHoc commands/scripts.
90
- - `system` (`SystemManager`): system info, executions enable/disable, logstorage, ACL.
91
- - `config_management` (`ConfigManagementManager`): global configuration `/config` (commercial, deprecated and scheduled for removal in 2.0).
92
- - `scheduler` (`SchedulerManager`): cluster scheduler takeover (commercial, deprecated and scheduled for removal in 2.0).
93
+ - `system` (`SystemManager`): system info and summary, executions enable/disable, logstorage, ACL.
94
+ - `scheduler` (`SchedulerManager`): scheduled job listing and cluster schedule takeover (Community).
93
95
  - `scm` (via `project.scm` and `job.scm`): import/export plugins, setup, enable/disable, status, actions (commit/import/export...).
94
96
 
95
97
  Return types policy
@@ -110,7 +112,7 @@ Factory managers:
110
112
  Returns `Execution` objects (execution factory).
111
113
 
112
114
  Utility managers:
113
- - `system`, `config_management`, `metrics`, `scheduler`, `webhooks` (event send).
115
+ - `system`, `metrics`, `scheduler`, `webhooks` (event send), `authorizations` (experimental).
114
116
  - `scm` (via `project.scm` and `job.scm`), and project sub-managers like
115
117
  `project.config`, `project.resources`, `project.sources`, `project.readme`,
116
118
  `project.archive`, `project.acl`.
@@ -118,6 +120,23 @@ Utility managers:
118
120
  Note: `project.webhooks.create(...)` returns the raw API response (e.g., `{"msg": "Saved webhook"}`)
119
121
  because the Rundeck API does not return the created webhook object.
120
122
 
123
+ Compatibility with Community 6.3.0 (2.0.1)
124
+ ---------------------------------------
125
+ - API v60 is recognized; the default stays v59 to retain compatibility with Community 6.2.1. The new v60 cluster endpoints are Enterprise-only and excluded.
126
+ - ACL creation and updates send the YAML text inside a JSON `contents` envelope and request JSON responses. Reads still request YAML and unwrap legacy JSON responses from older project ACL controllers. Python signatures and resource return conventions are unchanged.
127
+ - `job.meta(meta="executionAclValid")` and `project.jobs.browse(meta="executionAclValid")` expose the server's saved-user authorization warning. It does not block execution. Plugin `outputs` and property `outputMetadata` are preserved when returned by the server.
128
+ - Archive imports with `importConfig=True` or `importNodesSources=True` also require project `configure` authorization on 6.3.0. Use explicit `False` values for jobs-only imports. Authorization failures retain the server response in `RundeckAuthenticationError.response_body`.
129
+ - Webhook tokens remain opaque public secrets. The server handles hashing and migration; callers must continue sending the original secret.
130
+ - Session authentication uses standard cookie handling; 6.3.0 fixes direct HTTP authentication when the public URL is HTTPS behind forwarded headers.
131
+ - The stable 6.3.0 sources restore the trailing slash on `${job.serverUrl}` (RUN-5051), despite the earlier release-note wording. The client does not rewrite job context URLs.
132
+
133
+ Migration from 1.1.0
134
+ -------------------
135
+ - Set `api_version=59` explicitly when upgrading a server/client pair gradually; the default is now 59.
136
+ - Remove calls to `rd.config_management` and `job.tags()`: their endpoints are only available in commercial Rundeck products.
137
+ - `rd.scheduler.takeover()` and scheduler job listing remain supported by Community. On a non-cluster server, takeover returns a successful response indicating that no action was performed.
138
+ - `rd.authorizations` wraps experimental endpoints and can change with Rundeck releases.
139
+
121
140
  Example:
122
141
  ```python
123
142
  job = rd.jobs.get("job-id") # -> Job (object)
@@ -430,17 +449,6 @@ if features:
430
449
  print(first.name, status.enabled)
431
450
  ```
432
451
 
433
- Global configuration `/config`
434
- ```python
435
- # Commercial compatibility surface; deprecated and removed in 2.0.
436
- cfg = rd.config_management
437
- all_configs = cfg.list()
438
- cfg.save([{"key": "ui.banner", "value": "Hello"}])
439
- cfg.delete("ui.banner", strata="default")
440
- cfg.refresh()
441
- cfg.restart()
442
- ```
443
-
444
452
  Monitoring
445
453
  ----------
446
454
  ```python
@@ -452,6 +460,24 @@ thread_dump = rd.monitoring.thread_dump()
452
460
 
453
461
  # Legacy metrics require rundeck.metrics.legacy.enabled=true on Rundeck 6.0.
454
462
  legacy_threads = rd.metrics.threads()
463
+ ready = rd.monitoring.readiness()
464
+ ```
465
+
466
+ Community additions
467
+ -------------------
468
+ ```python
469
+ api_info = rd.api_info()
470
+ summary = rd.system.summary()
471
+ scheduled_jobs = rd.scheduler.jobs()
472
+ rd.tokens.remove_expired("admin") # use "*" with suitable administrator rights
473
+
474
+ project = rd.projects.get("demo")
475
+ project.plugins.save("ResourceModelSource", "resources.source", plugins=[])
476
+ project.archive.export_async(execution_ids=["1", "2"], use_post=True)
477
+ project.jobs.validate_option(option_data, job_was_scheduled=False)
478
+
479
+ # Experimental authorization API; actions are repeated query parameters.
480
+ permissions = rd.authorizations.application_kind("job", ["read", "run"])
455
481
  ```
456
482
 
457
483
  Pagination
@@ -472,12 +498,15 @@ Development and testing
472
498
  - Formatting/Lint: `black src/rundeck tests/`, `ruff check src/rundeck tests/`
473
499
  - Typing: `mypy src/rundeck/`
474
500
  - Tests: `pytest`
475
- - Local integration tests: `scripts/run-integration.sh` starts Rundeck Community 6.0.1 by default with API v58. Use `RUNDECK_IMAGE=rundeck/rundeck:5.18.0 RUNDECK_API_VERSION=56 scripts/run-integration.sh` for the v56 compatibility run. Add `KEEP_STACK=1` to keep the instance running after tests. Dedicated teardown script: `scripts/stop-integration.sh`.
501
+ - Local integration tests: `scripts/run-integration.sh` starts `rundeck/rundeck:6.3.0-20261022` with API v59. Each run has an isolated Compose project and cleans up even when tests fail. With `KEEP_STACK=1`, the script prints the project name needed by `scripts/stop-integration.sh`.
502
+ - GitLab runs five integration targets: 6.3.0/API 59, 6.3.0/API 60, 6.2.1/API 59, 6.0.1/API 58 and 5.18.0/API 56. Set `RUNDECK_IMAGE` and `RUNDECK_API_VERSION` to run each target locally. Python compatibility tests retain 3.11–3.15 (3.15-rc Docker image).
503
+ - To exercise the 6.3.0 session regression and legacy metrics: `RUNDECK_PUBLIC_URL=https://rundeck.example.invalid RUNDECK_METRICS_LEGACY_ENABLED=true scripts/run-integration.sh`. The client still connects to `RUNDECK_URL` (default `http://localhost:4440`). The public URL defaults to the client URL, ensuring session redirects also work on older servers. Optional `RUNDECK_HTTP_PORT` changes the published port; set `RUNDECK_URL` accordingly.
476
504
  - The docker compose stack mounts `tokens.properties` at the repo root to seed a local admin token for integration tests. This file is only intended for local testing.
505
+ - Run local compatibility targets sequentially, stopping each stack before starting the next. This keeps at most one Rundeck JVM and one PostgreSQL container active.
477
506
 
478
507
  Code structure
479
508
  --------------
480
509
  - `src/rundeck/base.py`: generic objects/managers, `_build_path` helpers, pagination, CRUD.
481
510
  - `src/rundeck/client.py`: HTTP client, `http_get/post/put/delete/list` methods.
482
- - `src/rundeck/v1/objects/`: domain managers and objects (projects, jobs, executions, system, tokens, users, config_management).
483
- - Managers/objects schema: we apply the manager -> object pattern wherever the API exposes an identifiable resource (jobs, projects, users, tokens, executions, etc.). For purely global or utility endpoints without a resource (e.g., `/system` and its subdomains, `/config`, `/metrics`), we use dedicated sub-managers rather than forcing an artificial object.
511
+ - `src/rundeck/v1/objects/`: Community domain managers and objects (projects, jobs, executions, system, tokens, users, authorizations).
512
+ - Managers/objects schema: we apply the manager -> object pattern wherever the API exposes an identifiable resource (jobs, projects, users, tokens, executions, etc.). For global or utility endpoints without a resource (e.g., `/system`, `/scheduler`, `/metrics`), we use dedicated sub-managers rather than forcing an artificial object.
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "python-rundeck"
3
- version = "1.1.0"
4
- description = "Python client for the Rundeck API (v14-v58)."
3
+ version = "2.0.1"
4
+ description = "Python client for the Rundeck Community API (v14-v60, default v59)."
5
5
  authors = [
6
6
  {name = "Pascal Seckinger",email = "pascal.seckinger@protonmail.com"}
7
7
  ]
@@ -23,7 +23,7 @@ classifiers = [
23
23
  "Typing :: Typed",
24
24
  ]
25
25
  dependencies = [
26
- "requests (>=2.32.5,<3.0.0)"
26
+ "requests (>=2.33.0,<3.0.0)"
27
27
  ]
28
28
 
29
29
  [project.urls]
@@ -36,10 +36,10 @@ Changelog = "https://gitlab.com/pascal.seckinger/python-rundeck/-/blob/main/CHAN
36
36
  [project.optional-dependencies]
37
37
  dev = [
38
38
  "bandit (>=1.7.9,<2.0.0)",
39
- "black (>=25.11.0,<26.0.0)",
39
+ "black (>=26.3.1,<27.0.0)",
40
40
  "flake8 (>=7.0.0,<8.0.0)",
41
41
  "isort (>=7.0.0,<8.0.0)",
42
- "pytest (>=9.0.2,<10.0.0)",
42
+ "pytest (>=9.0.3,<10.0.0)",
43
43
  "pytest-cov (>=5.0.0,<6.0.0)",
44
44
  "ruff (>=0.7.0,<1.0.0)",
45
45
  "safety (>=3.2.0,<4.0.0)",
@@ -47,7 +47,11 @@ dev = [
47
47
 
48
48
  [tool.poetry]
49
49
  packages = [{include = "rundeck", from = "src"}]
50
- include = ["src/rundeck/py.typed"]
50
+ include = [
51
+ "src/rundeck/py.typed",
52
+ {path = "API_COVERAGE.md", format = "sdist"},
53
+ {path = "CHANGELOG.md", format = "sdist"},
54
+ ]
51
55
 
52
56
  [build-system]
53
57
  requires = ["poetry-core>=2.0.0,<3.0.0"]
@@ -56,10 +60,10 @@ build-backend = "poetry.core.masonry.api"
56
60
  [dependency-groups]
57
61
  dev = [
58
62
  "bandit (>=1.7.9,<2.0.0)",
59
- "black (>=25.11.0,<26.0.0)",
63
+ "black (>=26.3.1,<27.0.0)",
60
64
  "flake8 (>=7.0.0,<8.0.0)",
61
65
  "isort (>=7.0.0,<8.0.0)",
62
- "pytest (>=9.0.2,<10.0.0)",
66
+ "pytest (>=9.0.3,<10.0.0)",
63
67
  "pytest-cov (>=5.0.0,<6.0.0)",
64
68
  "ruff (>=0.7.0,<1.0.0)",
65
69
  "safety (>=3.2.0,<4.0.0)"
@@ -81,7 +81,7 @@ class Rundeck:
81
81
  def _init_managers(self) -> None:
82
82
  """Initialise les managers d'objets."""
83
83
  # Import paresseux pour éviter les boucles d'import.
84
- from rundeck.v1.objects.config_management import ConfigManagementManager
84
+ from rundeck.v1.objects.authorizations import AuthorizationsManager
85
85
  from rundeck.v1.objects.executions import ExecutionManager
86
86
  from rundeck.v1.objects.features import FeatureManager
87
87
  from rundeck.v1.objects.jobs import JobManager
@@ -98,7 +98,7 @@ class Rundeck:
98
98
  self.projects = ProjectManager(self)
99
99
  self.jobs = JobManager(self)
100
100
  self.executions = ExecutionManager(self)
101
- self.config_management = ConfigManagementManager(self)
101
+ self.authorizations = AuthorizationsManager(self)
102
102
  self.metrics = MetricsManager(self)
103
103
  self.monitoring = MonitoringManager(self)
104
104
  self.plugins = PluginManager(self)
@@ -110,6 +110,10 @@ class Rundeck:
110
110
  self.tokens = TokenManager(self)
111
111
  self.users = UserManager(self)
112
112
 
113
+ def api_info(self) -> dict[str, Any]:
114
+ """Récupère les informations de l'API utilisée par ce client."""
115
+ return self.http_get("/")
116
+
113
117
  @property
114
118
  def url(self) -> str:
115
119
  """URL fournie par l'utilisateur."""
@@ -2,9 +2,9 @@
2
2
  Constantes partagées pour le client Rundeck.
3
3
  """
4
4
 
5
+ import tomllib
5
6
  from importlib.metadata import PackageNotFoundError, version
6
7
  from pathlib import Path
7
- import tomllib
8
8
 
9
9
 
10
10
  # Package metadata
@@ -40,13 +40,14 @@ FORMAT_JSON = "json"
40
40
 
41
41
  # API versions. The library remains compatible with legacy Rundeck servers
42
42
  # using v14, while current Rundeck servers require at least v17.
43
- API_VERSION_CURRENT = "58"
43
+ API_VERSION_CURRENT = "60"
44
44
  API_VERSION_MIN = "14"
45
45
  API_VERSION_SERVER_MIN = "17"
46
46
  API_VERSION_DEPRECATION = "34"
47
47
 
48
48
  # Defaults
49
- DEFAULT_API_VERSION = API_VERSION_CURRENT
49
+ # v60 adds Enterprise endpoints only; keep the Community-compatible default.
50
+ DEFAULT_API_VERSION = "59"
50
51
  DEFAULT_TIMEOUT = 30
51
52
  USER_AGENT = "python-rundeck"
52
53
 
@@ -131,7 +131,7 @@ def raise_for_status(response: requests.Response) -> None:
131
131
  raise RundeckAuthenticationError(message, error_code, response)
132
132
  if code_lower == "api-version-unsupported":
133
133
  raise RundeckApiVersionUnsupportedError(message, error_code, response)
134
- if status == 400:
134
+ if status in (400, 422):
135
135
  raise RundeckValidationError(message, error_code, response)
136
136
  if status in (401, 403):
137
137
  raise RundeckAuthenticationError(message, error_code, response)
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from rundeck.utils import build_headers
6
+
7
+
8
+ class YamlPolicyMixin:
9
+ def _get_yaml(self, path: str) -> str:
10
+ response = self.rd.http_get(
11
+ path, raw=True, headers={"Accept": "application/yaml"}
12
+ )
13
+ # Some older project ACL controllers return a JSON envelope despite Accept.
14
+ if (
15
+ "application/json"
16
+ in getattr(response, "headers", {}).get("Content-Type", "").lower()
17
+ ):
18
+ result = response.json()
19
+ if isinstance(result, dict) and isinstance(result.get("contents"), str):
20
+ return result["contents"]
21
+ return response.text
22
+
23
+ def _yaml_headers(self) -> dict[str, str]:
24
+ return (
25
+ build_headers(accept="application/json", content_type="application/json")
26
+ or {}
27
+ )
28
+
29
+ def _post_yaml(self, path: str, content: Any) -> Any:
30
+ return self.rd.http_post(
31
+ path, json={"contents": content}, headers=self._yaml_headers()
32
+ )
33
+
34
+ def _put_yaml(self, path: str, content: Any) -> Any:
35
+ return self.rd.http_put(
36
+ path, json={"contents": content}, headers=self._yaml_headers()
37
+ )
@@ -0,0 +1,45 @@
1
+ """Endpoints expérimentaux d'évaluation des autorisations."""
2
+
3
+ from typing import Any
4
+
5
+ from rundeck.base import RundeckObjectManager
6
+
7
+
8
+ class AuthorizationsManager(RundeckObjectManager):
9
+ """Évalue les droits du sujet courant (API expérimentale)."""
10
+
11
+ _path = "/authorizations"
12
+
13
+ def _check(self, path: str, actions: list[str]) -> Any:
14
+ return self.rd.http_get(path, params={"actions": actions})
15
+
16
+ def application_kind(self, kind: str, actions: list[str]) -> Any:
17
+ """Vérifie des actions sur un type de ressource de l'application."""
18
+ return self._check(f"/authorizations/application/{kind}", actions)
19
+
20
+ def application_resource(
21
+ self, resource_type: str, specifier: str, actions: list[str]
22
+ ) -> Any:
23
+ """Vérifie des actions sur une ressource de l'application."""
24
+ return self._check(
25
+ f"/authorizations/application/{resource_type}/{specifier}", actions
26
+ )
27
+
28
+ def project_kind(self, project: str, kind: str, actions: list[str]) -> Any:
29
+ """Vérifie des actions sur un type de ressource du projet."""
30
+ return self._check(f"/authorizations/project/{project}/{kind}", actions)
31
+
32
+ def project_resource(
33
+ self, project: str, resource_type: str, specifier: str, actions: list[str]
34
+ ) -> Any:
35
+ """Vérifie des actions sur une ressource du projet."""
36
+ return self._check(
37
+ f"/authorizations/project/{project}/{resource_type}/{specifier}", actions
38
+ )
39
+
40
+ def project_job(self, project: str, job_id: str, actions: list[str]) -> Any:
41
+ """Vérifie des actions sur un job du projet."""
42
+ return self._check(f"/authorizations/project/{project}/job/{job_id}", actions)
43
+
44
+
45
+ __all__ = ["AuthorizationsManager"]
@@ -2,7 +2,6 @@
2
2
  Gestion des jobs Rundeck
3
3
  """
4
4
 
5
- import warnings
6
5
  from typing import Any, Iterator
7
6
 
8
7
  from rundeck.base import RundeckObject, RundeckObjectManager
@@ -114,16 +113,6 @@ class Job(RundeckObject):
114
113
  path = self._job_path("meta")
115
114
  return self.rd.http_get(path, params=params or None)
116
115
 
117
- def tags(self) -> list[str]:
118
- """Retourne les tags du job (commercial, endpoint /job/{id}/tags)."""
119
- warnings.warn(
120
- "Job.tags() cible un endpoint commercial et sera retiré en 2.0.",
121
- DeprecationWarning,
122
- stacklevel=2,
123
- )
124
- path = self._job_path("tags")
125
- return self.rd.http_get(path)
126
-
127
116
  def executions(
128
117
  self,
129
118
  status: str | None = None,
@@ -327,18 +316,42 @@ class JobManager(RundeckObjectManager[Job]):
327
316
  meta: str | None = None,
328
317
  meta_exclude: str | None = None,
329
318
  breakpoint: int | None = None,
319
+ use_post: bool = False,
330
320
  **filters: Any,
331
321
  ) -> dict[str, Any]:
332
322
  """Parcourt un groupe de jobs et ses sous-groupes (API v46+)."""
333
323
  project_path = self._project_path(project)
334
- params = compact(
324
+ query = compact(meta=meta, metaExclude=meta_exclude, breakpoint=breakpoint)
325
+ browse_path = f"{project_path}/browse"
326
+ if use_post:
327
+ if path is not None:
328
+ query["path"] = path
329
+ return self.rd.http_post(browse_path, params=query or None, json=filters)
330
+ query = compact(
335
331
  filters,
336
332
  path=path,
337
333
  meta=meta,
338
334
  metaExclude=meta_exclude,
339
335
  breakpoint=breakpoint,
340
336
  )
341
- return self.rd.http_get(f"{project_path}/browse", params=params or None)
337
+ return self.rd.http_get(browse_path, params=query or None)
338
+
339
+ def validate_option(
340
+ self,
341
+ option_data: dict[str, Any],
342
+ *,
343
+ job_was_scheduled: bool,
344
+ project: str | None = None,
345
+ ) -> Any:
346
+ """Valide une option de job (API v47+)."""
347
+ project_name = project or (self.parent.id if self.parent else None)
348
+ if not project_name:
349
+ raise ValueError("Le paramètre project est requis")
350
+ return self.rd.http_post(
351
+ f"/project/{project_name}/jobs/validateOption",
352
+ params={"jobWasScheduled": job_was_scheduled},
353
+ json=option_data,
354
+ )
342
355
 
343
356
  def get(self, id: str) -> "Job":
344
357
  """Récupère un job par identifiant."""
@@ -65,6 +65,10 @@ class MonitoringManager(RundeckObjectManager):
65
65
  """Récupère l'état de santé de l'application."""
66
66
  return self.rd.http_get(self._monitoring_path("health"))
67
67
 
68
+ def readiness(self) -> dict[str, Any]:
69
+ """Récupère l'état de disponibilité pour les sondes de readiness."""
70
+ return self.rd.http_get(self._monitoring_path("health/readiness"))
71
+
68
72
  def info(self) -> dict[str, Any]:
69
73
  """Récupère les informations applicatives."""
70
74
  return self.rd.http_get(self._monitoring_path("info"))
@@ -0,0 +1,29 @@
1
+ """Configuration des plugins au niveau d'un projet."""
2
+
3
+ from typing import Any
4
+
5
+ from rundeck.base import RundeckObjectManager
6
+
7
+
8
+ class ProjectPluginsManager(RundeckObjectManager):
9
+ """Sauvegarde les configurations de plugins d'un projet (API v55+)."""
10
+
11
+ _path = "/project/{parent}/plugins"
12
+
13
+ def save(
14
+ self,
15
+ service_name: str,
16
+ config_prefix: str,
17
+ plugins: list[dict[str, Any]],
18
+ removed_plugins: list[dict[str, Any]] | None = None,
19
+ ) -> Any:
20
+ """Crée, modifie ou retire des configurations de plugins projet."""
21
+ path = self._build_path("save")
22
+ return self.rd.http_post(
23
+ path,
24
+ params={"serviceName": service_name, "configPrefix": config_prefix},
25
+ json={"plugins": plugins, "removedPlugins": removed_plugins or []},
26
+ )
27
+
28
+
29
+ __all__ = ["ProjectPluginsManager"]
@@ -10,6 +10,7 @@ from rundeck.v1.objects.acl import YamlPolicyMixin
10
10
  from rundeck.v1.objects.adhoc import AdhocManager
11
11
  from rundeck.v1.objects.executions import ExecutionManager
12
12
  from rundeck.v1.objects.jobs import JobManager
13
+ from rundeck.v1.objects.project_plugins import ProjectPluginsManager
13
14
  from rundeck.v1.objects.scm import ProjectSCMManager
14
15
  from rundeck.v1.objects.webhooks import ProjectWebhookManager
15
16
 
@@ -65,6 +66,11 @@ class Project(RundeckObject):
65
66
  """Accès aux opérations SCM du projet (import/export)."""
66
67
  return ProjectSCMManager(self.rd, parent=self)
67
68
 
69
+ @property
70
+ def plugins(self) -> ProjectPluginsManager:
71
+ """Accès à la configuration des plugins du projet."""
72
+ return ProjectPluginsManager(self.rd, parent=self)
73
+
68
74
  @property
69
75
  def webhooks(self) -> ProjectWebhookManager:
70
76
  """Accès aux webhooks du projet."""
@@ -187,8 +193,7 @@ class ProjectACLManager(YamlPolicyMixin, RundeckObjectManager):
187
193
  def get(self, policy_name: str) -> str:
188
194
  """Récupère une politique ACL projet (contenu texte)."""
189
195
  path = self._build_path(policy_name)
190
- response = self.rd.http_get(path, raw=True)
191
- return response.text
196
+ return self._get_yaml(path)
192
197
 
193
198
  def create(self, policy_name: str, content: str) -> dict[str, Any]:
194
199
  """Crée une politique ACL projet."""
@@ -429,9 +434,25 @@ class ProjectArchiveManager(RundeckObjectManager):
429
434
  )
430
435
  return self.rd.http_get(path, params=params or None, raw=True)
431
436
 
432
- def export_async(self, **params: Any) -> Any:
433
- """Lance une export async, retourne le token."""
437
+ def export_async(
438
+ self,
439
+ *,
440
+ execution_ids: list[str] | None = None,
441
+ use_post: bool = False,
442
+ **params: Any,
443
+ ) -> Any:
444
+ """Lance une export async, en POST pour les longues listes d'IDs."""
434
445
  path = self._build_path("export/async")
446
+ if use_post:
447
+ form = {"executionIds": execution_ids} if execution_ids else None
448
+ return self.rd.http_post(
449
+ path,
450
+ params=params or None,
451
+ data=form,
452
+ headers=build_headers(content_type="application/x-www-form-urlencoded"),
453
+ )
454
+ if execution_ids:
455
+ params["executionIds"] = execution_ids
435
456
  return self.rd.http_get(path, params=params or None)
436
457
 
437
458
  def export_status(self, token: str) -> Any:
@@ -460,6 +481,9 @@ class ProjectArchiveManager(RundeckObjectManager):
460
481
  ) -> Any:
461
482
  """
462
483
  Importe une archive ZIP dans le projet.
484
+
485
+ Community 6.3+ exige aussi le droit configure lorsque importConfig ou
486
+ importNodesSources est activé. Les options et refus sont transmis tels quels.
463
487
  """
464
488
  params = compact(
465
489
  component_options,
@@ -2,7 +2,6 @@
2
2
  Gestion des endpoints /scheduler
3
3
  """
4
4
 
5
- import warnings
6
5
  from typing import Any
7
6
 
8
7
  from rundeck.base import RundeckObjectManager
@@ -20,24 +19,24 @@ class SchedulerManager(RundeckObjectManager):
20
19
  all_servers: bool = False,
21
20
  project: str | None = None,
22
21
  job_id: str | None = None,
22
+ job_ids: list[str] | None = None,
23
23
  ) -> dict[str, Any]:
24
24
  """
25
- Reprend le planning des jobs en mode cluster.
25
+ Reprend le planning des jobs. Sur un serveur sans mode cluster,
26
+ l'API est disponible, mais aucun planning distant n'est transféré.
26
27
 
27
28
  Args:
28
29
  server_uuid: UUID du serveur cible.
29
30
  all_servers: True pour reprendre tous les serveurs.
30
31
  project: Projet concerné (optionnel).
31
32
  job_id: Job spécifique (optionnel).
33
+ job_ids: Liste de jobs (exclusif avec job_id).
32
34
 
33
35
  Returns:
34
36
  Résultat de la reprise.
35
37
  """
36
- warnings.warn(
37
- "Les opérations Scheduler Cluster sont commerciales et seront retirées en 2.0.",
38
- DeprecationWarning,
39
- stacklevel=2,
40
- )
38
+ if job_id is not None and job_ids is not None:
39
+ raise ValueError("job_id et job_ids ne peuvent pas être utilisés ensemble")
41
40
  path = self._build_path("takeover")
42
41
 
43
42
  server = None
@@ -50,9 +49,19 @@ class SchedulerManager(RundeckObjectManager):
50
49
  server=server,
51
50
  project=project if project else None,
52
51
  job={"id": job_id} if job_id else None,
52
+ jobs=[{"id": item} for item in job_ids] if job_ids else None,
53
53
  )
54
54
 
55
55
  return self.rd.http_put(path, json=data)
56
56
 
57
+ def jobs(self, server_uuid: str | None = None) -> Any:
58
+ """Liste les jobs planifiés sur le serveur courant ou le serveur donné."""
59
+ path = (
60
+ f"/scheduler/server/{server_uuid}/jobs"
61
+ if server_uuid
62
+ else "/scheduler/jobs"
63
+ )
64
+ return self.rd.http_get(path)
65
+
57
66
 
58
67
  __all__ = ["SchedulerManager"]
@@ -67,8 +67,7 @@ class SystemACLManager(YamlPolicyMixin, RundeckObjectManager[SystemACL]):
67
67
  def get(self, policy_name: str) -> SystemACL:
68
68
  """Récupère une politique ACL (contenu texte)."""
69
69
  path = self._build_path(policy_name)
70
- response = self.rd.http_get(path, raw=True)
71
- return self._wrap({"name": policy_name, "content": response.text})
70
+ return self._wrap({"name": policy_name, "content": self._get_yaml(path)})
72
71
 
73
72
  def create(self, policy_name: str, content: str) -> SystemACL:
74
73
  """Crée une politique ACL système."""
@@ -143,3 +142,7 @@ class SystemManager(RundeckObjectManager):
143
142
  """
144
143
  path = self._build_path("info")
145
144
  return self.rd.http_get(path)
145
+
146
+ def summary(self) -> dict[str, Any]:
147
+ """Récupère le résumé des exécutions et projets."""
148
+ return self.rd.http_get("/home/summary")
@@ -100,3 +100,7 @@ class TokenManager(RundeckObjectManager):
100
100
  """
101
101
  path = self._token_path(token_id)
102
102
  self._delete(token_id, path=path)
103
+
104
+ def remove_expired(self, user: str) -> Any:
105
+ """Supprime les tokens expirés d'un utilisateur ou de tous (``*``)."""
106
+ return self.rd.http_post(self._build_path(f"{user}/removeExpired"))
@@ -1,19 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
- from rundeck.utils import build_headers
6
-
7
-
8
- class YamlPolicyMixin:
9
- _yaml_content_type = "application/yaml"
10
-
11
- def _yaml_headers(self) -> dict[str, str]:
12
- headers = build_headers(content_type=self._yaml_content_type)
13
- return headers or {"Content-Type": self._yaml_content_type}
14
-
15
- def _post_yaml(self, path: str, content: Any) -> Any:
16
- return self.rd.http_post(path, data=content, headers=self._yaml_headers())
17
-
18
- def _put_yaml(self, path: str, content: Any) -> Any:
19
- return self.rd.http_put(path, data=content, headers=self._yaml_headers())
@@ -1,64 +0,0 @@
1
- """Gestion de la configuration Rundeck (instance globale)."""
2
-
3
- from __future__ import annotations
4
-
5
- import warnings
6
-
7
- from rundeck.base import RundeckObjectManager
8
-
9
-
10
- class ConfigManagementManager(RundeckObjectManager):
11
- """
12
- Gestion des configurations globales (plugin/custom) via /config.
13
- """
14
-
15
- _path = "/config"
16
-
17
- @staticmethod
18
- def _warn_commercial() -> None:
19
- warnings.warn(
20
- "Config Management est une surface commerciale et sera retirée en 2.0.",
21
- DeprecationWarning,
22
- stacklevel=3,
23
- )
24
-
25
- def list(self) -> list[dict[str, str]]:
26
- """Liste toutes les configs et propriétés."""
27
- self._warn_commercial()
28
- path = self._build_path("list")
29
- return self.rd.http_get(path)
30
-
31
- def save(self, entries: list[dict[str, str]]) -> dict[str, str | list[str]]:
32
- """
33
- Crée ou met à jour des configs (payload: liste de dicts {key, value, strata?}).
34
- """
35
- self._warn_commercial()
36
- path = self._build_path("save")
37
- return self.rd.http_post(path, json=entries)
38
-
39
- def delete(self, key: str, strata: str | None = None) -> None:
40
- """
41
- Supprime une config via /config/delete avec payload JSON.
42
- """
43
- self._warn_commercial()
44
- payload: dict[str, str] = {"key": key}
45
- if strata:
46
- payload["strata"] = strata
47
- path = self._build_path("delete")
48
- self.rd.http_delete(path, json=payload)
49
-
50
- def refresh(self) -> dict[str, str]:
51
- """
52
- Recharge les configurations depuis les fichiers de propriétés.
53
- """
54
- self._warn_commercial()
55
- path = self._build_path("refresh")
56
- return self.rd.http_post(path)
57
-
58
- def restart(self) -> dict[str, str]:
59
- """
60
- Redémarre le serveur Rundeck.
61
- """
62
- self._warn_commercial()
63
- path = self._build_path("restart")
64
- return self.rd.http_post(path)
File without changes