python-rundeck 0.2.0__tar.gz → 0.3.0__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.
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/PKG-INFO +130 -127
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/README.md +129 -126
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/pyproject.toml +1 -1
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/LICENSE +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/__init__.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/base.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/client.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/config.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/const.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/exceptions.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/py.typed +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/__init__.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/__init__.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/adhoc.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/config_management.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/executions.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/features.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/jobs.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/key_storage.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/metrics.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/plugins.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/projects.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/scheduler.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/scm.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/system.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/tokens.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/users.py +0 -0
- {python_rundeck-0.2.0 → python_rundeck-0.3.0}/src/rundeck/v1/objects/webhooks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-rundeck
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Python client for the Rundeck API (v14-v56).
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -38,44 +38,46 @@ Description-Content-Type: text/markdown
|
|
|
38
38
|
|
|
39
39
|
python-rundeck
|
|
40
40
|
===============
|
|
41
|
+

|
|
42
|
+

|
|
43
|
+

|
|
44
|
+

|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
Python client for the Rundeck API (v14-v56), inspired by the python-gitlab architecture. Provides typed managers for key resources (projects, jobs, executions, tokens, users, system, configuration) and SCM (import/export).
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
Contents
|
|
45
49
|
--------
|
|
46
50
|
- Installation
|
|
47
|
-
-
|
|
51
|
+
- Quick start
|
|
48
52
|
- Configuration
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
+
- Available resources
|
|
54
|
+
- Examples by resource
|
|
55
|
+
- Error handling
|
|
56
|
+
- Development and testing
|
|
53
57
|
|
|
54
58
|
Installation
|
|
55
59
|
------------
|
|
56
|
-
|
|
60
|
+
Prerequisites: Python 3.11+. Install from PyPI.
|
|
57
61
|
|
|
58
62
|
```bash
|
|
59
|
-
|
|
60
|
-
# ou en editable avec pip
|
|
61
|
-
pip install -e .
|
|
63
|
+
pip install python-rundeck
|
|
62
64
|
```
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
Quick start
|
|
67
|
+
-----------
|
|
66
68
|
```python
|
|
67
69
|
from rundeck.client import Rundeck
|
|
68
70
|
|
|
69
71
|
rd = Rundeck(url="https://rundeck.example.com", token="MY_TOKEN", api_version=56)
|
|
70
|
-
#
|
|
72
|
+
# Password auth (session cookie) if no token
|
|
71
73
|
rd = Rundeck(url="https://rundeck.example.com", username="admin", password="admin", api_version=56)
|
|
72
74
|
|
|
73
|
-
#
|
|
75
|
+
# List projects
|
|
74
76
|
projects = rd.projects.list()
|
|
75
77
|
for p in projects:
|
|
76
78
|
print(p.name)
|
|
77
79
|
|
|
78
|
-
#
|
|
80
|
+
# Get a project and run a job
|
|
79
81
|
project = rd.projects.get("demo")
|
|
80
82
|
jobs = project.jobs.list()
|
|
81
83
|
execu = jobs[0].run()
|
|
@@ -84,64 +86,64 @@ print(execu.id)
|
|
|
84
86
|
|
|
85
87
|
Configuration
|
|
86
88
|
-------------
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
- `url
|
|
91
|
-
- `token
|
|
92
|
-
- `username` / `password
|
|
93
|
-
- `api_version
|
|
94
|
-
- `timeout
|
|
95
|
-
- `ssl_verify
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
- `RUNDECK_URL
|
|
100
|
-
- `RUNDECK_TOKEN
|
|
101
|
-
- `RUNDECK_USERNAME` / `RUNDECK_PASSWORD
|
|
102
|
-
- `RUNDECK_API_VERSION
|
|
103
|
-
- `RUNDECK_TIMEOUT
|
|
104
|
-
- `RUNDECK_SSL_VERIFY
|
|
105
|
-
- `RUNDECK_USER_AGENT
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
- `projects` (`ProjectManager`)
|
|
110
|
-
- `jobs` (`JobManager`)
|
|
111
|
-
- `executions` (`ExecutionManager`)
|
|
112
|
-
- `tokens` (`TokenManager`)
|
|
113
|
-
- `users` (`UserManager`)
|
|
114
|
-
- `metrics` (`MetricsManager`)
|
|
115
|
-
- `plugins` (`PluginManager`)
|
|
116
|
-
- `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`)
|
|
117
|
-
- `key_storage` (`StorageKeyManager`)
|
|
118
|
-
- `adhoc` (`AdhocManager`, via `project.adhoc`)
|
|
119
|
-
- `system` (`SystemManager`)
|
|
120
|
-
- `config_management` (`ConfigManagementManager`)
|
|
121
|
-
- `scm` (via `project.scm`
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
89
|
+
Configuration follows a cascading model (args > env > files > defaults) via `RundeckConfig`.
|
|
90
|
+
|
|
91
|
+
Main parameters:
|
|
92
|
+
- `url`: Rundeck URL (e.g. `https://rundeck.example.com`)
|
|
93
|
+
- `token`: API token (header `X-Rundeck-Auth-Token`)
|
|
94
|
+
- `username` / `password`: Session authentication (j_security_check) if no token is provided.
|
|
95
|
+
- `api_version`: API version (e.g. `56`)
|
|
96
|
+
- `timeout`: Request timeout (float, seconds)
|
|
97
|
+
- `ssl_verify`: TLS verification (bool or CA path)
|
|
98
|
+
|
|
99
|
+
Config files: if needed, pass `config_files` or `Rundeck.from_config(config_section=...)`.
|
|
100
|
+
Useful env vars (client config):
|
|
101
|
+
- `RUNDECK_URL`: Base URL
|
|
102
|
+
- `RUNDECK_TOKEN`: API token (header `X-Rundeck-Auth-Token`)
|
|
103
|
+
- `RUNDECK_USERNAME` / `RUNDECK_PASSWORD`: Session auth (if no token)
|
|
104
|
+
- `RUNDECK_API_VERSION`: API version (e.g. 56)
|
|
105
|
+
- `RUNDECK_TIMEOUT`: Request timeout (seconds)
|
|
106
|
+
- `RUNDECK_SSL_VERIFY`: TLS verification (bool or CA path)
|
|
107
|
+
- `RUNDECK_USER_AGENT`: HTTP User-Agent
|
|
108
|
+
|
|
109
|
+
Available resources
|
|
110
|
+
-------------------
|
|
111
|
+
- `projects` (`ProjectManager`): CRUD projects, job export/import, project config, archive (export/import).
|
|
112
|
+
- `jobs` (`JobManager`): list, get, deletions, bulk actions, execution.
|
|
113
|
+
- `executions` (`ExecutionManager`): list/filter, running, get/delete, advanced query.
|
|
114
|
+
- `tokens` (`TokenManager`): list, get, create, delete.
|
|
115
|
+
- `users` (`UserManager`): user operations (per current implementation).
|
|
116
|
+
- `metrics` (`MetricsManager`): `/metrics` endpoints (list/data/healthcheck/ping).
|
|
117
|
+
- `plugins` (`PluginManager`): list installed plugins (`/plugin/list`).
|
|
118
|
+
- `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`): project webhook management and sending via token.
|
|
119
|
+
- `key_storage` (`StorageKeyManager`): key storage management `/storage/keys`.
|
|
120
|
+
- `adhoc` (`AdhocManager`, via `project.adhoc`): run AdHoc commands/scripts.
|
|
121
|
+
- `system` (`SystemManager`): system info, executions enable/disable, logstorage, ACL.
|
|
122
|
+
- `config_management` (`ConfigManagementManager`): global configuration `/config`.
|
|
123
|
+
- `scm` (via `project.scm` and `job.scm`): import/export plugins, setup, enable/disable, status, actions (commit/import/export...).
|
|
124
|
+
|
|
125
|
+
Examples by resource (complete)
|
|
126
|
+
-------------------------------
|
|
127
|
+
Projects
|
|
126
128
|
```python
|
|
127
|
-
# CRUD
|
|
129
|
+
# CRUD project
|
|
128
130
|
p = rd.projects.create("demo")
|
|
129
131
|
p = rd.projects.get("demo")
|
|
130
132
|
rd.projects.delete("demo")
|
|
131
133
|
projects = rd.projects.list()
|
|
132
134
|
|
|
133
|
-
# Export / import
|
|
135
|
+
# Export / import jobs from a project (via job manager)
|
|
134
136
|
project.jobs.export(format="json", idlist="id1,id2", groupPath="group/sub")
|
|
135
137
|
project.jobs.import_jobs(content=open("jobs.json").read(), format="json", dupeOption="update", uuidOption="remove")
|
|
136
138
|
|
|
137
|
-
#
|
|
139
|
+
# Project export / import (ZIP archive)
|
|
138
140
|
archive = project.archive
|
|
139
|
-
resp = archive.export(export_all=False, export_webhooks=True) #
|
|
141
|
+
resp = archive.export(export_all=False, export_webhooks=True) # Raw response (zip)
|
|
140
142
|
token_info = archive.export_async(exportAll=True)
|
|
141
143
|
status = archive.export_status(token_info.get("token", ""))
|
|
142
144
|
zip_resp = archive.export_download(token_info.get("token", ""))
|
|
143
145
|
|
|
144
|
-
#
|
|
146
|
+
# Archive import (sync or async)
|
|
145
147
|
archive.import_archive(
|
|
146
148
|
content=open("project-export.zip", "rb").read(),
|
|
147
149
|
jobUuidOption="preserve",
|
|
@@ -151,16 +153,16 @@ archive.import_archive(
|
|
|
151
153
|
archive.import_archive(content=open("project-export.zip", "rb").read(), async_import=True)
|
|
152
154
|
archive.import_status()
|
|
153
155
|
|
|
154
|
-
#
|
|
155
|
-
project.readme.get_readme() #
|
|
156
|
+
# Project README / MOTD
|
|
157
|
+
project.readme.get_readme() # default text
|
|
156
158
|
project.readme.get_readme(accept="application/json")
|
|
157
|
-
project.readme.update_readme("
|
|
159
|
+
project.readme.update_readme("New content", content_type="text/plain")
|
|
158
160
|
project.readme.delete_readme()
|
|
159
161
|
project.readme.get_motd()
|
|
160
|
-
project.readme.update_motd("Message
|
|
162
|
+
project.readme.update_motd("Message of the day", content_type="text/plain")
|
|
161
163
|
project.readme.delete_motd()
|
|
162
164
|
|
|
163
|
-
#
|
|
165
|
+
# Project config (key/value)
|
|
164
166
|
conf = p.config.get()
|
|
165
167
|
p.config.keys.get("project.label")
|
|
166
168
|
p.config.keys.set("project.label", "Demo")
|
|
@@ -168,27 +170,27 @@ p.config.keys.update({"project.description": "Sample"})
|
|
|
168
170
|
p.config.replace({"project.label": "Demo", "project.description": "Sample"})
|
|
169
171
|
p.config.keys.delete("project.label")
|
|
170
172
|
|
|
171
|
-
# SCM import/export (
|
|
172
|
-
scm_import = project.scm.import_ #
|
|
173
|
+
# SCM import/export (on a project)
|
|
174
|
+
scm_import = project.scm.import_ # or getattr(project.scm, "import")
|
|
173
175
|
scm_export = project.scm.export
|
|
174
176
|
|
|
175
|
-
#
|
|
177
|
+
# Plugin discovery
|
|
176
178
|
scm_import.plugins.list()
|
|
177
179
|
scm_export.plugins.list()
|
|
178
180
|
|
|
179
|
-
#
|
|
181
|
+
# Input fields for a plugin and setup (explicit plugin_type)
|
|
180
182
|
fields = scm_import.plugins.input_fields("git-import")
|
|
181
183
|
scm_import.config.setup("git-import", {"url": "ssh://git@example.com/repo.git", "dir": "/tmp/repo"})
|
|
182
184
|
|
|
183
|
-
#
|
|
185
|
+
# Enable/disable a plugin
|
|
184
186
|
scm_import.config.enable("git-import")
|
|
185
187
|
scm_export.config.disable("custom-export")
|
|
186
188
|
|
|
187
|
-
#
|
|
189
|
+
# SCM status/config
|
|
188
190
|
import_status = scm_import.actions.status()
|
|
189
191
|
export_conf = scm_export.config.get()
|
|
190
192
|
|
|
191
|
-
#
|
|
193
|
+
# Project-side SCM actions (e.g., commit/pull/push depending on plugin)
|
|
192
194
|
action_fields = scm_export.actions.input_fields("commit")
|
|
193
195
|
scm_export.actions.perform(
|
|
194
196
|
"commit",
|
|
@@ -201,14 +203,14 @@ scm_export.actions.perform(
|
|
|
201
203
|
|
|
202
204
|
Jobs
|
|
203
205
|
```python
|
|
204
|
-
#
|
|
206
|
+
# List jobs for a project
|
|
205
207
|
jobs = rd.jobs.list(project="demo", groupPath="ops")
|
|
206
208
|
|
|
207
|
-
#
|
|
209
|
+
# From a parent project
|
|
208
210
|
project = rd.projects.get("demo")
|
|
209
211
|
jobs = project.jobs.list()
|
|
210
212
|
|
|
211
|
-
#
|
|
213
|
+
# Direct access to a job
|
|
212
214
|
job = rd.jobs.get("job-id")
|
|
213
215
|
job.delete()
|
|
214
216
|
job.definition(format="yaml")
|
|
@@ -223,7 +225,7 @@ tags = job.tags()
|
|
|
223
225
|
workflow = job.workflow()
|
|
224
226
|
forecast = job.forecast(time="2024-05-01T10:00:00Z", max=5)
|
|
225
227
|
|
|
226
|
-
#
|
|
228
|
+
# Export/import jobs via manager (project parameter or parent)
|
|
227
229
|
rd.jobs.export(project="demo", format="xml", idlist="id1,id2", groupPath="group")
|
|
228
230
|
rd.jobs.import_jobs(
|
|
229
231
|
project="demo",
|
|
@@ -231,30 +233,30 @@ rd.jobs.import_jobs(
|
|
|
231
233
|
fileformat="xml",
|
|
232
234
|
dupeOption="update",
|
|
233
235
|
)
|
|
234
|
-
#
|
|
236
|
+
# Or via a parent project
|
|
235
237
|
project.jobs.export(format="json")
|
|
236
238
|
project.jobs.import_jobs(content=open("jobs.json", "rb").read(), fileformat="json")
|
|
237
239
|
|
|
238
|
-
Note:
|
|
240
|
+
Note: import remains exposed as `import_jobs(...)` (the Python keyword prevents a direct call to `.import`). If you prefer the alias, use `getattr(rd.jobs, "import")(...)`.
|
|
239
241
|
|
|
240
|
-
#
|
|
242
|
+
# Run and get the execution
|
|
241
243
|
execution = job.run(argString="-option value")
|
|
242
244
|
|
|
243
|
-
#
|
|
245
|
+
# Bulk actions
|
|
244
246
|
rd.jobs.bulk.enable_execution(["id1", "id2"])
|
|
245
247
|
rd.jobs.bulk.disable_execution(["id1", "id2"])
|
|
246
248
|
rd.jobs.bulk.delete(["id1", "id2"])
|
|
247
249
|
rd.jobs.bulk.enable_schedule(["id1", "id2"])
|
|
248
250
|
rd.jobs.bulk.disable_schedule(["id1", "id2"])
|
|
249
251
|
|
|
250
|
-
# Upload
|
|
252
|
+
# Upload option files and uploaded files
|
|
251
253
|
job.upload_option_file("csvfile", open("data.csv", "rb").read(), file_name="data.csv")
|
|
252
254
|
job.list_uploaded_files(max=20)
|
|
253
255
|
rd.jobs.get_uploaded_file_info("file-id")
|
|
254
256
|
|
|
255
|
-
# SCM import/export
|
|
257
|
+
# SCM import/export on a job
|
|
256
258
|
job_scm_export = job.scm.export
|
|
257
|
-
job_scm_import = job.scm.import_ #
|
|
259
|
+
job_scm_import = job.scm.import_ # or getattr(job.scm, "import")
|
|
258
260
|
|
|
259
261
|
job_scm_export.status()
|
|
260
262
|
job_scm_export.diff()
|
|
@@ -264,7 +266,7 @@ job_scm_import.status()
|
|
|
264
266
|
job_scm_import.input_fields("pull")
|
|
265
267
|
job_scm_import.perform("pull", input_values={"message": "Update from repo"})
|
|
266
268
|
|
|
267
|
-
#
|
|
269
|
+
# Project resources
|
|
268
270
|
resources = project.resources.list(format="json", groupPath="ops")
|
|
269
271
|
node = project.resources.get("node1")
|
|
270
272
|
sources = project.sources.list()
|
|
@@ -278,18 +280,18 @@ project.acl.update("policy.aclpolicy", content="...yaml...")
|
|
|
278
280
|
project.acl.delete("policy.aclpolicy")
|
|
279
281
|
```
|
|
280
282
|
|
|
281
|
-
|
|
283
|
+
Executions
|
|
282
284
|
```python
|
|
283
|
-
#
|
|
285
|
+
# Simple or paginated list
|
|
284
286
|
execs = rd.executions.list(project="demo", status="running", max=50, offset=0)
|
|
285
|
-
running = rd.executions.running(project="demo") #
|
|
287
|
+
running = rd.executions.running(project="demo") # or "*" for all
|
|
286
288
|
|
|
287
|
-
#
|
|
289
|
+
# Details / deletion
|
|
288
290
|
e = rd.executions.get("123")
|
|
289
291
|
rd.executions.delete("123")
|
|
290
292
|
rd.executions.bulk_delete(["123", "124"])
|
|
291
293
|
|
|
292
|
-
#
|
|
294
|
+
# Advanced query
|
|
293
295
|
advanced = rd.executions.query(
|
|
294
296
|
project="demo",
|
|
295
297
|
statusFilter="failed",
|
|
@@ -299,12 +301,12 @@ advanced = rd.executions.query(
|
|
|
299
301
|
max=100,
|
|
300
302
|
)
|
|
301
303
|
|
|
302
|
-
#
|
|
304
|
+
# Execution methods
|
|
303
305
|
e.abort(asUser="admin")
|
|
304
306
|
output = e.get_output(offset=0, maxlines=100)
|
|
305
307
|
state = e.get_state()
|
|
306
308
|
is_running = e.is_running()
|
|
307
|
-
e.refresh() #
|
|
309
|
+
e.refresh() # reload data
|
|
308
310
|
```
|
|
309
311
|
|
|
310
312
|
Tokens
|
|
@@ -316,20 +318,20 @@ new_token = rd.tokens.create(user="alice", roles=["admin"], duration="90d", name
|
|
|
316
318
|
rd.tokens.delete(new_token.id)
|
|
317
319
|
```
|
|
318
320
|
|
|
319
|
-
|
|
321
|
+
Users
|
|
320
322
|
```python
|
|
321
323
|
users = rd.users.list()
|
|
322
324
|
me = rd.users.get_current()
|
|
323
325
|
u = rd.users.get("bob")
|
|
324
326
|
roles = rd.users.current_roles()
|
|
325
327
|
|
|
326
|
-
#
|
|
328
|
+
# Update via manager or object
|
|
327
329
|
u = rd.users.update("bob", firstName="Bob", lastName="Builder", email="bob@example.com")
|
|
328
|
-
u.roles() # via
|
|
329
|
-
u.update(email="new@example.com") #
|
|
330
|
+
u.roles() # via the object
|
|
331
|
+
u.update(email="new@example.com") # updates and refreshes the object
|
|
330
332
|
```
|
|
331
333
|
|
|
332
|
-
|
|
334
|
+
System
|
|
333
335
|
```python
|
|
334
336
|
system = rd.system
|
|
335
337
|
info = system.info()
|
|
@@ -339,7 +341,7 @@ system.logstorage.info()
|
|
|
339
341
|
system.logstorage.incomplete(max=50, offset=0)
|
|
340
342
|
system.logstorage.incomplete_resume()
|
|
341
343
|
|
|
342
|
-
#
|
|
344
|
+
# Execution mode (sub-manager)
|
|
343
345
|
system.executions.enable()
|
|
344
346
|
system.executions.disable()
|
|
345
347
|
system.executions.status()
|
|
@@ -356,7 +358,7 @@ rd.scheduler.takeover(all_servers=True)
|
|
|
356
358
|
rd.scheduler.takeover(server_uuid="uuid-123", project="demo", job_id="job-1")
|
|
357
359
|
```
|
|
358
360
|
|
|
359
|
-
|
|
361
|
+
Metrics
|
|
360
362
|
```python
|
|
361
363
|
metrics = rd.metrics
|
|
362
364
|
metrics.list()
|
|
@@ -364,16 +366,16 @@ metrics.data()
|
|
|
364
366
|
metrics.healthcheck()
|
|
365
367
|
metrics.ping()
|
|
366
368
|
|
|
367
|
-
#
|
|
369
|
+
# Installed plugins
|
|
368
370
|
plugins = rd.plugins.list()
|
|
369
371
|
for plugin in plugins:
|
|
370
372
|
print(plugin.name, plugin.service)
|
|
371
373
|
|
|
372
|
-
#
|
|
374
|
+
# Plugin details
|
|
373
375
|
first = plugins[0]
|
|
374
376
|
detail = rd.plugins.detail(first.service, first.name)
|
|
375
377
|
|
|
376
|
-
#
|
|
378
|
+
# Project webhooks and send
|
|
377
379
|
project = rd.projects.get("demo")
|
|
378
380
|
wh = project.webhooks
|
|
379
381
|
wh.create(
|
|
@@ -393,9 +395,9 @@ wh.delete(first_hook.id)
|
|
|
393
395
|
|
|
394
396
|
# Key storage (/storage/keys)
|
|
395
397
|
ks = rd.key_storage
|
|
396
|
-
#
|
|
398
|
+
# Create a secret (password)
|
|
397
399
|
ks.create("integration/secret1", content="s3cr3t", content_type="application/x-rundeck-data-password")
|
|
398
|
-
resources = ks.list() #
|
|
400
|
+
resources = ks.list() # root listing
|
|
399
401
|
meta = ks.get_metadata("integration/secret1")
|
|
400
402
|
ks.delete("integration/secret1")
|
|
401
403
|
|
|
@@ -403,17 +405,17 @@ ks.delete("integration/secret1")
|
|
|
403
405
|
project = rd.projects.get("demo")
|
|
404
406
|
exec1 = project.adhoc.run_command("echo 'hello world'")
|
|
405
407
|
exec2 = project.adhoc.run_script("echo 'from script'")
|
|
406
|
-
# exec2.id
|
|
407
|
-
#
|
|
408
|
+
# exec2.id to track execution
|
|
409
|
+
# Stub mode (no immediate refresh) then manual refresh
|
|
408
410
|
exec_stub = project.adhoc.run_command("echo stub", refresh=False)
|
|
409
|
-
exec_stub.refresh() #
|
|
411
|
+
exec_stub.refresh() # load the full object
|
|
410
412
|
# Script via multipart (upload)
|
|
411
413
|
exec3 = project.adhoc.run_script(
|
|
412
414
|
script_file=("hello.sh", "echo multipart", "text/plain"),
|
|
413
415
|
refresh=False,
|
|
414
416
|
)
|
|
415
417
|
|
|
416
|
-
#
|
|
418
|
+
# System features
|
|
417
419
|
features = rd.features.list()
|
|
418
420
|
if features:
|
|
419
421
|
first = features[0]
|
|
@@ -421,7 +423,7 @@ if features:
|
|
|
421
423
|
print(first.name, status.enabled)
|
|
422
424
|
```
|
|
423
425
|
|
|
424
|
-
|
|
426
|
+
Global configuration `/config`
|
|
425
427
|
```python
|
|
426
428
|
cfg = rd.config_management
|
|
427
429
|
all_configs = cfg.list()
|
|
@@ -433,27 +435,28 @@ cfg.restart()
|
|
|
433
435
|
|
|
434
436
|
Pagination
|
|
435
437
|
----------
|
|
436
|
-
|
|
438
|
+
Managers inherit `RundeckObjectManager.iter(...)` (offset/max). Example:
|
|
437
439
|
```python
|
|
438
440
|
for job in rd.jobs.iter(project="demo", page_size=100):
|
|
439
441
|
print(job.id)
|
|
440
442
|
```
|
|
441
443
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
-
|
|
449
|
-
-
|
|
450
|
-
-
|
|
451
|
-
- Tests
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
- `src/rundeck/
|
|
457
|
-
- `src/rundeck/
|
|
458
|
-
-
|
|
444
|
+
Error handling
|
|
445
|
+
--------------
|
|
446
|
+
HTTP errors go through `raise_for_status` and raise dedicated exceptions (e.g., `RundeckAuthenticationError`, `RundeckNotFoundError`, `RundeckValidationError`, `RundeckConflictError`, `RundeckServerError`). Handle them with a `try/except` block around client calls.
|
|
447
|
+
|
|
448
|
+
Development and testing
|
|
449
|
+
-----------------------
|
|
450
|
+
- Development setup (contributors): clone the repo and run `poetry install`.
|
|
451
|
+
- Formatting/Lint: `black src/rundeck tests/`, `ruff check src/rundeck tests/`
|
|
452
|
+
- Typing: `mypy src/rundeck/`
|
|
453
|
+
- Tests: `pytest`
|
|
454
|
+
- Local integration tests: `scripts/run-integration.sh` (starts docker compose at repo root, exports by default `RUNDECK_URL=http://localhost:4440`, `RUNDECK_TOKEN=adminToken`, `RUNDECK_API_VERSION=56`, waits for the healthcheck, runs `poetry run pytest -m integration`). You can override env vars before running. Add `KEEP_STACK=1` to keep the instance running after tests. Dedicated teardown script: `scripts/stop-integration.sh`.
|
|
455
|
+
|
|
456
|
+
Code structure
|
|
457
|
+
--------------
|
|
458
|
+
- `src/rundeck/base.py`: generic objects/managers, `_build_path` helpers, pagination, CRUD.
|
|
459
|
+
- `src/rundeck/client.py`: HTTP client, `http_get/post/put/delete/list` methods.
|
|
460
|
+
- `src/rundeck/v1/objects/`: domain managers and objects (projects, jobs, executions, system, tokens, users, config_management).
|
|
461
|
+
- 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.
|
|
459
462
|
|
|
@@ -1,43 +1,45 @@
|
|
|
1
1
|
python-rundeck
|
|
2
2
|
===============
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
3
7
|
|
|
4
|
-
|
|
8
|
+
Python client for the Rundeck API (v14-v56), inspired by the python-gitlab architecture. Provides typed managers for key resources (projects, jobs, executions, tokens, users, system, configuration) and SCM (import/export).
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
Contents
|
|
7
11
|
--------
|
|
8
12
|
- Installation
|
|
9
|
-
-
|
|
13
|
+
- Quick start
|
|
10
14
|
- Configuration
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
+
- Available resources
|
|
16
|
+
- Examples by resource
|
|
17
|
+
- Error handling
|
|
18
|
+
- Development and testing
|
|
15
19
|
|
|
16
20
|
Installation
|
|
17
21
|
------------
|
|
18
|
-
|
|
22
|
+
Prerequisites: Python 3.11+. Install from PyPI.
|
|
19
23
|
|
|
20
24
|
```bash
|
|
21
|
-
|
|
22
|
-
# ou en editable avec pip
|
|
23
|
-
pip install -e .
|
|
25
|
+
pip install python-rundeck
|
|
24
26
|
```
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
Quick start
|
|
29
|
+
-----------
|
|
28
30
|
```python
|
|
29
31
|
from rundeck.client import Rundeck
|
|
30
32
|
|
|
31
33
|
rd = Rundeck(url="https://rundeck.example.com", token="MY_TOKEN", api_version=56)
|
|
32
|
-
#
|
|
34
|
+
# Password auth (session cookie) if no token
|
|
33
35
|
rd = Rundeck(url="https://rundeck.example.com", username="admin", password="admin", api_version=56)
|
|
34
36
|
|
|
35
|
-
#
|
|
37
|
+
# List projects
|
|
36
38
|
projects = rd.projects.list()
|
|
37
39
|
for p in projects:
|
|
38
40
|
print(p.name)
|
|
39
41
|
|
|
40
|
-
#
|
|
42
|
+
# Get a project and run a job
|
|
41
43
|
project = rd.projects.get("demo")
|
|
42
44
|
jobs = project.jobs.list()
|
|
43
45
|
execu = jobs[0].run()
|
|
@@ -46,64 +48,64 @@ print(execu.id)
|
|
|
46
48
|
|
|
47
49
|
Configuration
|
|
48
50
|
-------------
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- `url
|
|
53
|
-
- `token
|
|
54
|
-
- `username` / `password
|
|
55
|
-
- `api_version
|
|
56
|
-
- `timeout
|
|
57
|
-
- `ssl_verify
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- `RUNDECK_URL
|
|
62
|
-
- `RUNDECK_TOKEN
|
|
63
|
-
- `RUNDECK_USERNAME` / `RUNDECK_PASSWORD
|
|
64
|
-
- `RUNDECK_API_VERSION
|
|
65
|
-
- `RUNDECK_TIMEOUT
|
|
66
|
-
- `RUNDECK_SSL_VERIFY
|
|
67
|
-
- `RUNDECK_USER_AGENT
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
- `projects` (`ProjectManager`)
|
|
72
|
-
- `jobs` (`JobManager`)
|
|
73
|
-
- `executions` (`ExecutionManager`)
|
|
74
|
-
- `tokens` (`TokenManager`)
|
|
75
|
-
- `users` (`UserManager`)
|
|
76
|
-
- `metrics` (`MetricsManager`)
|
|
77
|
-
- `plugins` (`PluginManager`)
|
|
78
|
-
- `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`)
|
|
79
|
-
- `key_storage` (`StorageKeyManager`)
|
|
80
|
-
- `adhoc` (`AdhocManager`, via `project.adhoc`)
|
|
81
|
-
- `system` (`SystemManager`)
|
|
82
|
-
- `config_management` (`ConfigManagementManager`)
|
|
83
|
-
- `scm` (via `project.scm`
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
51
|
+
Configuration follows a cascading model (args > env > files > defaults) via `RundeckConfig`.
|
|
52
|
+
|
|
53
|
+
Main parameters:
|
|
54
|
+
- `url`: Rundeck URL (e.g. `https://rundeck.example.com`)
|
|
55
|
+
- `token`: API token (header `X-Rundeck-Auth-Token`)
|
|
56
|
+
- `username` / `password`: Session authentication (j_security_check) if no token is provided.
|
|
57
|
+
- `api_version`: API version (e.g. `56`)
|
|
58
|
+
- `timeout`: Request timeout (float, seconds)
|
|
59
|
+
- `ssl_verify`: TLS verification (bool or CA path)
|
|
60
|
+
|
|
61
|
+
Config files: if needed, pass `config_files` or `Rundeck.from_config(config_section=...)`.
|
|
62
|
+
Useful env vars (client config):
|
|
63
|
+
- `RUNDECK_URL`: Base URL
|
|
64
|
+
- `RUNDECK_TOKEN`: API token (header `X-Rundeck-Auth-Token`)
|
|
65
|
+
- `RUNDECK_USERNAME` / `RUNDECK_PASSWORD`: Session auth (if no token)
|
|
66
|
+
- `RUNDECK_API_VERSION`: API version (e.g. 56)
|
|
67
|
+
- `RUNDECK_TIMEOUT`: Request timeout (seconds)
|
|
68
|
+
- `RUNDECK_SSL_VERIFY`: TLS verification (bool or CA path)
|
|
69
|
+
- `RUNDECK_USER_AGENT`: HTTP User-Agent
|
|
70
|
+
|
|
71
|
+
Available resources
|
|
72
|
+
-------------------
|
|
73
|
+
- `projects` (`ProjectManager`): CRUD projects, job export/import, project config, archive (export/import).
|
|
74
|
+
- `jobs` (`JobManager`): list, get, deletions, bulk actions, execution.
|
|
75
|
+
- `executions` (`ExecutionManager`): list/filter, running, get/delete, advanced query.
|
|
76
|
+
- `tokens` (`TokenManager`): list, get, create, delete.
|
|
77
|
+
- `users` (`UserManager`): user operations (per current implementation).
|
|
78
|
+
- `metrics` (`MetricsManager`): `/metrics` endpoints (list/data/healthcheck/ping).
|
|
79
|
+
- `plugins` (`PluginManager`): list installed plugins (`/plugin/list`).
|
|
80
|
+
- `webhooks` (`WebhookEventManager` + `ProjectWebhookManager`): project webhook management and sending via token.
|
|
81
|
+
- `key_storage` (`StorageKeyManager`): key storage management `/storage/keys`.
|
|
82
|
+
- `adhoc` (`AdhocManager`, via `project.adhoc`): run AdHoc commands/scripts.
|
|
83
|
+
- `system` (`SystemManager`): system info, executions enable/disable, logstorage, ACL.
|
|
84
|
+
- `config_management` (`ConfigManagementManager`): global configuration `/config`.
|
|
85
|
+
- `scm` (via `project.scm` and `job.scm`): import/export plugins, setup, enable/disable, status, actions (commit/import/export...).
|
|
86
|
+
|
|
87
|
+
Examples by resource (complete)
|
|
88
|
+
-------------------------------
|
|
89
|
+
Projects
|
|
88
90
|
```python
|
|
89
|
-
# CRUD
|
|
91
|
+
# CRUD project
|
|
90
92
|
p = rd.projects.create("demo")
|
|
91
93
|
p = rd.projects.get("demo")
|
|
92
94
|
rd.projects.delete("demo")
|
|
93
95
|
projects = rd.projects.list()
|
|
94
96
|
|
|
95
|
-
# Export / import
|
|
97
|
+
# Export / import jobs from a project (via job manager)
|
|
96
98
|
project.jobs.export(format="json", idlist="id1,id2", groupPath="group/sub")
|
|
97
99
|
project.jobs.import_jobs(content=open("jobs.json").read(), format="json", dupeOption="update", uuidOption="remove")
|
|
98
100
|
|
|
99
|
-
#
|
|
101
|
+
# Project export / import (ZIP archive)
|
|
100
102
|
archive = project.archive
|
|
101
|
-
resp = archive.export(export_all=False, export_webhooks=True) #
|
|
103
|
+
resp = archive.export(export_all=False, export_webhooks=True) # Raw response (zip)
|
|
102
104
|
token_info = archive.export_async(exportAll=True)
|
|
103
105
|
status = archive.export_status(token_info.get("token", ""))
|
|
104
106
|
zip_resp = archive.export_download(token_info.get("token", ""))
|
|
105
107
|
|
|
106
|
-
#
|
|
108
|
+
# Archive import (sync or async)
|
|
107
109
|
archive.import_archive(
|
|
108
110
|
content=open("project-export.zip", "rb").read(),
|
|
109
111
|
jobUuidOption="preserve",
|
|
@@ -113,16 +115,16 @@ archive.import_archive(
|
|
|
113
115
|
archive.import_archive(content=open("project-export.zip", "rb").read(), async_import=True)
|
|
114
116
|
archive.import_status()
|
|
115
117
|
|
|
116
|
-
#
|
|
117
|
-
project.readme.get_readme() #
|
|
118
|
+
# Project README / MOTD
|
|
119
|
+
project.readme.get_readme() # default text
|
|
118
120
|
project.readme.get_readme(accept="application/json")
|
|
119
|
-
project.readme.update_readme("
|
|
121
|
+
project.readme.update_readme("New content", content_type="text/plain")
|
|
120
122
|
project.readme.delete_readme()
|
|
121
123
|
project.readme.get_motd()
|
|
122
|
-
project.readme.update_motd("Message
|
|
124
|
+
project.readme.update_motd("Message of the day", content_type="text/plain")
|
|
123
125
|
project.readme.delete_motd()
|
|
124
126
|
|
|
125
|
-
#
|
|
127
|
+
# Project config (key/value)
|
|
126
128
|
conf = p.config.get()
|
|
127
129
|
p.config.keys.get("project.label")
|
|
128
130
|
p.config.keys.set("project.label", "Demo")
|
|
@@ -130,27 +132,27 @@ p.config.keys.update({"project.description": "Sample"})
|
|
|
130
132
|
p.config.replace({"project.label": "Demo", "project.description": "Sample"})
|
|
131
133
|
p.config.keys.delete("project.label")
|
|
132
134
|
|
|
133
|
-
# SCM import/export (
|
|
134
|
-
scm_import = project.scm.import_ #
|
|
135
|
+
# SCM import/export (on a project)
|
|
136
|
+
scm_import = project.scm.import_ # or getattr(project.scm, "import")
|
|
135
137
|
scm_export = project.scm.export
|
|
136
138
|
|
|
137
|
-
#
|
|
139
|
+
# Plugin discovery
|
|
138
140
|
scm_import.plugins.list()
|
|
139
141
|
scm_export.plugins.list()
|
|
140
142
|
|
|
141
|
-
#
|
|
143
|
+
# Input fields for a plugin and setup (explicit plugin_type)
|
|
142
144
|
fields = scm_import.plugins.input_fields("git-import")
|
|
143
145
|
scm_import.config.setup("git-import", {"url": "ssh://git@example.com/repo.git", "dir": "/tmp/repo"})
|
|
144
146
|
|
|
145
|
-
#
|
|
147
|
+
# Enable/disable a plugin
|
|
146
148
|
scm_import.config.enable("git-import")
|
|
147
149
|
scm_export.config.disable("custom-export")
|
|
148
150
|
|
|
149
|
-
#
|
|
151
|
+
# SCM status/config
|
|
150
152
|
import_status = scm_import.actions.status()
|
|
151
153
|
export_conf = scm_export.config.get()
|
|
152
154
|
|
|
153
|
-
#
|
|
155
|
+
# Project-side SCM actions (e.g., commit/pull/push depending on plugin)
|
|
154
156
|
action_fields = scm_export.actions.input_fields("commit")
|
|
155
157
|
scm_export.actions.perform(
|
|
156
158
|
"commit",
|
|
@@ -163,14 +165,14 @@ scm_export.actions.perform(
|
|
|
163
165
|
|
|
164
166
|
Jobs
|
|
165
167
|
```python
|
|
166
|
-
#
|
|
168
|
+
# List jobs for a project
|
|
167
169
|
jobs = rd.jobs.list(project="demo", groupPath="ops")
|
|
168
170
|
|
|
169
|
-
#
|
|
171
|
+
# From a parent project
|
|
170
172
|
project = rd.projects.get("demo")
|
|
171
173
|
jobs = project.jobs.list()
|
|
172
174
|
|
|
173
|
-
#
|
|
175
|
+
# Direct access to a job
|
|
174
176
|
job = rd.jobs.get("job-id")
|
|
175
177
|
job.delete()
|
|
176
178
|
job.definition(format="yaml")
|
|
@@ -185,7 +187,7 @@ tags = job.tags()
|
|
|
185
187
|
workflow = job.workflow()
|
|
186
188
|
forecast = job.forecast(time="2024-05-01T10:00:00Z", max=5)
|
|
187
189
|
|
|
188
|
-
#
|
|
190
|
+
# Export/import jobs via manager (project parameter or parent)
|
|
189
191
|
rd.jobs.export(project="demo", format="xml", idlist="id1,id2", groupPath="group")
|
|
190
192
|
rd.jobs.import_jobs(
|
|
191
193
|
project="demo",
|
|
@@ -193,30 +195,30 @@ rd.jobs.import_jobs(
|
|
|
193
195
|
fileformat="xml",
|
|
194
196
|
dupeOption="update",
|
|
195
197
|
)
|
|
196
|
-
#
|
|
198
|
+
# Or via a parent project
|
|
197
199
|
project.jobs.export(format="json")
|
|
198
200
|
project.jobs.import_jobs(content=open("jobs.json", "rb").read(), fileformat="json")
|
|
199
201
|
|
|
200
|
-
Note:
|
|
202
|
+
Note: import remains exposed as `import_jobs(...)` (the Python keyword prevents a direct call to `.import`). If you prefer the alias, use `getattr(rd.jobs, "import")(...)`.
|
|
201
203
|
|
|
202
|
-
#
|
|
204
|
+
# Run and get the execution
|
|
203
205
|
execution = job.run(argString="-option value")
|
|
204
206
|
|
|
205
|
-
#
|
|
207
|
+
# Bulk actions
|
|
206
208
|
rd.jobs.bulk.enable_execution(["id1", "id2"])
|
|
207
209
|
rd.jobs.bulk.disable_execution(["id1", "id2"])
|
|
208
210
|
rd.jobs.bulk.delete(["id1", "id2"])
|
|
209
211
|
rd.jobs.bulk.enable_schedule(["id1", "id2"])
|
|
210
212
|
rd.jobs.bulk.disable_schedule(["id1", "id2"])
|
|
211
213
|
|
|
212
|
-
# Upload
|
|
214
|
+
# Upload option files and uploaded files
|
|
213
215
|
job.upload_option_file("csvfile", open("data.csv", "rb").read(), file_name="data.csv")
|
|
214
216
|
job.list_uploaded_files(max=20)
|
|
215
217
|
rd.jobs.get_uploaded_file_info("file-id")
|
|
216
218
|
|
|
217
|
-
# SCM import/export
|
|
219
|
+
# SCM import/export on a job
|
|
218
220
|
job_scm_export = job.scm.export
|
|
219
|
-
job_scm_import = job.scm.import_ #
|
|
221
|
+
job_scm_import = job.scm.import_ # or getattr(job.scm, "import")
|
|
220
222
|
|
|
221
223
|
job_scm_export.status()
|
|
222
224
|
job_scm_export.diff()
|
|
@@ -226,7 +228,7 @@ job_scm_import.status()
|
|
|
226
228
|
job_scm_import.input_fields("pull")
|
|
227
229
|
job_scm_import.perform("pull", input_values={"message": "Update from repo"})
|
|
228
230
|
|
|
229
|
-
#
|
|
231
|
+
# Project resources
|
|
230
232
|
resources = project.resources.list(format="json", groupPath="ops")
|
|
231
233
|
node = project.resources.get("node1")
|
|
232
234
|
sources = project.sources.list()
|
|
@@ -240,18 +242,18 @@ project.acl.update("policy.aclpolicy", content="...yaml...")
|
|
|
240
242
|
project.acl.delete("policy.aclpolicy")
|
|
241
243
|
```
|
|
242
244
|
|
|
243
|
-
|
|
245
|
+
Executions
|
|
244
246
|
```python
|
|
245
|
-
#
|
|
247
|
+
# Simple or paginated list
|
|
246
248
|
execs = rd.executions.list(project="demo", status="running", max=50, offset=0)
|
|
247
|
-
running = rd.executions.running(project="demo") #
|
|
249
|
+
running = rd.executions.running(project="demo") # or "*" for all
|
|
248
250
|
|
|
249
|
-
#
|
|
251
|
+
# Details / deletion
|
|
250
252
|
e = rd.executions.get("123")
|
|
251
253
|
rd.executions.delete("123")
|
|
252
254
|
rd.executions.bulk_delete(["123", "124"])
|
|
253
255
|
|
|
254
|
-
#
|
|
256
|
+
# Advanced query
|
|
255
257
|
advanced = rd.executions.query(
|
|
256
258
|
project="demo",
|
|
257
259
|
statusFilter="failed",
|
|
@@ -261,12 +263,12 @@ advanced = rd.executions.query(
|
|
|
261
263
|
max=100,
|
|
262
264
|
)
|
|
263
265
|
|
|
264
|
-
#
|
|
266
|
+
# Execution methods
|
|
265
267
|
e.abort(asUser="admin")
|
|
266
268
|
output = e.get_output(offset=0, maxlines=100)
|
|
267
269
|
state = e.get_state()
|
|
268
270
|
is_running = e.is_running()
|
|
269
|
-
e.refresh() #
|
|
271
|
+
e.refresh() # reload data
|
|
270
272
|
```
|
|
271
273
|
|
|
272
274
|
Tokens
|
|
@@ -278,20 +280,20 @@ new_token = rd.tokens.create(user="alice", roles=["admin"], duration="90d", name
|
|
|
278
280
|
rd.tokens.delete(new_token.id)
|
|
279
281
|
```
|
|
280
282
|
|
|
281
|
-
|
|
283
|
+
Users
|
|
282
284
|
```python
|
|
283
285
|
users = rd.users.list()
|
|
284
286
|
me = rd.users.get_current()
|
|
285
287
|
u = rd.users.get("bob")
|
|
286
288
|
roles = rd.users.current_roles()
|
|
287
289
|
|
|
288
|
-
#
|
|
290
|
+
# Update via manager or object
|
|
289
291
|
u = rd.users.update("bob", firstName="Bob", lastName="Builder", email="bob@example.com")
|
|
290
|
-
u.roles() # via
|
|
291
|
-
u.update(email="new@example.com") #
|
|
292
|
+
u.roles() # via the object
|
|
293
|
+
u.update(email="new@example.com") # updates and refreshes the object
|
|
292
294
|
```
|
|
293
295
|
|
|
294
|
-
|
|
296
|
+
System
|
|
295
297
|
```python
|
|
296
298
|
system = rd.system
|
|
297
299
|
info = system.info()
|
|
@@ -301,7 +303,7 @@ system.logstorage.info()
|
|
|
301
303
|
system.logstorage.incomplete(max=50, offset=0)
|
|
302
304
|
system.logstorage.incomplete_resume()
|
|
303
305
|
|
|
304
|
-
#
|
|
306
|
+
# Execution mode (sub-manager)
|
|
305
307
|
system.executions.enable()
|
|
306
308
|
system.executions.disable()
|
|
307
309
|
system.executions.status()
|
|
@@ -318,7 +320,7 @@ rd.scheduler.takeover(all_servers=True)
|
|
|
318
320
|
rd.scheduler.takeover(server_uuid="uuid-123", project="demo", job_id="job-1")
|
|
319
321
|
```
|
|
320
322
|
|
|
321
|
-
|
|
323
|
+
Metrics
|
|
322
324
|
```python
|
|
323
325
|
metrics = rd.metrics
|
|
324
326
|
metrics.list()
|
|
@@ -326,16 +328,16 @@ metrics.data()
|
|
|
326
328
|
metrics.healthcheck()
|
|
327
329
|
metrics.ping()
|
|
328
330
|
|
|
329
|
-
#
|
|
331
|
+
# Installed plugins
|
|
330
332
|
plugins = rd.plugins.list()
|
|
331
333
|
for plugin in plugins:
|
|
332
334
|
print(plugin.name, plugin.service)
|
|
333
335
|
|
|
334
|
-
#
|
|
336
|
+
# Plugin details
|
|
335
337
|
first = plugins[0]
|
|
336
338
|
detail = rd.plugins.detail(first.service, first.name)
|
|
337
339
|
|
|
338
|
-
#
|
|
340
|
+
# Project webhooks and send
|
|
339
341
|
project = rd.projects.get("demo")
|
|
340
342
|
wh = project.webhooks
|
|
341
343
|
wh.create(
|
|
@@ -355,9 +357,9 @@ wh.delete(first_hook.id)
|
|
|
355
357
|
|
|
356
358
|
# Key storage (/storage/keys)
|
|
357
359
|
ks = rd.key_storage
|
|
358
|
-
#
|
|
360
|
+
# Create a secret (password)
|
|
359
361
|
ks.create("integration/secret1", content="s3cr3t", content_type="application/x-rundeck-data-password")
|
|
360
|
-
resources = ks.list() #
|
|
362
|
+
resources = ks.list() # root listing
|
|
361
363
|
meta = ks.get_metadata("integration/secret1")
|
|
362
364
|
ks.delete("integration/secret1")
|
|
363
365
|
|
|
@@ -365,17 +367,17 @@ ks.delete("integration/secret1")
|
|
|
365
367
|
project = rd.projects.get("demo")
|
|
366
368
|
exec1 = project.adhoc.run_command("echo 'hello world'")
|
|
367
369
|
exec2 = project.adhoc.run_script("echo 'from script'")
|
|
368
|
-
# exec2.id
|
|
369
|
-
#
|
|
370
|
+
# exec2.id to track execution
|
|
371
|
+
# Stub mode (no immediate refresh) then manual refresh
|
|
370
372
|
exec_stub = project.adhoc.run_command("echo stub", refresh=False)
|
|
371
|
-
exec_stub.refresh() #
|
|
373
|
+
exec_stub.refresh() # load the full object
|
|
372
374
|
# Script via multipart (upload)
|
|
373
375
|
exec3 = project.adhoc.run_script(
|
|
374
376
|
script_file=("hello.sh", "echo multipart", "text/plain"),
|
|
375
377
|
refresh=False,
|
|
376
378
|
)
|
|
377
379
|
|
|
378
|
-
#
|
|
380
|
+
# System features
|
|
379
381
|
features = rd.features.list()
|
|
380
382
|
if features:
|
|
381
383
|
first = features[0]
|
|
@@ -383,7 +385,7 @@ if features:
|
|
|
383
385
|
print(first.name, status.enabled)
|
|
384
386
|
```
|
|
385
387
|
|
|
386
|
-
|
|
388
|
+
Global configuration `/config`
|
|
387
389
|
```python
|
|
388
390
|
cfg = rd.config_management
|
|
389
391
|
all_configs = cfg.list()
|
|
@@ -395,26 +397,27 @@ cfg.restart()
|
|
|
395
397
|
|
|
396
398
|
Pagination
|
|
397
399
|
----------
|
|
398
|
-
|
|
400
|
+
Managers inherit `RundeckObjectManager.iter(...)` (offset/max). Example:
|
|
399
401
|
```python
|
|
400
402
|
for job in rd.jobs.iter(project="demo", page_size=100):
|
|
401
403
|
print(job.id)
|
|
402
404
|
```
|
|
403
405
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
-
|
|
411
|
-
-
|
|
412
|
-
-
|
|
413
|
-
- Tests
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
- `src/rundeck/
|
|
419
|
-
- `src/rundeck/
|
|
420
|
-
-
|
|
406
|
+
Error handling
|
|
407
|
+
--------------
|
|
408
|
+
HTTP errors go through `raise_for_status` and raise dedicated exceptions (e.g., `RundeckAuthenticationError`, `RundeckNotFoundError`, `RundeckValidationError`, `RundeckConflictError`, `RundeckServerError`). Handle them with a `try/except` block around client calls.
|
|
409
|
+
|
|
410
|
+
Development and testing
|
|
411
|
+
-----------------------
|
|
412
|
+
- Development setup (contributors): clone the repo and run `poetry install`.
|
|
413
|
+
- Formatting/Lint: `black src/rundeck tests/`, `ruff check src/rundeck tests/`
|
|
414
|
+
- Typing: `mypy src/rundeck/`
|
|
415
|
+
- Tests: `pytest`
|
|
416
|
+
- Local integration tests: `scripts/run-integration.sh` (starts docker compose at repo root, exports by default `RUNDECK_URL=http://localhost:4440`, `RUNDECK_TOKEN=adminToken`, `RUNDECK_API_VERSION=56`, waits for the healthcheck, runs `poetry run pytest -m integration`). You can override env vars before running. Add `KEEP_STACK=1` to keep the instance running after tests. Dedicated teardown script: `scripts/stop-integration.sh`.
|
|
417
|
+
|
|
418
|
+
Code structure
|
|
419
|
+
--------------
|
|
420
|
+
- `src/rundeck/base.py`: generic objects/managers, `_build_path` helpers, pagination, CRUD.
|
|
421
|
+
- `src/rundeck/client.py`: HTTP client, `http_get/post/put/delete/list` methods.
|
|
422
|
+
- `src/rundeck/v1/objects/`: domain managers and objects (projects, jobs, executions, system, tokens, users, config_management).
|
|
423
|
+
- 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.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|